Storm-0558: When a Stolen Signing Key Forges Any Token
Most breach write-ups end with a stolen password or a missed patch. Storm-0558 is different, and more instructive for anyone who runs an identity provider or validates tokens: the attackers never logged in. They forged the proof of login. In mid-2023 a China-based actor that Microsoft tracks as Storm-0558 used an acquired signing key to mint authentication tokens and read mailboxes at roughly 25 organizations, including US government agencies.
What happened
Per Microsoft’s own account and its later root-cause post, the actor obtained an inactive signing key for Microsoft’s consumer accounts (MSA). Using it, they forged tokens that Exchange Online accepted as if they were issued for enterprise Azure AD users. They then called the Outlook Web Access API directly, with no password and no MFA prompt.
Two failures had to line up:
- Key custody. Microsoft’s investigation concluded the key most likely leaked via a 2021 crash dump that should have been scrubbed of key material but wasn’t, and that ended up in a debugging environment. An engineer’s corporate account was later compromised. Microsoft said it could not confirm that path with certainty because of missing logs.
- Validation flaw. A library issue meant Exchange Online accepted tokens signed with the consumer key for enterprise identities. A key that should never have been trusted for that audience was.
The US Cyber Safety Review Board later called the intrusion preventable and criticized Microsoft’s security culture and its public statements about the root cause.
How it was caught
Not by the vendor. A customer noticed. The State Department detected anomalous
mail access using the MailItemsAccessed audit event, which at the time was
only available at a premium licensing tier. The intrusion had run for about a
month by then. The lesson transfers directly: the logs that catch a
forged-identity attack are often the ones locked behind the expensive plan.
Microsoft has since expanded access to that logging.
Blast radius
A signing key is a root of trust. Compromising it is not “one account” or “one tenant”; it is every identity the verifier will accept for that key. The attacker only needed to know a target’s identity, not touch it. Because the tokens were valid, nothing downstream of the validator could tell the difference. That includes SIEM rules keyed on failed logins, impossible travel, or MFA challenges, none of which fire when no authentication happens.
What this means for AWS and cloud builders
You probably don’t run Exchange, but the pattern applies to anything that signs or verifies tokens:
- Your own JWT/OIDC issuers. If you mint tokens for internal services, the signing key deserves the same protection as a root credential. Keep it in a KMS or HSM-backed service where it cannot be exported, rather than a file or environment variable that can land in a crash dump or backup.
- OIDC trust to AWS. IAM roles trusted via an OIDC provider (GitHub
Actions, EKS IRSA) rely on the issuer’s signing keys. Constrain trust
policies with
subandaudconditions so a forged or misdirected token still has to match a specific subject. See my GitHub Actions OIDC tutorial for the condition keys. - Audience and issuer checks. The Microsoft bug was a verifier trusting a
key for the wrong population. Validate
iss,aud, and key-to-tenant binding explicitly; “signature is valid” alone is not enough.
Remediation checklist
- Inventory signing keys. List every system that issues tokens, SAML assertions, or signed URLs, and where each private key lives.
- Move keys to non-exportable storage (KMS asymmetric keys, HSM) and
restrict
kms:Signto the issuing service role. - Rotate on a schedule, and practice emergency rotation. Retire old keys fully; an “inactive” key that still validates is a liability, which is exactly what happened here.
- Scrub diagnostics. Treat crash dumps, core files, and debug snapshots as secret-bearing. Restrict where they are stored and who can open them.
- Turn on the high-fidelity audit logs now, including object and mailbox access events, and alert on access with no matching sign-in event.
- Detect authentication without a login. Correlate API calls with a preceding interactive or token-exchange event. Access with no origin is the signature of forged credentials.
Takeaway
Storm-0558 shows that identity security is only as strong as the least-guarded signing key and the least-careful validation check. Assume a key will leak someday, then design so the leak is detectable, bounded to a narrow audience, and fast to rotate.
Join the discussion
Comments for this post live on social — reply to the thread.
Related posts
CircleCI Breach: Stolen Session Cookies and Your CI Secrets
One infected engineer laptop, one stolen SSO session, and every customer secret in CircleCI was in play. What failed, the blast radius, and how to make CI secrets cheap to rotate.
The chalk/debug npm Compromise: Anatomy of a Package Hijack
One phished maintainer turned 18 ubiquitous npm packages into a crypto-stealing payload in about two hours. Here is how it worked and the build-pipeline controls that blunt it.
Snowflake Customer Breaches: Why Passwords Without MFA Still Lose
The 2024 Snowflake customer data thefts needed no exploit, just stolen credentials and no MFA. Root cause, blast radius, and a phishing-resistant MFA rollout plan.