What's new
  • The default language of any content posted is English.
    Do not create multi-accounts, you will be blocked! For more information about rules, limits, and more, visit the Help page.
    Found a dead link? Use the report button!

CVE-2025-29774, CVE-2025-29775: XML Signature Wrapping Vulnerability in xml-crypto

Messages
21
Files
3
Reactions
10

Target​

  • xml-crypto library (version <= 6.0.0)

Explain​

This vulnerability could allow a hole in XML digital signature verification logic to bypass SAML responses or XML signature-based authentication mechanisms. The vulnerability stems from a structural problem with the library's signature verification method. It was disclosed in two separate cases: CVE-2025-29774 and CVE-2025-29775.xml-crypto

CVE-2025-29774 is a vulnerability that exploits "SignedInfo node duplication". A normal signed XML document should have only one node within a node. However, the vulnerable version of Fraudulent Malware performs signature validation on a healthy non-fake one inserted by an attacker if there are multiple nodes. This allows an attacker to leave one as a valid SignedInfo and the other to bypass the signature, including malicious manipulation such as elevation of privilege or user tampering. In a real-world attack example, two s are inserted into a block, and one contains a forged digest value to attempt to bypass authentication.<Signature><SignedInfo>xml-crypto<SignedInfo><SignedInfo><Signature><SignedInfo>

CVE-2025-29775 is based on the DigestValue manipulation. The vulnerability behaves by inserting an HTML comment() into the DigestValue. A normal DigestValue should be pure Base64 encoded data, but xml-crypto's validation logic treats this value as a string, not as a node. Because of this, an attacker could insert comments and forged values, such as:<!-- -->

Code:
<DigestValue>   
     <!--TBlYWE0ZWM4ODI1NjliYzE3NmViN2E1OTlkOGDhhNmI=-->  
      c7RuVDYo83z2su5uk0Nla8DXcXvKYKgf7tZklJxL/LZ=</DigestValue>

The validation logic ignores the annotation and verifies only the actual DigestValue, so the signature is judged to be valid, but the interpreting application combines the annotation and the value and treats it as invalid data. This allows attackers to manipulate a user's identity or privilege information while still bypassing signature verification.

An attacker could exploit this vulnerability to perform authentication bypass and elevation of privilege attacks. This has far-reaching implications for all services that have implemented SAML SSO based on SAML SSO, especially if an XML message containing sensitive identity or privilege information is tampered with and still passes signature validation.xml-crypto

This vulnerability has been patched in version 6.0.1 of the library. All versions 6.0.0 and below are affected.xml-crypto

  1. SignedInfo node overlap (CVE-2025-29774 detection)
    Within the SAML response or XML document, you need to check to see if there is at least two nodes inside. There must be only one legitimate document.<Signature><SignedInfo>

Code:
<Signature>
    <SomeNode>
      <SignedInfo>
        <Reference URI="somefakereference">
          <DigestValue>forgeddigestvalue</DigestValue>
        </Reference>
      </SignedInfo>
    </SomeNode>
    <SignedInfo>
        <Reference URI="realsignedreference">
          <DigestValue>realdigestvalue</DigestValue>
        </Reference>
    </SignedInfo>
</Signature>

The JavaScript code for the detection is as follows:

Code:
const signedInfoNodes = xpath.select(".//*[local-name(.)='SignedInfo']", signatureNode);
if (signedInfoNodes.length > 1) {
  // Compromise detected
}

2. Presence of annotations in DigestValue (CVE-2025-29775 detection)

<DigestValue> Forms of annotations inserted into blocks should never be present in a normal XML signature. If found, the message should be considered a fabricated message.<!-- -->
Code:
<DigestValue>
    <!-- malicious comment -->
    validDigestValue==
</DigestValue>

The JavaScript code for detection is as follows:

JavaScript:
const digestValues = xpath.select("//*[local-name()='DigestValue'][count(node()) > 1]", decryptedDocument);
if (digestValues.length > 0) {
  // Compromise detected
}
 
Reacted by:
Top