Signature validation bypass
XMLSecLibs is a library written by Rob Richards that implements the xml-enc and xml-dsig W3C recommendations. It allows its users to handle encrypted and digitally signed XML documents. SimpleSAMLphp delegates encryption and signature handling to this library.
Producing a digital signature over an XML document or part of it is a complex set of steps that must be performed one after the other:
- First, the signed element, in this case a SAML assertion, needs to be canonicalized (that is, computing a unique version of the XML independent of formatting).
- The canonicalized version of the signed element is then passed to a hash or digest algorithm, which computes a fixed-length, non-reversible, deterministic representation of the original string.
- This digest is then included in a
Referenceelement, which is in turn placed inside a
Referenceelement is actually referring to the XML element that is being signed, by means of its
URIattribute. The value of this
URIattribute is essentially an URI matching the identifier (
ID) of the element.
- The entire
SignedInfoelement is also canonicalized.
- The canonicalized
SignedInfois then fed to a cryptographic algorithm using the private key belonging to the entity signing the piece of XML. The output of this algorithm is stored as the content of the
SignatureValueelement, which is included as the next child of the top
- Finally, depending on the type of XML signature, this
Signatureelement is either provided as a detached document or enveloped inside the actual signed XML document.
For signature validation, the process is similar, although the
SignatureValue is validated with the public key of
the signing entity, and the result is then compared to the digest computed over the canonicalized element whose
signature is being validated. All the steps in the validation procedure must be taken carefully to ensure that we are
inspecting the right elements of the XML document.
There are multiple ways to locate elements inside an XML document, and a very popular one, used by XMLSecLibs, is the query language known as XPath (XML Path Language). XPath allows to retrieve elements from an XML document by writing expressions. These expressions can be either relative or absolute to the root of the document, and they can match one or multiple elements at the same time. If an expression is not written carefully, it might select multiple elements at the same time, even though that wasn’t expected.
The issue at hand is rooted at the way XPath expressions are used by XMLSecLibs to select
Reference elements inside of
SignedInfo. The XPath expression ` ./secdsig:SignedInfo/secdsig:Reference
will select multiple Reference
inside a SignedInfo` element from the current element, as in the following example:
<Signature> <SignedInfo> <Reference URI="#1">...</Reference> <Reference URI="#2">...</Reference> </SignedInfo> </Signature>
However, it will also select multiple
Reference elements from different
SignedInfo elements when all of them are
children of the current element:
<Signature> <SignedInfo> <Reference URI="#1">...</Reference> </SignedInfo> <SignedInfo> <Reference URI="#2">...</Reference> </SignedInfo> </Signature>
The resulting set of
Reference elements is the same with such XPath expression and the given documents. XMLSecLibs
iterates over the list of references and processes all of them, adding them to a list of validated nodes. When the
signature is computed, only the first
SignedInfo element (canonicalized) will be taken into account, though. If the
original signed element is kept in a different part of the XML document, the original
SignedInfo will still validate.
However, a modified assertion taking the place of the original can now also be validated if an unsigned reference to it
is included in a new
SignedInfo element that will be processed by XMLSecLibs, due to the previous XPath expression.
All XMLSecLibs versions 1.x, 2.x and 3.x are affected, up to (including) 1.4.2, 2.1.0 and 3.0.3, respectively.
All SimpleSAMLphp versions are affected, up to (including) 1.17.6.
An attacker could leverage this issue to manually craft an assertion and have the message validated as correctly signed without access to the signing key. This message will then be consumed by a SimpleSAMLphp service provider and the malicious assertion will be processed as if it was legal.
The attacker needs, however, to be able to generate a valid SAML response issued by the targeted identity provider. This means the attacker must be in possession of an account in that IdP. While this can be seen as a mitigation, it should be noted that there is no limitation for an attacker in terms of what to include in the malicious assertion. Therefore, it is possible to impersonate any identity at the targeted IdP once the attacker has any kind of account there.
Upgrade to the latest versions of the library, 3.0.4 or 2.1.1.
For SimpleSAMLphp users, run
composer update or upgrade to SimpleSAMLphp 1.17.7. Refer to the documentation for
instructions on how to run composer.
For those using the
simplesamlphp/saml2 library directly,
run composer update to upgrade to the latest version of
This security issue was discovered by Juraj Somorovsky and Karsten Meyer zu Selhausen (Hackmanit) during a security audit commissioned by SURFnet, and reported on October 24, 2019. Additional details can be found in a blog post written by Hackmanit.