Lab 4 JWT authentication bypass via jwk header injection
This lab uses a JWT-based mechanism for handling sessions. The server supports the jwk parameter in the JWT header. This is sometimes used to embed the correct verification key directly in the token. However, it fails to check whether the provided key came from a trusted source.
To solve the lab, modify and sign a JWT that gives you access to the admin panel at /admin, then delete the user carlos.
You can log in to your own account using the following credentials: wiener:peter
Vuln: Server accepts a self-embedded public key (jwk header) for signature verification instead of using its own trusted keystore.
Steps:
Log in as
wiener, capture session JWT, send to Repeater.Confirm
/adminrequiressub: administrator.In JWT Editor Keys tab, generate a new RSA key pair.
Edit payload:
sub β administrator.Click Attack β Embedded JWK, select the generated key.
Extension signs the token with the private key and embeds the matching public key +
kidinto the header automatically.Send to
/adminβ access granted.Delete
carlosvia/admin/delete?username=carlos.
Root cause: Server verifies using whatever key the token supplies (jwk), instead of only trusting keys from its own pre-approved keystore.
Fix: Ignore embedded jwk headers entirely; verify only against a fixed, server-side whitelist of trusted keys.

Header becomes
Understanding the Forged jwk Header
Field-by-field
alg: RS256
Tells server to verify using RSA-SHA256
kid (top-level)
ID used by server's key-lookup logic to pick a key
jwk
Embedded JSON Web Key - the public key to verify with
kty: RSA
Key type = RSA
e: AQAB
Public exponent (standard value, 65537)
n
RSA modulus - the actual public key material, base64url-encoded
kid (inside jwk)
Matches the outer kid, so lookup logic links the two
Key point: The n/e pair is the public half of a key pair you generated yourself. You hold the private key locally - the server has never seen it before and doesn't need to.
What you did to get here
Generated your own RSA key pair (unrelated to server's real key).
Edited payload:
sub β administrator.Signed the token using your private key.
Embedded your public key in
jwk, with matchingkid.
What the server does with this token
Reads
alg: RS256β prepares RSA verification.Sees
jwkin header β extractsn/eβ builds a public key object from it directly (instead of pulling a key from its own trusted store).Runs:
rsa_verify(embedded_public_key, signing_input, signature).Verification succeeds - not because of any crypto weakness, but because you supplied both the signature and the matching public key. Mathematically, of course they match.
Server treats token as authentic β reads
sub: administratorβ grants admin access.
The one-line takeaway
Server asks "does the signature match this key?" - not "should I trust this key?" You supplied both the signature and the key, so the check passes by construction.
A secure server would ignore jwk entirely and only verify against keys from its own internal, pre-approved keystore.
Last updated