In the past few months, there have been multiple public disclosures related to SAML Bypasses. This writeup is loosely inspired from them and my journey to uncover yet another SAML Bypass!

If you aren’t familiar with SAML already, I’ll recommend to read the ProjectDiscovery blog first.

Preparation

It was a regular day when I encountered a public disclosure post about SAML Signature bypass in Github Enterprise. It was a Critical Severity vulnerability. I was curious about the exploit, but there was no public writeup at that time, so I let it go.

After few days, I stumbled upon an X(formerly twitter) post from ProjectDiscovery team and they published a blog post on SAML Bypass in Gitlab, with a very detailed explanation on SAML and what went wrong with ruby-saml library.

It was at this point when I decided to test out Cloudflare ZeroTrust’s SAML integration feature.

Opening: Playing around with SAMLResponse

To give a bit of context, Cloudflare allows the organization administrators to integrate any Identity Provider using SAML, acting itself as the ServiceProvider(SP). The End-users can then use their IdP account to access Apps integrated inside Cloudflare ZeroTrust.

I had reported a similar vulnerability in the same SAML feature about an year ago, so the configuration was already done and I was ready to test it. I quickly checked if Cloudflare uses the same vulnerable library by any chance, but unfortunately, they weren’t.

I tried a few known techniques like Missing Signature node, non-verification of Signature node to allow arbitrary Assertion node mutation, using a Self-Signed certificate and they all obviously failed.

It was a complete black-box testing and I was finding it hard to guess if Cloudflare actually uses a public library or have their own custom implementation upon it(which might be insecure). Being a frequent reader of Cloudflare’s blog, I knew that they use Rust a lot in their backend and might be using a far-less popular Rust SAML library. I had a quick look over them, but in vain 🥱

I remembered that they have a “testing” feature to verify whether the configured SAML settings are proper or not. A valid SAMLResponse will display the identity claims like emails, name in the HTML Response. On changing the XML to above exploit payloads, the error messages were visible(similar to how we exploit error based SQLi) and I was confident to pwn them now 😎

Mid Game: XSW For the Win!

The error messages made it very easy to find the actual library(atleast some part of it) & architecture used by the backend. On sending a non-valid base64 data, an error message reflecting the use of Javascript’s atob() function appeared. Cloudflare is very proud of dogfooding their own products and Cloudflare Workers is one of them – most of them use Javascript/TypeScript as the programming language!

On testing a few more payloads, I got a very unique error message and a quick search on Github gives it away! It was a XML verification library, not a SAML verification library. There were few public CVEs related to that library. I quickly checked them if Cloudflare is using the old vulnerable library versions, but again unsuccesful.

I audited the library’s code and found that it is not blocking multiple Assertion Nodes. I found out that we can insert another Assertion node with malicious identity claims in the original SAMLResponse(need to be generated by an inside attacker) with empty Signature node. Adding a second Assertion node, but with empty Signature throws a “multiple assertions not allowed” kinda error. I tried wrapping the second, valid Assertion inside another xml tag and it worked! The original Assertion node in the original SAMLResponse was verified, but the malicious Assertion node wasn’t! I was hoping that prepending this malicious Assertion Node will be chosen for the identity claims & surprisingly, it did 🤯

A malicious SAMLResponse look like was accepted as a valid one:

<SAMLResponse>

<Assertion ID="malicious_assertion">
malicious identity claims...
</Assertion>

<XSW>
<Assertion ID="...">
original account claims
</Assertion>
</XSW>

<!-- Signature from above valid Assertion Node at the end -->
<Signature>
<Reference>
...
</Reference>
</Signature>

</SAMLResponse>

This exploit, however, requires a valid SAMLResponse to impersonate another user because a valid Assertion Node is still required and thus, a valid compromised internal account by attacker too!

End Game: Where’s the CEO takeover?

I reported my initial finding to CF’s security team and the report got triaged after a month.

Due to the above limitation, I thought that the Cloudflare security team might degrade the attack severity to High& they did too. I then attempted to further escalate the attack, without requiring a valid internal compromised account(required to generate a valid SAMLResponse).

Looking at the public library’s code, it was clear that all I require is a valid Signature Node. I was primarily using EntraId as the IdP and discovered that the federation metadata endpoint(which is partially public) contains a Signature Node, which signs the SAML metadata like certificate & other info. I tweaked my script to include this change and created a malicious SAMLResponse, instead of a valid user’s Signature Node(which requires an internal account). I burped the change and Eureka!!

The attack is much better, except the federation metadata endpoint requires somewhat internal access to victim’s organization to retrieve it. I was running out of ideas when I realized that Cloudflare dogfoods it’s own products 😉

Cloudflare uses Authentik, an open-source Identity Provider for their own usage. ChatGPT told me that unlike EntraId, Authentik’s metadata endpoint is public. I tried fetching the same for Cloudflare’s Authentik Server, but got an error message. That was weird 😒

After multiple attempts of trying various things, I realized that Cloudflare might have added some WAF rules to prevent it. Authentik also allows to return the federation metadata xml in json format - this might have not been blocked by Cloudflare WAF, but sadly, it was blocked too.

Later, on a different account, I verified that it was indeed possible to retrieve the federation metadata xml from other Authentik installations(without blocking Cloudflare WAF rules), which includes a valid Signature Node and therefore, complete the attack – zero click account takeover to remotely impersonate any user in Cloudflare’s ZeroTrust!

Upon a recent review of Cloudflare developer documentation, I’ve realized that Cloudflare has a special ZeroTrust AppType, Cloudflare Dash SSO – which allows access to the main Cloudflare Dashboard, not just the small scoped ZeroTrust enrolled Applications. An attacker can use the above zero click ATO to access this special App, and thus, access the victim’s(Cloudflare’s customer using this solution) entire Cloudflare ecosystem as the SuperAdmin 🤯

I was expecting a severity upgrade, since the attack was effective against some specific IdPs, proved against Authentik, similar to how the Github report was also categorized as Critical and using the similar “signed federation metadata XML” trick 😉

Sadly, the Cloudflare Security team denied to upgrade it “based on our likelihood and impact discussion internally”, even after proving a much greater impact and referencing the Github example or ruby-saml 10.0 CVSS vulnerability & kept it to High Severity 🥲

Timelines:

  • Report Submission: 15 Oct 2024
  • Triaged: 13 Nov 2024. Set to High Severity
  • Bounty: 7 grands awarded on 2 Dec 2024 🤑 My highest till date!
  • Fix & Resolution: 6 Dec 2024
  • Disclosure Allowed by Cloudflare: 8 Jan 2025

Conclusion:

To wind-up, the vulnerability allows a remote attacker to impersonate any user in the target organization with the mentioned tricks. For an internal attacker, someone like a Cloudflare Employee could have accessed their own CEO’s account 😜 A gentle disclaimer: If you find any security/privacy issues in Cloudflare’s services/products, report it straightaway to their Public Bug bounty program & get yourself a nice bounty. That’s all for this post. Stay hackin'