Skip to main content
← Vulnerabilities

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

Exploited
No known exploitation
Fix
Fixed in 0.5.0b3.dev101
Who is exposed
Not reported

Do first: Upgrade to a fixed version: 0.5.0b3.dev101.

Published
Last modified
17/100 · provisional
High (ghsa)
None on Exploit-DB, Metasploit or GitHub

Summary

pyLoad has an authentication bypass in its API key check that lets an attacker authenticate with a forged API key while a legitimate key with the same key ID is still in the cache after recent use. Because the cached check never verifies the key's secret, the forged key is accepted until the cache entry expires.

Written by anthropic/claude-haiku-5.5 from the sources below. Verify against them.

Read the original description

The API-key cache in check_apikey() allows an attacker to authenticate with a forged API key as long as the legitimate key for the same key_id has been used recently.

On a cache hit, the API key secret is never verified, resulting in a complete authentication bypass during the cache lifetime.

---

Affected Components

  • src/pyload/core/api/init.py
  • Api.check_apikey()
  • src/pyload/core/database/apikey_database.py
  • check_apikey()
  • _check_key()

The cache is used by normal API requests through:

  • src/pyload/webui/app/helpers.py

which calls:

api.check_apikey(api_key)

without providing a ttl, meaning the default 300-second cache is always enabled.

---

Root Cause

Successful API key validations are cached in:

self._apikey_cache

using the following structure:

{
    key_id: (timestamp, cached_data)
}

The cache key is based only on `key_id`, and that value is parsed directly from the user-supplied API key rather than being obtained from a trusted lookup.

When a cache hit occurs within the TTL, check_apikey() returns:

{
    "success": True,
    "data": cached_data
}

after checking only:

  • expires_at

At no point on this execution path is the supplied secret:

apikey[-43:]

compared against the stored key hash.

---

The actual secret verification exists only in:

_check_key()

which correctly uses:

hmac.compare_digest()

However, this function is only reached on a cache miss through the database layer's check_apikey() implementation.

Furthermore, the cached object cannot be used for verification because it intentionally does not include the stored key hash.

The cached record contains only:

  • id
  • user_id
  • name
  • created_at
  • expires_at
  • last_used

Since key_hash is absent, the cache lacks the information required to validate the presented secret.

As a result, once a legitimate request populates the cache, every subsequent request using the same key_id within the 300-second cache window is accepted without validating the secret.

---

Impact

key_id values are small sequential integers assigned when API keys are created (1, 2, 3, ...), making them trivial to enumerate.

The API key format is also publicly reconstructable from the source code.

An attacker therefore does not need to know any valid API key secret.

They only need a key_id whose legitimate key has been used within the previous five minutes, which is likely on an actively used deployment.

During that cache window, a forged API key follows exactly the same authentication path as a legitimate key and inherits the associated user's permissions.

This results in a complete remote authentication bypass.

I would classify the issue as Critical severity.

---

Estimated CVSS v3.1

AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:H

(AC:H only because successful exploitation depends on the cache already containing an entry.)

---

Reproduction

1. Create an administrator account.

---

2. Generate an API key.

Example:

pl_11<43-character-secret>

where:

key_id = 1

---

3. Send a legitimate authenticated request.

curl \
  -H "X-API-Key: pl_11<real-43-char-secret>" \
  http://127.0.0.1:8000/api/status

Expected response:

HTTP/1.1 200 OK

---

4. Within 300 seconds, send the same request using a forged API key.

curl \
  -H "X-API-Key: pl_11xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
  http://127.0.0.1:8000/api/status

Observed response:

HTTP/1.1 200 OK

The request succeeds even though the API key secret is entirely incorrect.

---

Control Test

If the forged API key is sent before any legitimate request has populated the cache, the server correctly returns:

HTTP/1.1 401 Unauthorized

The only difference between the failing and succeeding forged request is whether the cache already contains an entry for that key_id.

This isolates the vulnerability specifically to the cache-hit path.

---

Suggested Fix

Cache hits should never replace secret verification.

Possible approaches include:

  • Store a value derived from the complete API key (or its hash) in the cache and verify it using hmac.compare_digest() on every cache hit before returning success.
  • Alternatively, key the cache using a value derived from the entire API key secret (for example, a cryptographic hash of the complete API key) rather than using only key_id.

Regardless of implementation, the security invariant should remain:

> The API key secret must be validated on every authenticated request, regardless of whether the cache is hit or missed.

cve.now Index

17Pcve.now Index 17 of 100 · methodology v3 · provisional
  • Exploitation?/40Unknown. No CISA SSVC exploitation rating yet
  • Exposure?/25Unknown. No advisory context yet
  • Reach10/20Affects PyPI packages
  • Severity7/10Rated high by ghsa
  • No patch0/5No signal

What would change the score

  • Would score about 72 if added to CISA KEV
  • Would score about 67 if CISA's SSVC rates exploitation active
  • Would score about 39 if advisories confirm default setups are exposed

Impact

  • Widely used

    pyload-ng has 1.2k weekly downloads.Source: pypistats.org

    1 packages depend on pyload-ng.Source: deps.dev

What to do

  • Upgrade to a fixed version: 0.5.0b3.dev101.

Timeline

  1. Advisory published
  2. Record last updated

Affected versions

  • fixed in 0.5.0b3.dev101

    Fixed in 0.5.0b3.dev101 (released Jul 9, 2026, 92 days before disclosure).

    The latest release, 0.5.0b3.dev102, is not affected.

Evidence 3

  1. Advisory
  2. Repository
  3. Repository

Corrections

None so far.

Something wrong on this page? Report an error. Every fix is listed here.

Questions about GHSA-R44W-V6GF-X3P6

Is GHSA-R44W-V6GF-X3P6 being exploited?

It is not in CISA's Known Exploited Vulnerabilities catalog, and CISA's SSVC assessment does not mark exploitation as active. That does not mean it has not been attacked: vendors and researchers can report attacks before CISA lists them. No public exploit code is tracked.Source: cve.now feed data

Which versions are affected by GHSA-R44W-V6GF-X3P6?

pyload-ng (PyPI): before 0.5.0b3.dev101.Source: cve.now feed data

How do I fix GHSA-R44W-V6GF-X3P6?

Upgrade to a fixed version: 0.5.0b3.dev101.Source: cve.now feed data