201911-01

Signature validation bypass

Background

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.

Description

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:

  1. 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).
  2. 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.
  3. This digest is then included in a Reference element, which is in turn placed inside a SignedInfo element. This Reference element is actually referring to the XML element that is being signed, by means of its URI attribute. The value of this URI attribute is essentially an URI matching the identifier (ID) of the element.
  4. The entire SignedInfo element is also canonicalized.
  5. The canonicalized SignedInfo is 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 SignatureValue element, which is included as the next child of the top Signature XML element.
  6. Finally, depending on the type of XML signature, this Signature element 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 elements 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.

Affected versions

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.

Impact

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.

Resolution

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 the library.

Credit

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.