SAML and SAML Attacks

Recently a client mentioned that they wanted me to pay particular attention to the SAML authentication on an app I was going to be testing. It’s been a while since I’ve done anything with SAML, so I thought I’d refresh myself on SAML basics and SAML attacks.

SAML Basics

SAML stands for Security Assertion Markup Language. It’s an XML-based standard for exchanging authentication and authorization data between identity providers (IdP) like Okta, Google, Microsoft Azure AD, and more; and service providers (SP) including big services like Box and Google, as well as countless custom applications. SAML is one technology enabling single sign-on (SSO), allowing a user to authenticate to multiple services with credentials managed by a single IdP.

When a user goes to log in to a service using SAML for authentication, here’s what happens.

  1. The user’s browser requests access to the SP
  2. The service provider generates a SAML request for the IdP, asking if the user has permission to access that resource
  3. The SP sends the SAML request to the user’s browser and redirects it to the IdP
  4. The IdP parses the SAML request and authenticates the user
  5. The IdP generates a SAML response
  6. The IdP sends the SAML response to the browser and redirects it back to the SP
  7. The SP validates the SAML response
  8. The SP logs the user in
SAML authentication flow

A SAML request is an xml document specifying what SP the user is requesting access to.

A SAML response is an xml document carrying information about the sending IdP and when the response was generated, as well as an Assertion indicating that the user was successfully authenticated. The Assertion may carry additional information about the user such as their phone number, address, nickname or other details in the form of Attributes. It may also carry information about Authorizations, which describe a user’s access rights to different resources.

Either the assertion, the entire SAML response, or both may be cryptographically signed to ensure their validity.

Con Badges as SAML SSO

One way to think about SAML and SSO is like going to a conference. At a con, before you access any of the events (aka service providers) such as speakers, villages, CTFs and vendor areas, you’ll encounter staff checking for your badge. If you don’t have a badge, you get redirected to the registration desk. The registration desk (aka the identity provider) verifies your identity and authenticates you. If you’re registered for the con, you get a badge, which acts like a SAML response. At many cons, the badge’s color and shape serves as a SAML authorization, detailing what you have access to: attendee areas like villages and speaker tracks, the prep room for speakers, or all the backstage areas for staff. Badge in hand, you can now go and enjoy the con, without having to re-register or re-authenticate at every door.

These aren’t SAML Responses, but they could be

SAML Attacks

SAML implementations can be vulnerable to a variety of attacks. Some implementations may not properly validate signatures. Others may not check that a SAML response was actually intended for the SP receiving it or may accept additional SAML responses tacked on to the real one. And because it’s XML, XML External Entity (XXE) and Extensible Stylesheet Language Transformation (XSLT) attacks are also possible.

Below I’ll demonstrate three attacks: signature removal, signature replacement and condition violation.

The easiest way to perform these attacks is using a tool such as SAML Raider, a Burp Suite extension available in the BApp Store.

SAML-practice

To help practice these attacks. I built a small practice environment. It’s based on the example IdP and SP in Tim Heap’s flask-saml2 repo, which I’ve modified to include some additional vulnerabilities. SAML-practice spins up three docker containers, hosting an IdP and two SPs, on ports 8000, 9000 and 9001, respectively. Running the start-saml-practice.sh script will prompt you for your host’s IP address, configure and build the docker containers accordingly, then run them. For most of the exercises below, I demonstrate attacks using the SP running on port 9000 (Sp0), but either SP will work. Only the third attack requires the use of both SPs at once.

Getting Started

In my case, I configured my containers to run on my host ip of 192.168.109.175.

Visiting ‘http://192.168.109.175:9000/‘ I receive the message that I’m logged out, and that I can log in to continue.

Haven’t logged in to the SP yet

Clicking the link, I’m redirected to a login page at http://192.168.109.175:8000/login/ — the IdP — and prompted to select a user.

Selecting a user at the Identity Provider

After I select a user and hit ‘login’, I very quickly see a redirect page flash up, before I’m redirected back to the SP, which informs me I’m logged in and the IdP sent back the attributes ‘foo’: ‘bar’. There are no authorization fields in this assertion).

Now I open up Burp and repeat the process using its built-in browser. Looking at Burp Proxy’s HTTP History I can see the authentication flow play out. Reading from the bottom up is my initial request to the SP (line 25), the redirects to the IdP (26-29), the POST where I identify myself (line 31), and then the redirect back to the SP (33), followed by the SP confirming I’m logged in.

The SAML authentication flow

The SAML response, which is what interests me, is in line 33, the POST request to the SP http://192.168.109.175:9000/. The actual SAML response is the large base64-encoded parameter at the bottom labelled, clearly enough “SAMLResponse”.

The SAML Response in the request to the SP

Activating the SAML Raider extension, I can examine the response’s XML, seeing that it has a signature block at the top, and a single assertion, giving our selected user access to the system. The question now is, can I leverage this to gain access to someone else’s data?

Signature Removal

The simplest attack is to remove all the certificates and signatures from the SAML response. Some SAML libraries will rigorously validate every signature they’re given. And if they’re given no signature, they’ll look, see that no signatures have failed to validate, and let you in. In this case, I’m going to search for NameID field where the Response identifies me as ‘alex@example.com’ and change it to ‘jordan@example.com’, then remove the signatures. With no signatures, I don’t need to worry about the data tampering causing a signature mismatch. First I change the names.

I don’t want to be Alex, I want to be Jordan

I then hit the “remove signatures” button.

Removing signatures in SAML Raider

Turn interception off, and…

I’m Jordan, now

Signature Replacement

In a secure SAML implementation, the IdP and SP should exchange certificates out-of-band before the application goes into production, so they can verify that the certificates being used to sign messages are the correct ones. Some service providers will insecurely verify a SAML Response’s signatures against the certificate provided in the response. In those cases, tampering with a message, then re-signing it will allow it to pass muster.

To test this, I again go the the login page at http://192.168.109.175:9000/. Again, I turn on interceptor, select ‘alex@example.com’ and then forward requests until SAML Raider activates.

As before, I’m going to replace alex with jordan. But before I remove the signatures, I’m first going to hit the “Send Certificate to SAML Raider” button.

Send a certificate to a SAML Raider Certificates

That sends the certificate to the SAML Raider Certificates tab, which allows you to edit, and more importantly, re-sign certificates so you can use them to generate signatures. So I hit the “Save and Self-Sign”, then go back to the Interceptor tab. At this stage I could also generate my own X.509 certificate with OpenSSL, import it into SAML Raider and use it the same way as I’m about to use the re-signed certificate.

Resigning a certificate in SAML Raider

Now, the certificate drop-down that was empty is filled with the certificate I re-signed. I remove the existing signatures, change the user from Alex to Harry, and then re-sign the SAML Response with my tampered certificate. Re-signing the SAML Response has to be the last step, as any changes made after that point will invalidate the signature.

Remove the signatures first, tamper with the content, then re-sign the cert

I turn off interception, let the authentication flow finish, and I’m logged in as Harry.

You’re a hacker, Harry!

Condition Violations

SAML responses can have one or more conditions limiting their use. Typical ones include time limits on the response’s validity (e.g. NotBefore and NotOnOrAfter), and audience restrictions. If a service provider does not check these conditions, it becomes possible to do things like save responses for reuse later, or capture a response generated for one service provider, and send it to a second. The screenshot below shows the conditions in one of the SAML Responses I’ve been working with in this blog post, including time limits and audience restrictions.

This SAML response is good for a limited time, with one SP (maybe)

The saml-practice repository includes a second service provider, running on http://192.168.109.175:9001/sp1 which ./start-saml-practice.sh starts up alongside the first SP and the IdP. For this exercise I open an incognito window (using a second browser would work, too) to interact with the other SP, to prevent cookies issues causing unexpected side effects. With this second service provider, I can practice Audience Substitution – using a SAML Response generated for one SP with a different SP.

I start by logging in to the SP on port 9001 (Sp1). I don’t even need to use Interceptor; I can just select the user I want and let the authentication flow run. In this case, I log in as Harry. In the HTTP History window, I find the POST request to http://192.168.109.175:9001/sp1/saml/acs, which contains the SAML Response from the IdP to Sp1. I need the base64 encoded version, not the XML for this, so I’m viewing the request in Raw mode. I copy the entire encoded SAMLResponse parameter.

Copy the entire URL- and base64- encoded SAML response

In a second browser, or an incognito window, I go to http://192.168.109.175:9000/, which will start the login flow for Sp0. This time, I’ll use Interceptor, as I did in the other two attacks, starting my login as Alex or Jordan. When the SAML Response shows up in Interceptor, change the mode back to Raw, and replace the SAMLResponse parameter in the request with the one I copied out of the request for Sp1. I turn interception off and when the authentication flow finishes, I’m logged in to Sp0 as Harry.

I’m supposed to be here, really.

There are a whole host of other attacks involving XML signature wrapping, which I wasn’t able to build into this practice environment, but this should be enough to get you started with testing for SAML vulnerabilities. If you’d like to experiment a bit more, there are a couple of hidden bits in the practice environment that take advantage of the attacks described above. Play around a bit and see if you can find them!

Hint: Try using names that are available in one SP, but not the other, or maybe common usernames that would give yo u more access in a real environment.

Resources