# pyLoad has an authentication bypass in API key validation (check_apikey cache)

pyLoad's API key cache keyed only on sequential key_id skips secret verification on cache hits, letting attackers forge keys and bypass auth.

- Payout: Research
- Severity: high
- Program: pyLoad
- Found by: nirtem
- Disclosed: 2026-10-09
- Write-up quality: 7/10
- Original: https://github.com/advisories/GHSA-r44w-v6gf-x3p6
- Library page: https://bugbountylibrary.com/r/ghsa%3AGHSA-r44w-v6gf-x3p6
- Tags: area:web domain:web impact:ato impact:privesc kind:research platform:ghsa sev:high tech:python technique:source-review vuln:access-control vuln:auth-bypass

**Target:** pyLoad web API authentication (check_apikey)
**Root cause:** The API key validation cache is keyed solely on the user-supplied key_id and stores no key hash, so cache hits return success after checking only expires_at without ever comparing the secret via hmac.compare_digest.
**Steps:**
1. Create admin account and generate API key `pl_11<43-char-secret>` (key_id=1).
2. Send legitimate request: `curl -H "X-API-Key: pl_11<real-43-char-secret>" http://127.0.0.1:8000/api/status` → 200, populating cache.
3. Within 300s send forged key: `curl -H "X-API-Key: pl_11xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" http://127.0.0.1:8000/api/status` → 200 OK.
4. Control: send forged key before any legitimate request → 401 Unauthorized, isolating the cache-hit path.
**How found:** Code review of check_apikey() cache path revealed the cache key derives from user-supplied key_id and cached data omits key_hash, so the secret comparison in _check_key() is unreachable on cache hits.
**Exploitation:** Attacker enumerates small sequential key_ids, constructs a forged API key with the correct key_id prefix and arbitrary secret, and sends it within the 300s TTL after the legitimate key was used; the request authenticates as the key's owner with full permissions.
**Escalation:** Enumerating key_id 1 (admin) yields full administrative access.
**Insight:** When auditing auth caches, check whether the cache key is attacker-controlled and whether the cached record retains the secret/hash needed to re-verify; a cache hit that skips hmac.compare_digest is a full bypass.

