The Anatomy of a JWT Hack
5 min read
May 25, 2025

Table of contents
Hey everyone!
Big day: the first chapter of my Docker + Rust series is out, and this newsletter drops alongside it! 🎉
If you want to learn how container security works from the inside out, while watching me build a CLI tool (Valeris) from scratch, go check it out on the blog.
But today, we're shifting gears from containers to credentials.
Let’s talk about JSON Web Tokens (JWTs) those small Base64 blobs that silently carry identity and permissions across APIs. They're everywhere in modern web apps, and when implemented poorly, they open the door to serious attacks.
In this issue, we’ll explore what JWTs really are, how they work, and the most common ways they get hacked: from alg: none
tricks to key injection and algorithm confusion attacks. Real bugs, real CVEs, real exploitation.
Let’s dive in 👇
🧩 What Are JWTs, and Why Do They Matter?
JSON Web Tokens (JWTs) are a compact way to represent claims between two parties. Think of them as signed JSON blobs used in web apps to handle sessions, permissions, and user identity without needing server-side state.
They look like this:
<base64url(header)>.<base64url(payload)>.<base64url(signature)>
Each section is Base64URL-encoded:
- Header – specifies algorithm (
alg
) and token type (typ
) - Payload – includes claims like
sub
,admin
,exp
,iat
, etc. - Signature – ensures the data hasn’t been tampered with
Example payload:
{
"sub": "user1",
"admin": false
}
JWTs are usually sent in an Authorization: Bearer
header. And since the payload is not encrypted by default, anyone with the token can read it but they shouldn’t be able to modify it without invalidating the signature. That’s the theory… let’s see what happens in practice 👇
🔓 Signature Not Verified
This is the deadliest and most common mistake. Some developers use decode()
to parse JWTs without verifying the signature with verify()
.
If the backend skips signature verification:
- You can edit any field (e.g.,
admin: true
) - Keep or remove the signature altogether
- And the server will accept it
📌 Result: total authentication and authorization bypass. You're basically the admin now.
❌ alg: none
Attack
JWTs specify the signing algorithm in the header, and alg: none
means… no signature.
Originally intended for debugging, some libraries (or careless configs) still allow it.
Attack flow:
- Change
"alg"
in the header to"none"
- Remove the signature part
- Modify the payload however you like
- Reassemble:
header.payload.
- Send the token
📌 If accepted, the app is trusting unsigned data. Game over.
🧨 Weak HMAC Secrets (HS256 Brute Force)
When apps use HS256, the same key signs and verifies the token. If that key is weak (e.g., secret
, 123456
, app name…), it can be brute-forced offline.
Steps:
- Capture a valid JWT
- Use tools like
hashcat
orjwt-tool
to brute-force the key - Forge tokens with any payload you want
📌 This works especially well on dev/staging environments, open-source projects, and rushed setups.
🌀 Algorithm Confusion: RS256 ➜ HS256
This one is sneaky.
Suppose the app uses RS256, which relies on a private/public key pair:
- The server signs with the private key
- Verifies with the public key
But if it trusts the alg
field from the JWT, you can:
- Change
RS256
→HS256
- Use the public key (which you might have) as the HMAC secret
- Sign a token with your payload
📌 The server thinks it’s verifying with RSA, but it’s actually verifying a forged HMAC. Access granted.
🔀 Algorithm Confusion: ES256 ➜ HS256
Same idea, different algorithm. Instead of RSA, the server uses ECDSA (ES256).
If the app doesn't enforce the expected algorithm:
- Change
alg: ES256
toHS256
- Use the ECDSA public key as HMAC secret
- Sign your payload
📌 Another case of mixing asymmetric and symmetric crypto. And attackers love it.
🪤kid
Injection (Key ID Manipulation)
JWTs can include a kid
field in the header to indicate which key should be used to verify the token.
But if the app:
- Loads keys from file system using
kid
, or - Performs a raw DB query with
kid
Then you can do things like:
kid: ../../../../dev/null
→ server reads empty keykid: ' UNION SELECT 'fake-key' --
→ SQL injection
📌 Used correctly, you can point the app to a key you control, or force it to use a blank key and sign your own tokens.
🧬 Embedded JWK (CVE-2018-0114)
JWTs also support an optional jwk
field that embeds the public key directly in the token header.
If the server accepts any key from this field without validation, it’s vulnerable.
Attack:
- Generate your own RSA key pair
- Sign the JWT with your private key
- Embed the public key in the
jwk
header - Send it
📌 The app uses your embedded key to verify your fake token. Total bypass.
🌐 JKU / X5U Header Abuse
The jku
and x5u
fields let a token point to external URLs for keys or certs.
If the backend:
- Fetches these keys dynamically
- Doesn’t validate where they come from
Then attackers can:
- Host their own JWKS or X.509 cert
- Sign the token with their private key
- Insert
jku
/x5u
to point at their hosted key - Send the forged token
📌 This isn’t just key injection it can also be a vector for SSRF.
🧪 Claim Confusion (Missing aud
, iss
, sub
Checks)
If an app doesn’t validate claims like audience
or issuer
, attackers can:
- Use tokens issued for a different service
- Reuse tokens across microservices or APIs
- Escalate privileges horizontally
📌 This is surprisingly common in modern, distributed architectures.
⏳ No Expiration / Long-Lived Tokens
JWTs should expire fast. If a token has:
- No
exp
, or - An
exp
set to years in the future
Then:
- It can be reused forever
- Attackers can persist access indefinitely
📌 Often paired with other techniques to maintain access after compromise.
🛠 Tools for JWT Hacking
Keep it lean, sharp, and fast. These are the essentials:
jwt-tool
: All-in-one CLI to decode, tamper, brute-force, and test JWT vulnerabilities.
👉ticarpi/jwt_tool
- Hashcat: Brute-force HMAC secrets (HS256/HS512) offline with GPU power.
👉 Mode16500
for HS256 - Burp Suite + JWT Editor: Decode, modify, re-sign JWTs on the fly. Great for testing
alg
,kid
,jku
, and more. - TruffleHog / Gitleaks: Scan repos for leaked JWTs or weak secrets. Great for recon.
🧪 Where to Practice
Want to test these attacks in the wild (safely)? Here’s where to train:
- 🎯 PortSwigger Web Security Academy
- Realistic JWT labs: signature bypass, weak key brute-forcing, and header abuse
- 👉 Great for hands-on skill building
- 🎮 Hack The Box – Craft
- Combine Git recon with JWT forging and signature cracking
- 👉 Perfect mix of theory + practice
- 🎮 Hack The Box – Awkward
- Crack JWTs with Hashcat, forge tokens, and exploit logic flaws
- 👉 Covers HS256 abuse, scripting, and privilege escalation
- 🎮 Hack The Box – Yummy
- Exploit weak RSA keys using
RsaCtfTool
to forge JWTs - 👉 Learn how broken crypto affects JWTs in real apps
- Exploit weak RSA keys using
- 🎮 Hack The Box – CyberMonday
- Perform an RS256 ➝ HS256 algorithm confusion attack using the server's public key
- 👉 Realistic and highly relevant vulnerability
- 🎮 Hack The Box – Luke
- Find and crack the JWT secret, forge tokens, and access protected APIs
- 👉 End-to-end JWT manipulation and scripting practice
- 🎮 Hack The Box – Blazorized
- Extract hardcoded JWTs from DLLs and inspect traffic for insecure storage
- 👉 Mix of reverse engineering and token abuse
🧭 Final Thoughts
JWTs are everywhere: APIs, mobile apps, single sign-on flows. And when mishandled, they can be your easiest way in.
Start by understanding how they work. Then test. Tamper. Re-sign. Exploit.
Whether you're a pentester, bug hunter, or dev, knowing how to break JWTs means knowing how to protect them.
Until next time,
Stay sharp, stay curious,
Ruben 🚀
Chapters

Previous Issue