201802-01

Improper signature validation

Background

XMLSecLibs is a library written 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

SimpleSAMLphp delegates creating and handling all SAML objects (messages, specific elements, etc) to a separate library called the SAML2 library (simplesamlphp/saml2). This library performs all kinds of validations, including the verification of XML signatures. Signature validation is in turn delegated to XMLSecLibs.

When verifying a signature, the API in XMLSecLibs mandates the creation of an XMLSecurityKey object, which is built depending on the algorithm the key is going to be used for. The key material is later loaded into the object, and it is then ready to use to verify a given signature against that key. XMLSecurityKey objects are always used regardless of the algorithm associated with the key, and the library does not perform any checks on the type of key being loaded into the object. This leads to a key confusion issue, where an XMLSecurityKey object can be instantiated with a specific algorithm, and key material that doesn't correspond that particular algorithm. Additionally, since the API provides common methods regardless of the algorithm, those methods may return different values depending on the underlying backend used, which in turns depends on the algorithm.

The combination of these two factors came into play in the SAML2 library, effectively allowing a SAML message to be signed using the HMAC_SHA1 algorithm and the public key of a SAML2 entity as the secret material used for that algorithm. Obviously, a public key is intended to be public, and as such must always be considered in possession of an attacker.

In order to use the HTTP-Redirect SAML2 binding, the SAML2 library provides the SAML2\HTTPRedirect class, which is used by SimpleSAMLphp when a message is received using this binding in the Assertion Consumer Service endpoint (note that this endpoint is always available, and if a binding is supported, then it can always be used: it is not possible to switch off the use of a particular binding on an endpoint). The SAML2\HTTPRedirect class provides a static method called validateSignature() that extracts the relevant information from the query, and performs the verification of the signature by calling the verifySignature() method provided by XMLSecurityKey.

One of the arguments used to validate the signature is the algorithm, which is of course provided by the user. Since multiple algorithms are supported for digital signatures in SAML2, the SAML2 library allows creating a key with the right algorithm associated by means of the SAML2\Utils\castKey() method. This method simply checks the algorithm used in a given key (which defaults to RSA_SHA1), and if they don't match, it instantiates a new XMLSecurityKey object with the algorithm provided. We should stress again that the algorithm is provided by the user. Once a new key is instantiated, the castKey() method loads the key material present in the given key into this new instance, assuming the material is a public key.

XMLSecLibs version 1.3.3 added support for the HMAC_SHA1 algorithm in 2015. Therefore, since that version it was possible then to call castKey() with that algorithm, and get a XMLSecurityKey object for that algorithm in return, loaded with the public key of the SAML2 entity whose signature we want to verify. Of course, the HMAC_SHA1 algorithm is a symmetric algorithm, meaning in order to verify the signature, the key material used must be secret. That given, it is possible for an attacker to use the HMAC_SHA1 algorithm with that public key to generate a signature for a manually crafted SAML assertion, and a SimpleSAMLphp instance receiving the message with the HTTP-Redirect binding will consider it as a legitimate signature.

This issue was possible, to summarise, due to several facts concurring at the same time:

Had any of these requisites not been met, the issue would have been averted. A fix for the latter is in fact what avoided this issue from being present in other bindings like HTTP-POST, as verifySignature() returns the integer 1 when a public key algorithm is used and the signature is validated, as opposed to the boolean true when the signature is deemed valid by using the HMAC_SHA1 algorithm. This particular issue opens up for other kinds of side attacks previously fixed in other places, like 201612-01, 201612-02 or 201612-03. It has also been fixed in versions 3.1.4, 2.3.8 and 1.10.6 of the SAML2 library and in version 1.15.4 of SimpleSAMLphp. A security advisory has been published for it with the identifier SSPSA 201803-01.

Regarding the other two requisites, the first one implies a complete rewrite of the XMLSecLibs library, in order to offer separate programming interfaces depending on the use that can be made of a key and the type of key itself. We have been in contact with the author and maintainer of the library, but unfortunately the fix for this problem was deemed too complex to have in a reasonable time span, and it would also entail major changes for all code making use of this library.

The second requisite was fixed by introducing a whitelisting mechanism in the SAML2\Utils::castKey() method, effectively ensuring that only the supported RSA_SHA* algorithms can be used. This fix was introduced in versions 3.1.3, 2.3.7 and 1.10.5 of the SAML2 library, and in version 1.15.3 of SimpleSAMLphp, and it effectively tackles the security issue regardless of the API provided by XMLSecLibs and the value returned by verifySignature().

Affected versions

All simplesamlphp/saml2 versions 0.x, 1.x, 2.x and 3.x are affected, up to (including) 1.10.4, 2.3.6 and 3.1.2.

All SimpleSAMLphp 1.14.x and 1.15.x versions are affected, up to (including) 1.15.2.

Other software using the XMLSecLibs library might be affected by the same issue. Several vendors have been contacted, and so far the following implementations have been verified to not be affected:

Impact

For a SimpleSAMLphp installed as an Identity Provider, an attacker may impersonate a Service Provider asking for authentication or to log a user out. Neither of both cases have important consequences.

When SimpleSAMLphp is installed as a Service Provider, attackers may manually craft a SAML2 assertion to their complete will, including altering, adding or removing attributes, changing the subject of the assertion, specifying higher levels of assurance in the authentication context, etc. The only requirement for that is the public key of the Identity Provider being impersonated. The Service Provider will validate the signature as legitimate, and assume the assertion comes from the Identity Provider and corresponds to a legitimate user that has been properly authenticated.

Resolution

Upgrade to the latest versions of the SAML2 library.

For SimpleSAMLphp users, run composer update or upgrade to SimpleSAMLphp 1.15.3.Refer to the documentation for instructions on how to run composer.

Credit

This security issue was discovered during a security audit performed by Cure53 and reported on December 18, 2017.