Hacking SAML with Claude Code
CTO
I’ve worked with the SAML protocol ever since I made the unfortunate decision to add support to Dex almost a decade ago. SAML is often described as actively dangerous due to the brain melting technology that is XML Digital Signatures. Every SAML library I’ve ever interacted with has eventually been found to have a full authentication bypass, and my own implementation in Dex resulted in multiple vulnerabilities.
A couple months ago, I used this experience to give a talk on the history of SAML vulnerabilities at the Sec Nerds SF conference. This was an opportunity for me to rant a little, but also brush up on bypass techniques and the latest research in the area.
Around this time, I was reading Niels Provos’s “Finding Zero-Days with Any Model” and started to wonder what would happen if I took my research on SAML vulnerabilities and gave it to a harness. I applied to Anthropic’s Cyber Verification Program to remove the guardrails on my account, and after being accepted, I had access to an Opus model that could write exploits fed by my research and prompting.
I like to claim that SAML is a bad protocol, but could I prove it?
During my spare time over the course of a month or so, I attempted to hack every SAML implementation I could find. While I wasn’t able to structurally break the protocol (much to my chagrin), there were plenty of bugs, many of which are still live today.
Find me vulnerabilities, Claude
One of the core differences between asking Claude to code and asking it to hack is scale. Even in a large codebase, the amount of context needed to track down an individual bug or implement a feature can be relatively small. On the other hand, hacking is about being exhaustive, and going until you find a bug or run out of threads to chase.
To account for this, essentially all “hacking harnesses” (of which there are many) coalesce around a few core primitives:
- Break up work between multiple agents
- Provide storage for intermediate results that agents can use for direction
- Prioritize, scope, and dedupe the work to be done
I built the same. (Check out the source code at: github.com/oblique-security/saml-research)
Building the harness was easy. What I was still surprised by (even despite all the coverage of Mythos, OpenAI/Hugging Face, and model escapes) was that I didn’t really need to teach Claude Opus how to hack. Giving Claude a corpus of vulnerabilities often devolved into it trying to find replicas of those exact issues in other libraries. I found better results by providing a threat model and then letting Claude do its own exploration. This is similar to my experience with agentic coding: as the models get better, you provide fewer specific instructions, and more general guidance.
My pipeline settled on two core phases. A “gadget” phase attempting to find weird behavior in the underlying libraries, and a “findings” phase that combines gadgets then confirms them by writing an end-to-end exploit. These are written to JSONL files, which different steps perform operations on. For example, a step that rejects proposed findings that are out-of-scope before spending tokens to prove them.
Here’s an example of a gadget that Claude found in Node’s xml-crypto’s handling of processing instructions:
{
"id": "g-0005",
"title": "C14N DIFFERENTIAL -- processing instructions are FLATTENED to their data text: `<?php echo 1; ?>` canonicalizes to the bare characters `echo 1;`",
"impacts": [
"s-0108",
"s-0126",
"s-0210"
],
"status": "confirmed"
}Which the finding phase later turned into an email truncation bypass:
<samlp:Response>
<saml:Assertion>
<ds:Signature><!--valid--></ds:Signature>
<saml:Subject>
- <saml:NameID>not-an-admin@example.com</saml:NameID>
+ <saml:NameID><?p not-an-?>admin@example.com</saml:NameID>
</saml:Subject>
</saml:Assertion>
</samlp:Response>Investigating every SAML implementation
It was now time to take my Claude Max 20x plan and see what my pipeline could find.
Since 2020, there’s been a bypass in a major SAML library about once a quarter. GitHub Enterprise alone had four across 2024-2025 (CVE-2024-4985, CVE-2024-6800, CVE-2024-9487, CVE-2025-23369). Essentially all of these stem from components interpreting XML in subtly different ways, and mistakenly processing unsigned data as if it was verified. Today many libraries have adopted hardened APIs that attempt to defend against these kinds of attacks.
While I wasn’t able to set the Internet ablaze, I was able to find full authentication bypasses in four different projects:
- Authentik: Injecting a comment in NameID can be used to truncate to another user’s account and authenticate as them (CVE-2026-57580)
- PHP litesaml/lightsaml: Signature wrapping on Response message (CVE-2026-63182)
- OneUptime: Signature wrapping on Response message (OneUptime/oneuptime#2949)
- Java’s saml-client: Signature wrapping on Response message (justinbleach/saml-client#149)
Again, if you’re ever going to hand roll an SAML implementation, don’t (maybe even just use OpenID Connect instead?).
The Authentik vulnerability was the most interesting because eight different independent researchers reported it at the same time. Clearly, I’m not the only one hacking with AI.
After looking at many top level “login as anyone” attacks, I started poking around other parts of the protocol. In SAML, a “SAML Response” containing a user’s email is signed, but technically any message can have a signature, AuthnRequest, AttributeQuery, and LogoutRequest being the most commonly implemented. These other messages rarely get the kind of security attention the SAML Response does, and I found signature bypasses in twelve projects (and counting) for these components, resulting in information disclosure and arbitrary logouts issues. An example that’s been publicly reported in the TypeScript project samlify.
Finally, denial-of-service vectors. SAML fundamentally requires processing arbitrary XML documents POST’d over the internet, and almost every major SAML library was susceptible to out-of-memory conditions from unauthenticated requests. I was able to get patches accepted for the Go ecosystem, but all Python and Node libraries have active DoS issues that are currently unfixed.
Go’s xmldsig had a quadratic memory allocation during signature validation due to a missing check. JavaScript’s xmldom causes similar allocations (though the report is currently still private) impacting all Node libraries. Python packages don’t filter the set of transforms they pass to libxmlsec1, so an XSLT template can cause the libraries to generate an arbitrarily large document:
<ds:Transform Algorithm="http://www.w3.org/TR/1999/REC-xslt-19991116">
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/"><out>
<xsl:call-template name="d"><xsl:with-param name="n" select="27"/></xsl:call-template>
</out></xsl:template>
<xsl:template name="d"><xsl:param name="n"/><xsl:choose>
<xsl:when test="$n > 0">
<xsl:call-template name="d"><xsl:with-param name="n" select="$n - 1"/></xsl:call-template>
<xsl:call-template name="d"><xsl:with-param name="n" select="$n - 1"/></xsl:call-template>
</xsl:when><xsl:otherwise><x/></xsl:otherwise>
</xsl:choose></xsl:template>
</xsl:stylesheet>
</ds:Transform>The state of vulnerability research in 2026 is bleak. Many of my reports landed on with maintainers who either didn’t respond, or if they did, were clearly overwhelmed. If being an open source maintainer wasn’t already a thankless job, they’re now getting flooded with both genuine and slop vulnerability reports, thanks to AI. One company I reported an issue to insisted I submit a screen recording to filter out generated reports (which I refused to do, but due to the impact of the vuln, they still responded, fixed it, and I got a CVE).
One of the most of-the-times interactions I had was with the OneUptime project, who… let’s say… have embraced AI. After no response to a private report for a month, I opened a public issue, only for a very large PR to merge later that day claiming to have fixed it with no human acknowledgement. I then found a new bypass, and a new large fixed appeared. After the third bypass, the large PRs seemed to arrive at something secure:
- Multiple SAML signature bypasses (#2949)
- SAML signature bypass via signed error response (#2981)
- XML processing instruction can modify SAML NameID (#2988)
It turns out LLMs are also bad at writing secure SAML code.
Ultimately, I picked a stopping point not because I had run out of bugs, but because the appetite for these reports wasn’t there.
So what did we learn?
SAML has been around for long enough that we’ve grown past the point where a stiff breeze will shake out numerous “login as anyone” issues, though the exception continues to be newly written implementations. Projects without the benefit of many years of issues will reimplement the same bugs and have the same kinds of bypasses, so I would again stress: do not roll your own SAML implementation.
I hope this kind of meta analysis becomes more popular. Security Engineers will often debate things like whether or not JWT are good or bad. Having the ability to scan all of GitHub and come up with concrete vulnerabilities is motivating in a way that CVE data and anecdotes aren’t. If you think a protocol has issues, you now have the tools to prove it.
If you’re ever thinking of integrating SAML into a product, be sure to run an LLM evaluation against your implementation. And if you want some common vulnerability patterns to look for, feel free to reach out to me!