9 February 2025 · auth · – views
A hardcoded SAML signing key, and logging in as anyone
Some bugs are complicated. This one is not, and that's what makes it a good story. A product let you log in with your directory account through SAML. The whole security of SAML rests on one thing: a signature you can't forge. The key used to make that signature was hardcoded in the code and identical on every install. Once I had it, I could sign a login for any user I wanted. No password, no brute-force, just a forged assertion the app trusted completely.
How SAML login is supposed to work
SAML is the thing behind a lot of "log in with your company account" flows. Keep the roles simple. There's the identity provider (IdP), which knows who you are and vouches for you, and the service provider (SP), the app you're trying to get into. When you log in, the IdP produces a signed document, a SAML assertion, that basically says "this is user alice, I've checked, let her in." The app reads that document and logs you in as whoever it names.
The only reason the app can trust that document is the signature. The IdP signs the assertion with its private key, and the app verifies it with the matching public key. Anyone can write a document that says "this is alice." What they can't do, in theory, is sign it so the app believes it. The private key is the whole trust anchor. Everything rides on it staying secret.
The bug: the private key was in the code
I traced the login through the app to the function that signs the assertion, and followed it to where the signing key came from. It wasn't generated per install, wasn't pulled from a protected store, wasn't unique to the deployment. It was a private key written directly into the product's code, the same key shipped to every customer who installed the software.
A signature only means something if one party can make it and nobody else can. A private key that ships inside the product is a private key everyone has. The signature stops proving anything.
That single fact breaks the entire model. The app trusts SAML assertions because they're signed with a key only the IdP should hold. But that key was sitting in a file anyone with a copy of the software could read. So I could become the IdP.
Forging a login
With the private key in hand, the attack is just "do what the IdP does, myself." I wrote a small script that builds a SAML assertion naming the user I wanted to be, signs it with the extracted key, and produces exactly the kind of response the app expects to receive after a real login.
1. build a SAML assertion: "this is <target user>, authenticated" 2. sign it with the hardcoded private key 3. deliver it to the app's SAML login endpoint 4. the app verifies the signature, it checks out, and issues a session
The app validated the signature, found it correct (of course it did, it was signed with the real key), trusted the assertion, and handed back a logged-in session for the named user. I took the session cookies from the script's output, dropped them into a browser, refreshed, and I was logged in as that user. Point it at an admin and you have the product.
The only precondition is knowing a valid username to impersonate, which for directory accounts is rarely a secret. No password ever entered the picture, because SAML was designed so the app never sees the password. It trusts the signature instead, and the signature was forgeable.
The fix
Simple to state, and it's the obvious one: don't hardcode the key. Generate a fresh signing key during installation, unique to that deployment, and keep it out of the shipped code. Then the key on your install is yours alone, an attacker with a copy of the software doesn't have your key, and a forged assertion no longer verifies. The whole attack depends on the key being shared and known; make it unique and secret and there's nothing to forge with.
What to take from it
Whenever a system trusts something because it's signed, encrypted, or "verified," my first question now is: where does the key live, and who else has it. A perfect signature check is worthless if the signing key is public, or shipped, or the same everywhere. I look for keys and secrets baked into code or configuration, defaults that never get rotated, and anything described as "the key" in the singular across many installs. The cryptography here was fine. The key management wasn't, and key management is usually where these fall down.