XML Signature Validation Bypass in simpleSAMLphp and xmlseclibs

In October, we conducted a grey-box penetration test of a SAML-based Single Sign-On solution operated by SURFnet. The tested application used the open-source PHP library SimpleSAMLphp, whose source code we analyzed as a part of the penetration test. We were able to identify a novel variant of an XML Signature Wrapping (XSW) attack in SimpleSAMLphp, which allowed us to bypass the integrity and authenticity protection of the SAML assertion and change its contents arbitrarily.

In the following, we explain the details of the successful XSW attack, but first, we give a brief overview to SimpleSAMLphp, SAML, and XSW in general.

SimpleSAMLphp: SimpleSAMLphp is an open-source implementation of the SAML Single Sign-On protocol. It is maintained by UNINETT, the operator of the Norwegian national research and education network, and written in native PHP. SimpleSAMLphp can be utilized to run a SAML Service Provider, as well as a SAML Identity Provider. It is mostly used to establish Single Sign-On on universities [9], but it can also be found as a plugin in WordPress and Drupal applications.

Security Assertion Markup Language (SAML): SAML is one of the most popular Single Sign-On protocols used for federated identity deployments by numerous applications worldwide. The protocol allows an Identity Provider (IdP) to pass identity information about an End-User to a Service Provider (SP) using XML messages. The IdP issues a so-called “SAML Assertion” which contains the identity information of the End-User in question after the End-User authenticated at the IdP. A signature-based on the XML Signature standard secures the integrity and authenticity of the SAML Assertion. It enables the SP to verify that it has been issued by the IdP and not manipulated by an attacker.

According to the SAML specification [1] the whole SAML assertion must be digitally signed – either the <Assertion> element or its ancestor (typically a <Response> element) must be protected with an enveloped XML Signature [2]. To sign a SAML assertion, the signer first canonicalizes the <Assertion> element and computes a digest value over the resulting canonicalization output. It puts the digest value into a <Reference> element located in the <SignedInfo> element. The <Reference> element references the signed assertion using a URI attribute, which contains the ID of the assertion ( 123 in Figure 1). Finally, the signer computes a signature over the canonicalized <SignedInfo> element and stores the resulting value in the <SignatureValue> element.

Figure 1 depicts an example of a SAML document structure. It contains a root <Response> element with a SAML assertion inside. The assertion contains identity information about the subject, for example, about the End-User ( attacker ), and is signed by an XML Signature.

Since not the whole XML document is signed, it is critical for the recipient to process only signed document parts. For this purpose, the XML Signature verification and dereferencing of signed XML parts has to be performed correctly. Otherwise, if an attacker is able to force the receiver to process unsigned data, he can change arbitrary assertion claims and use it to update expired assertions or even to impersonate an arbitrary user [3, 4].

XML Signature Wrapping (XSW): The validation of XML Signatures is a complex process. It involves URI-based dereferencing, XML canonicalization, two-step hash value computation, and evaluation of cryptographic functions. On the other hand, a search for a specific XML element (e.g., the SAML assertion in the SAML response) can easily be executed by the application of a simple XML parser. This leads to the fact that XML documents containing XML Signatures are typically processed in two independent steps: (1) signature validation and (2) SAML assertion evaluation. If the application processes signed XML documents using different modules having different views on the document, a class of attacks named XML Signature Wrapping (XSW) can appear [3, 4]. In these attacks, the attacker modifies the message structure by injecting forged elements, which do not invalidate the signature. The goal of this alteration is to change the message in such a way that the application logic and the signature verification module use different message parts. The receiver should successfully verify the signature, but the assertion evaluation logic should process a bogus element. The attacker thus circumvents the integrity protection and the origin authentication of the signature and can inject arbitrary content. In the context of SAML this allows him to change the identity stated in the assertion and impersonate arbitrary End-Users. The only prerequisite is to get in possession of a valid SAML assertion. This can be done, for example, by obtaining a valid account for an arbitrary user on the affected system.

Figure 2 depicts a simple SAML XSW attack technique from [4]. The attacker places the original assertion into a newly created <Wrapper> element. He then creates a new assertion with a new ID and a subject Alice . Since the original assertion has not been modified, it can be dereferenced and its digest value successfully verified. However, the assertion evaluation logic takes the new assertion located in the root element and authenticates the attacker as Alice.

There are multiple variants of XSW attacks utilizing different techniques. In order to (semi-)automatically test SAML Endpoints for XSW vulnerabilities, the EsPReSSo extension for BurpSuite can be used as described here.

XSW Vulnerability in SimpleSAMLphp 1.17.6: SimpleSAMLphp uses the underlying xmlseclibs for XML Signature validation. To prevent XSW attacks, SimpleSAMLphp only works with validly signed XML elements, and uses the Validator class [5] which works as follows: It first locates the <Signature> element and canonicalizes the <SignedInfo> string [6]. For this purpose, it uses the ./secdsig:SignedInfo XPath to locate the first <SignedInfo> element and performs an XML canonicalization using this element. (While the XPath expression returns a list of <SignedInfo> elements, XMLSecurityDSig later processes only the first one [7].) It then uses the reference validation logic to locate and validate <Reference> elements in <SignedInfo> with the following XPath query: ./secdsig:SignedInfo/secdsig:Reference [8]. Note that this XPath selects all <Reference> elements, which are located inside any <SignedInfo> element. Every successfully validated <Reference> element is stored in a list $validatedNodes . Once the signature has been successfully validated, the SAML message processor can retrieve the list of validated elements and ensure that it only works with a validated assertion.

The signature validation logic described above prevents typical XSW attacks. For example, if the attacker would construct an XSW message as described in Figure 2, the validation logic would reject this message because the SAML assertion has not been verified and is not contained in the list of $validatedNodes .

However, the SimpleSAMLphp validation logic can be circumvented by a tiny but effective modification of the XSW attack; by inserting an additional <SignedInfo> element, which contains a valid reference, but is not covered by the signature computation. An example of a manipulated SAML response is depicted in Figure 3.

Let us first discuss how this message can be constructed. The attacker modifies the original SAML assertion to contain a different ID ( attack in Figure 3) and identity information about a different End-User ( alice ) (1). Since these modifications invalidate the reference digest, the attacker computes a new digest value over the assertion and puts the value into a <SignedInfo> element together with a new URI ( attack ) reference (2). He places the <SignedInfo> element in the original <Signature> element. Finally, he puts the original assertion into a <Wrapper> element located directly in the SAML <Response> (3).

Once the SimpleSAMLphp validation logic processes this document, it can successfully validate the digest values computed over both SAML assertions, while it computes the signature only over the first <SignedInfo> element. Therefore, it puts both assertions into the list in $validatedNodes . After a successful signature validation, it processes the first assertion located in the SAML <Response> element. This means the assertion manipulated by the attacker is used to determine the End-User’s identity. The attacker successfully impersonates another End-User – the victim Alice.

Impact and countermeasures: There are different reasons for this attack, located on different levels of the SAML assertion validation:

The XMLSecurityDSig class from the xmlseclibs library does not validate the structure of the XML Signature; the <SignedInfo> element must be the first and only child of the <Signature> element. The implementation must check the XML Signature structure correctness. The XMLSecurityDSig processes unsigned <SignedInfo> elements and puts the referenced elements into a list of validated nodes: $validatedNodes . The implementation must not process unsigned <SignedInfo> elements. SimpleSAMLphp does not validate the structure of the SAML <Response> elements according to their XML schema. The implementation should verify the correctness of SAML messages according to their XML schemas. However, we would like to note that using XML schema would not completely prevent these attacks.

Since the attack is based on an insufficient validation in the xmlseclibs library, we believe there are more systems affected by this vulnerability, not only SimpleSAMLphp. If you use xmlseclibs, you should update it to the latest version or apply different countermeasures.

Responsible Disclosure: We notified the developers of SimpleSAMLphp and xmlseclibs about the identified vulnerability and provided them with all details on 24.10.2019. We supported them during the process of applying additional security measures to fix the vulnerability and verified that it was successfully fixed with xmlseclibs version 3.0.4 and SimpleSAMLphp version 1.17.7 released on 06.11.2019. The security advisory for SimpleSAMLphp can be found here.

We want to thank Thijs Kinkhorst from SURFnet and the developers of SimpleSAMLphp and xmlseclibs for the pleasant communication and the fast fix of the identified vulnerability.

If you want to learn more about the security of SAML and other Single Sign-On solutions, you can also book one of our in-depth Single Sign-On training courses.

Do you think the attack described above is interesting and would you like to participate in penetration tests yourself? Have a look at our career page and see if there is an interesting job offer for you.

[1] Scott Cantor et al. Assertions and Protocol for the OASIS Security Assertion Markup Language (SAML) V2.0. OASIS Standard. Organization for the Advancement of Structured Information Standards, 2005. URL: http://docs.oasis-open.org/security/saml/v2.0/saml-core-2.0-os.pdf.

[2] Donald Eastlake et al. XML Signature Syntax and Processing (Second Edition). In: W3C Recommendation (2008).

[3] Christian Mainka et al. Your Software at My Service: Security Analysis of SaaS Single Sign-On Solutions in the Cloud. In Proceedings of the 6th Edition of the ACM Workshop on Cloud Computing Security. CCSW ’14. Scottsdale, Arizona, USA, 2014.

[4] Juraj Somorovsky et al. On Breaking SAML: Be Whoever You Want to Be. In Proceedings of the 21st USENIX Security Symposium. Bellevue, WA, 2012.

[5] https://github.com/simplesamlphp/simplesamlphp/blob/587300fa2a02882ce4da7a87de630f5154e66399/lib/SimpleSAML/XML/Validator.php

[6] https://github.com/simplesamlphp/simplesamlphp/blob/587300fa2a02882ce4da7a87de630f5154e66399/lib/SimpleSAML/XML/Validator.php\#L77

[7] https://github.com/robrichards/xmlseclibs/blob/0f5466070e2a0f2d5d7f41968afdbbf982eea69c/src/XMLSecurityDSig.php\#L297

[8] https://github.com/robrichards/xmlseclibs/blob/0f5466070e2a0f2d5d7f41968afdbbf982eea69c/src/XMLSecurityDSig.php\#L572

[9] https://simplesamlphp.org/users