CVE-2026-123816
pyLoad: Authentication bypass in API key validation (check_apikey cache)
pyLoad is a free and open-source download manager written in Python. Prior to 0.5.0b3.dev101, Api.check_apikey caches successful validation in self._apikey_cache using only the attacker-controlled key_id. After a legitimate key populates the cache, a forged API key with the same key_id reaches the cache-hit path without comparing apikey[-43:] to the stored hash through hmac.compare_digest. Key identifiers are small sequential integers, so an unauthenticated remote attacker can enumerate an active identifier and inherit the cached key owner's permissions during the 300-second cache lifetime. This issue is fixed in version 0.5.0b3.dev101.
Attack profile
How easy it is to exploit
- Attack vector
- Network
- Complexity
- High
- Privileges needed
- None
- User interaction
- None
What an attacker can affect
- Confidentiality
- High
- Integrity
- High
- Availability
- High
Exploit likelihood
What each source reports
CVE List Score used
8.1
pyLoad: Authentication bypass in API key validation (check_apikey cache)
pyLoad is a free and open-source download manager written in Python. Prior to 0.5.0b3.dev101, Api.check_apikey caches successful validation in self._apikey_cache using only the attacker-controlled key_id. After a legitimate key populates the cache, a forged API key with the same key_id reaches the cache-hit path without comparing apikey[-43:] to the stored hash through hmac.compare_digest. Key identifiers are small sequential integers, so an unauthenticated remote attacker can enumerate an active identifier and inherit the cached key owner's permissions during the 300-second cache lifetime. This issue is fixed in version 0.5.0b3.dev101.
- Record
CVE-2026-123816- CVSS 3.1
- 8.1 High
CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:H - Weakness
- CWE-287
- Updated
- 2026-11-10
EUVDEUVD-2026-120452
8.1
pyLoad is a free and open-source download manager written in Python. Prior to 0.5.0b3.dev101, Api.check_apikey caches successful validation in self._apikey_cache using only the attacker-controlled key_id. After a legitimate key populates the cache, a forged API key with the same key_id reaches the cache-hit path without comparing apikey[-43:] to the stored hash through hmac.compare_digest. Key identifiers are small sequential integers, so an unauthenticated remote attacker can enumerate an active identifier and inherit the cached key owner's permissions during the 300-second cache lifetime. This issue is fixed in version 0.5.0b3.dev101.
- Record
EUVD-2026-120452- CVSS 3.1
- 8.1 High
CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:H - Updated
- 2026-11-09
GitHub AdvisoriesGHSA-r44w-v6gf-x3p6
8.1
pyLoad has an authentication bypass in API key validation (check_apikey cache)
Summary
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.pyApi.check_apikey()
-
src/pyload/core/database/apikey_database.pycheck_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:
iduser_idnamecreated_atexpires_atlast_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…
- Record
GHSA-r44w-v6gf-x3p6- CVSS 3.1
- 8.1 High
CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:H - Weakness
- CWE-287
- Updated
- 2026-11-09
NVD
8.1
pyLoad is a free and open-source download manager written in Python. Prior to 0.5.0b3.dev101, Api.check_apikey caches successful validation in self._apikey_cache using only the attacker-controlled key_id. After a legitimate key populates the cache, a forged API key with the same key_id reaches the cache-hit path without comparing apikey[-43:] to the stored hash through hmac.compare_digest. Key identifiers are small sequential integers, so an unauthenticated remote attacker can enumerate an active identifier and inherit the cached key owner's permissions during the 300-second cache lifetime. This issue is fixed in version 0.5.0b3.dev101.
- Record
CVE-2026-123816- CVSS 3.1
- 8.1 High
CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:H - Weakness
- CWE-287
- Updated
- 2026-11-10
OSV
8.1
pyLoad: Authentication bypass in API key validation (check_apikey cache)
pyLoad is a free and open-source download manager written in Python. Prior to 0.5.0b3.dev101, Api.check_apikey caches successful validation in self._apikey_cache using only the attacker-controlled key_id. After a legitimate key populates the cache, a forged API key with the same key_id reaches the cache-hit path without comparing apikey[-43:] to the stored hash through hmac.compare_digest. Key identifiers are small sequential integers, so an unauthenticated remote attacker can enumerate an active identifier and inherit the cached key owner's permissions during the 300-second cache lifetime. This issue is fixed in version 0.5.0b3.dev101.
- Record
CVE-2026-123816- CVSS 3.1
- 8.1 High
CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:H - Weakness
- CWE-287
- Updated
- 2026-11-11
OSVGHSA-r44w-v6gf-x3p6
8.1
pyLoad has an authentication bypass in API key validation (check_apikey cache)
Summary
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.pyApi.check_apikey()
-
src/pyload/core/database/apikey_database.pycheck_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:
iduser_idnamecreated_atexpires_atlast_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…
- Record
GHSA-r44w-v6gf-x3p6- CVSS 3.1
- 8.1 High
CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:H - Weakness
- CWE-287
- Updated
- 2026-11-09
Affected products
Affected packages
| Package | Affected | Fixed |
|---|---|---|
pyload-ngPyPI |
before 0.5.0b3.dev101 | 0.5.0b3.dev101 |
pyload-ngPyPI |
before 0.5.0b3.dev101, plus listed versions 0.5.0a5.dev5280.5.0a5.dev5320.5.0a5.dev5350.5.0a5.dev5360.5.0a5.dev537 Show 45 moreShow less0.5.0a5.dev5390.5.0a5.dev5400.5.0a5.dev5450.5.0a5.dev5620.5.0a5.dev5640.5.0a5.dev5650.5.0a6.dev5700.5.0a6.dev5780.5.0a6.dev5870.5.0a7.dev5960.5.0a8.dev6020.5.0a9.dev6150.5.0a9.dev6290.5.0a9.dev6320.5.0a9.dev6410.5.0a9.dev6430.5.0a9.dev6550.5.0a9.dev8060.5.0b1.dev10.5.0b1.dev20.5.0b1.dev30.5.0b1.dev40.5.0b1.dev50.5.0b2.dev100.5.0b2.dev110.5.0b2.dev120.5.0b2.dev90.5.0b3.dev1000.5.0b3.dev130.5.0b3.dev140.5.0b3.dev170.5.0b3.dev180.5.0b3.dev190.5.0b3.dev200.5.0b3.dev210.5.0b3.dev220.5.0b3.dev240.5.0b3.dev260.5.0b3.dev270.5.0b3.dev280.5.0b3.dev290.5.0b3.dev300.5.0b3.dev310.5.0b3.dev320.5.0b3.dev33 |
0.5.0b3.dev101 |
History
| Recorded | Changed | Sources |
|---|---|---|
| references | CVE List | |
| references | NVD, OSV | |
| description, references, severity, title | CVE List | |
| aliases, products | EUVD, OSV | |
| aliases, facts, id | GitHub Advisories | |
| first indexed | GitHub Advisories, OSV |
Dates are when Every CVE recorded each change. Upstream dates may differ.
In short
CVE-2026-123816 is a high vulnerability (CVSS 8.1) in Pyload, published October 9, 2026. No source lists public exploit code. EPSS puts the chance of exploitation in the next 30 days at 0.38%, the 32nd percentile. Fixed in pyload-ng 0.5.0b3.dev101.
References
github.com/pyload/pyload/security/advisories/GHSA-r44w-v6gf-x3p6
github.com/pyload/pyload/commit/00d1372510f915f843b1574a3f1b28fbb724a42e
github.com/advisories/GHSA-r44w-v6gf-x3p6
github.com/CVEProject/cvelistV5/tree/main/cves/2026/123xxx/CVE-2026-123816.json
nvd.nist.gov/vuln/detail/CVE-2026-123816
github.com/pyload/pyload
Left out here: 55 versions and the rest of 2 source descriptions. The JSON record has them.