{"id":"CVE-2026-44226","aliases":["EUVD-2026-29131","GHSA-c3gc-9pf2-84gg","PYSEC-2026-2993"],"status":"published","title":"pyLoad: Unauthenticated traceback disclosure via global exception handler in WebUI","description":"pyLoad is a free and open-source download manager written in Python. Prior to 0.5.0b3.dev100, pyload-ng WebUI returns full Python traceback details to clients on unhandled exceptions. Because /web/\u003cpath:filename\u003e is reachable without authentication and renders attacker-controlled template names, an unauthenticated user can reliably trigger a server exception (for example by requesting a non-existent template) and receive internal stack traces in the HTTP response. This vulnerability is fixed in 0.5.0b3.dev100.","severity":{"version":"3.1","score":5.3,"vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:N/A:N","level":"medium","assessor":"cna","from":"cve"},"epss":{"day":"2026-11-12","score":0.00336,"percentile":0.27394,"model":"v2026.06.15"},"facts":{"in_kev":false,"known_exploited":false,"public_exploit":false,"fix_available":true},"cwes":["CWE-209"],"published":"2026-05-06T17:54:20Z","modified":"2026-11-11T20:10:00Z","fixes":[{"ecosystem":"PyPI","package":"pyload-ng","fixed_in":["0.5.0b3.dev100"],"sources":["ghsa","osv"]}],"affected":[{"source":"ghsa","ecosystem":"PyPI","package":"pyload-ng","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"},{"fixed":"0.5.0b3.dev100"}]}]},{"source":"osv","ecosystem":"PyPI","package":"pyload-ng","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"},{"fixed":"0.5.0b3.dev100"}]}],"versions":["0.5.0a5.dev528","0.5.0a5.dev532","0.5.0a5.dev535","0.5.0a5.dev536","0.5.0a5.dev537","0.5.0a5.dev539","0.5.0a5.dev540","0.5.0a5.dev545","0.5.0a5.dev562","0.5.0a5.dev564","0.5.0a5.dev565","0.5.0a6.dev570","0.5.0a6.dev578","0.5.0a6.dev587","0.5.0a7.dev596","0.5.0a8.dev602","0.5.0a9.dev615","0.5.0a9.dev629","0.5.0a9.dev632","0.5.0a9.dev641","0.5.0a9.dev643","0.5.0a9.dev655","0.5.0a9.dev806","0.5.0b1.dev1","0.5.0b1.dev2","0.5.0b1.dev3","0.5.0b1.dev4","0.5.0b1.dev5","0.5.0b2.dev10","0.5.0b2.dev11","0.5.0b2.dev12","0.5.0b2.dev9","0.5.0b3.dev13","0.5.0b3.dev14","0.5.0b3.dev17","0.5.0b3.dev18","0.5.0b3.dev19","0.5.0b3.dev20","0.5.0b3.dev21","0.5.0b3.dev22","0.5.0b3.dev24","0.5.0b3.dev26","0.5.0b3.dev27","0.5.0b3.dev28","0.5.0b3.dev29","0.5.0b3.dev30","0.5.0b3.dev31","0.5.0b3.dev32","0.5.0b3.dev33","0.5.0b3.dev34","0.5.0b3.dev35","0.5.0b3.dev38","0.5.0b3.dev39","0.5.0b3.dev40","0.5.0b3.dev41","0.5.0b3.dev42","0.5.0b3.dev43","0.5.0b3.dev44","0.5.0b3.dev45","0.5.0b3.dev46","0.5.0b3.dev47","0.5.0b3.dev48","0.5.0b3.dev49","0.5.0b3.dev50","0.5.0b3.dev51","0.5.0b3.dev52","0.5.0b3.dev53","0.5.0b3.dev54","0.5.0b3.dev57","0.5.0b3.dev60","0.5.0b3.dev62","0.5.0b3.dev64","0.5.0b3.dev65","0.5.0b3.dev66","0.5.0b3.dev67","0.5.0b3.dev68","0.5.0b3.dev69","0.5.0b3.dev70","0.5.0b3.dev71","0.5.0b3.dev72","0.5.0b3.dev73","0.5.0b3.dev74","0.5.0b3.dev75","0.5.0b3.dev76","0.5.0b3.dev77","0.5.0b3.dev78","0.5.0b3.dev79","0.5.0b3.dev80","0.5.0b3.dev81","0.5.0b3.dev82","0.5.0b3.dev85","0.5.0b3.dev87","0.5.0b3.dev88","0.5.0b3.dev89","0.5.0b3.dev90","0.5.0b3.dev91","0.5.0b3.dev92","0.5.0b3.dev93","0.5.0b3.dev94","0.5.0b3.dev95","0.5.0b3.dev96","0.5.0b3.dev97","0.5.0b3.dev98","0.5.0b3.dev99"]},{"source":"osv","ecosystem":"PyPI","package":"pyload-ng","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"},{"fixed":"0.5.0b3.dev100"}]}],"versions":["0.5.0a5.dev528","0.5.0a5.dev532","0.5.0a5.dev535","0.5.0a5.dev536","0.5.0a5.dev537","0.5.0a5.dev539","0.5.0a5.dev540","0.5.0a5.dev545","0.5.0a5.dev562","0.5.0a5.dev564","0.5.0a5.dev565","0.5.0a6.dev570","0.5.0a6.dev578","0.5.0a6.dev587","0.5.0a7.dev596","0.5.0a8.dev602","0.5.0a9.dev615","0.5.0a9.dev629","0.5.0a9.dev632","0.5.0a9.dev641","0.5.0a9.dev643","0.5.0a9.dev655","0.5.0a9.dev806","0.5.0b1.dev1","0.5.0b1.dev2","0.5.0b1.dev3","0.5.0b1.dev4","0.5.0b1.dev5","0.5.0b2.dev10","0.5.0b2.dev11","0.5.0b2.dev12","0.5.0b2.dev9","0.5.0b3.dev13","0.5.0b3.dev14","0.5.0b3.dev17","0.5.0b3.dev18","0.5.0b3.dev19","0.5.0b3.dev20","0.5.0b3.dev21","0.5.0b3.dev22","0.5.0b3.dev24","0.5.0b3.dev26","0.5.0b3.dev27","0.5.0b3.dev28","0.5.0b3.dev29","0.5.0b3.dev30","0.5.0b3.dev31","0.5.0b3.dev32","0.5.0b3.dev33","0.5.0b3.dev34","0.5.0b3.dev35","0.5.0b3.dev38","0.5.0b3.dev39","0.5.0b3.dev40","0.5.0b3.dev41","0.5.0b3.dev42","0.5.0b3.dev43","0.5.0b3.dev44","0.5.0b3.dev45","0.5.0b3.dev46","0.5.0b3.dev47","0.5.0b3.dev48","0.5.0b3.dev49","0.5.0b3.dev50","0.5.0b3.dev51","0.5.0b3.dev52","0.5.0b3.dev53","0.5.0b3.dev54","0.5.0b3.dev57","0.5.0b3.dev60","0.5.0b3.dev62","0.5.0b3.dev64","0.5.0b3.dev65","0.5.0b3.dev66","0.5.0b3.dev67","0.5.0b3.dev68","0.5.0b3.dev69","0.5.0b3.dev70","0.5.0b3.dev71","0.5.0b3.dev72","0.5.0b3.dev73","0.5.0b3.dev74","0.5.0b3.dev75","0.5.0b3.dev76","0.5.0b3.dev77","0.5.0b3.dev78","0.5.0b3.dev79","0.5.0b3.dev80","0.5.0b3.dev81","0.5.0b3.dev82","0.5.0b3.dev85","0.5.0b3.dev87","0.5.0b3.dev88","0.5.0b3.dev89","0.5.0b3.dev90","0.5.0b3.dev91","0.5.0b3.dev92","0.5.0b3.dev93","0.5.0b3.dev94","0.5.0b3.dev95","0.5.0b3.dev96","0.5.0b3.dev97","0.5.0b3.dev98","0.5.0b3.dev99"]}],"products":[{"vendor":"pyload","product":"pyload"}],"references":[{"url":"https://github.com/pyload/pyload/security/advisories/GHSA-c3gc-9pf2-84gg","tags":["x_refsource_CONFIRM","exploit","Exploit","Vendor Advisory","web"]},{"url":"https://nvd.nist.gov/vuln/detail/CVE-2026-44226","tags":["advisory"]},{"url":"https://github.com/advisories/GHSA-c3gc-9pf2-84gg","tags":["advisory"]},{"url":"https://github.com/pyload/pyload","tags":["package"]},{"url":"https://pypi.org/project/pyload-ng","tags":["package"]}],"sources":{"cve":[{"id":"CVE-2026-44226","title":"pyLoad: Unauthenticated traceback disclosure via global exception handler in WebUI","description":"pyLoad is a free and open-source download manager written in Python. Prior to 0.5.0b3.dev100, pyload-ng WebUI returns full Python traceback details to clients on unhandled exceptions. Because /web/\u003cpath:filename\u003e is reachable without authentication and renders attacker-controlled template names, an unauthenticated user can reliably trigger a server exception (for example by requesting a non-existent template) and receive internal stack traces in the HTTP response. This vulnerability is fixed in 0.5.0b3.dev100.","status":"published","published":"2026-05-11T16:36:35.156Z","modified":"2026-05-11T18:27:05.705Z","cvss":[{"version":"3.1","score":5.3,"vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:N/A:N","level":"medium","assessor":"cna"}],"cwes":["CWE-209"],"updated_at":"2026-11-02T13:42:49.132798Z"}],"euvd":[{"id":"EUVD-2026-29131","description":"pyLoad is a free and open-source download manager written in Python. Prior to 0.5.0b3.dev100, pyload-ng WebUI returns full Python traceback details to clients on unhandled exceptions. Because /web/\u003cpath:filename\u003e is reachable without authentication and renders attacker-controlled template names, an unauthenticated user can reliably trigger a server exception (for example by requesting a non-existent template) and receive internal stack traces in the HTTP response. This vulnerability is fixed in 0.5.0b3.dev100.","status":"published","published":"2026-05-11T16:36:35Z","modified":"2026-05-11T18:27:05Z","cvss":[{"version":"3.1","score":5.3,"vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:N/A:N","level":"medium"}],"updated_at":"2026-11-02T19:05:10.375554Z"}],"ghsa":[{"id":"GHSA-c3gc-9pf2-84gg","title":"PyLoad vulnerable to unauthenticated traceback disclosure via global exception handler in WebUI","description":"### Summary\n`pyload-ng` WebUI returns full Python traceback details to clients on unhandled exceptions.\n\nBecause `/web/\u003cpath:filename\u003e` is reachable without authentication and renders attacker-controlled template names, an unauthenticated user can reliably trigger a server exception (for example by requesting a non-existent template) and receive internal stack traces in the HTTP response.\n\n### Details\nThe issue is caused by the combination of:\n\n1. Unauthenticated template-render route:\n- `src/pyload/webui/app/blueprints/app_blueprint.py:32-36`\n  - `@bp.route(\"/web/\u003cpath:filename\u003e\", endpoint=\"web\")`\n  - `data = render_template(filename)` with user-controlled `filename`\n  - no `@login_required(...)` on this route\n\n2. Global exception handler exposes traceback to response:\n- `src/pyload/webui/app/handlers.py:14-27`\n  - `tb = traceback.format_exc()`\n  - `messages.extend(tb.split('\\n'))`\n  - returned in rendered error page for all exceptions\n\n3. Error page renders all `messages`:\n- `src/pyload/webui/app/themes/modern/templates/base.html:217-219`\n  - loops over `messages` and prints them in response HTML\n\nSo any unhandled exception can disclose internal implementation details (stack frames, source paths, exception metadata) to remote unauthenticated clients. \n\nThis is a core behavior issue in default WebUI error handling\n\n### PoC\n```python\n#!/usr/bin/env python3\nfrom __future__ import annotations\n\nimport re\nimport shutil\nimport tempfile\nimport traceback\nfrom pathlib import Path\n\n\nROOT = Path(__file__).resolve().parent / \"pyload\" / \"src\" / \"pyload\"\n\n\ndef read_text(rel: str) -\u003e str:\n    return (ROOT / rel).read_text(encoding=\"utf-8\")\n\n\ndef route_has_no_login_required(app_blueprint: str) -\u003e bool:\n    m = re.search(\n        r'@bp\\\\.route\\\\(\"/web/\u003cpath:filename\u003e\", endpoint=\"web\"\\\\)\\\\s*'\n        r\"def render\\\\(filename\\\\):(?P\u003cbody\u003e.*?)(?:\\\\n\\\\n@bp\\\\.route|\\\\Z)\",\n        app_blueprint,\n        re.DOTALL,\n    )\n    if not m:\n        return False\n    block_start = max(0, m.start() - 200)\n    block = app_blueprint[block_start:m.end()]\n    return \"@login_required(\" not in block\n\n\ndef main() -\u003e None:\n    workdir = Path(tempfile.mkdtemp(prefix=\"pyload-traceback-infoleak-\"))\n    try:\n        app_blueprint = read_text(\"webui/app/blueprints/app_blueprint.py\")\n        handlers = read_text(\"webui/app/handlers.py\")\n        base_template = read_text(\"webui/app/themes/modern/templates/base.html\")\n\n        unauth_web_route = '/web/\u003cpath:filename\u003e' in app_blueprint and route_has_no_login_required(app_blueprint)\n        user_controlled_template_name = \"render_template(filename)\" in app_blueprint\n        handler_uses_traceback = \"traceback.format_exc()\" in handlers\n        handler_appends_trace = \"messages.extend(tb.split('\\\\n'))\" in handlers\n        global_exception_handler = \"(Exception, handle_exception_error)\" in handlers\n        template_renders_messages = \"{% for message in messages %}\" in base_template and \"{{message}}\" in base_template\n\n        leaked_traceback_keyword = False\n        leaked_exception_type = False\n        try:\n            raise RuntimeError(\"forced-poc-error\")\n        except Exception:\n            tb = traceback.format_exc()\n            messages = [f\"Error 500: forced-poc-error\"]\n            messages.extend(tb.split(\"\\\\n\"))\n            joined = \"\\\\n\".join(messages)\n            leaked_traceback_keyword = \"Traceback (most recent call last)\" in joined\n            leaked_exception_type = \"RuntimeError: forced-poc-error\" in joined\n\n        repro_success = all(\n            [\n                unauth_web_route,\n                user_controlled_template_name,\n                handler_uses_traceback,\n                handler_appends_trace,\n                global_exception_handler,\n                template_renders_messages,\n                leaked_traceback_keyword,\n                leaked_exception_type,\n            ]\n        )\n\n        print(\"unauth_web_route=\", unauth_web_route)\n        print(\"user_controlled_template_name=\", user_controlled_template_name)\n        print(\"handler_uses_traceback=\", handler_uses_traceback)\n        print(\"handler_appends_trace=\", handler_appends_trace)\n        print(\"global_exception_handler=\", global_exception_handler)\n        print(\"template_renders_messages=\", template_renders_messages)\n        print(\"leaked_traceback_keyword=\", leaked_traceback_keyword)\n        print(\"leaked_exception_type=\", leaked_exception_type)\n        print(\"traceback_infoleak_repro_success=\", repro_success)\n    finally:\n        shutil.rmtree(workdir, ignore_errors=True)\n        print(\"cleanup_done=True\")\n\n\nif __name__ == \"__main__\":\n    main()\n```\n\nObserved result:\n```text\nunauth_web_route= True\nuser_controlled_template_name= True\nhandler_uses_traceback= True\nhandler_appends_trace= True\nglobal_exception_handler= True\ntemplate_renders_messages= True\nleaked_traceback_keyword= True\nleaked_exception_type= True\ntraceback_infoleak_repro_success= True\ncleanup_done=True\n```\n\n### Impact\n- Vulnerability type: Information disclosure (stack trace / internal path leakage).\n- Attack surface: unauthenticated WebUI request path.\n- Exposes internal error details that help attackers map application internals and improve exploit reliability for follow-on attacks.","status":"published","published":"2026-05-06T17:54:20Z","modified":"2026-05-13T14:18:14Z","cvss":[{"version":"3.1","score":5.3,"vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:N/A:N","level":"medium"}],"cwes":["CWE-209"],"updated_at":"2026-11-02T14:40:08.003212Z"}],"nvd":[{"id":"CVE-2026-44226","description":"pyLoad is a free and open-source download manager written in Python. Prior to 0.5.0b3.dev100, pyload-ng WebUI returns full Python traceback details to clients on unhandled exceptions. Because /web/\u003cpath:filename\u003e is reachable without authentication and renders attacker-controlled template names, an unauthenticated user can reliably trigger a server exception (for example by requesting a non-existent template) and receive internal stack traces in the HTTP response. This vulnerability is fixed in 0.5.0b3.dev100.","status":"published","published":"2026-05-11T18:16:37.807Z","modified":"2026-11-11T20:10:00.273Z","cvss":[{"version":"3.1","score":5.3,"vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:N/A:N","level":"medium","assessor":"secondary"}],"cwes":["CWE-209"],"updated_at":"2026-11-11T21:26:15.366289Z"}],"osv":[{"id":"GHSA-c3gc-9pf2-84gg","title":"PyLoad vulnerable to unauthenticated traceback disclosure via global exception handler in WebUI","description":"### Summary\n`pyload-ng` WebUI returns full Python traceback details to clients on unhandled exceptions.\n\nBecause `/web/\u003cpath:filename\u003e` is reachable without authentication and renders attacker-controlled template names, an unauthenticated user can reliably trigger a server exception (for example by requesting a non-existent template) and receive internal stack traces in the HTTP response.\n\n### Details\nThe issue is caused by the combination of:\n\n1. Unauthenticated template-render route:\n- `src/pyload/webui/app/blueprints/app_blueprint.py:32-36`\n  - `@bp.route(\"/web/\u003cpath:filename\u003e\", endpoint=\"web\")`\n  - `data = render_template(filename)` with user-controlled `filename`\n  - no `@login_required(...)` on this route\n\n2. Global exception handler exposes traceback to response:\n- `src/pyload/webui/app/handlers.py:14-27`\n  - `tb = traceback.format_exc()`\n  - `messages.extend(tb.split('\\n'))`\n  - returned in rendered error page for all exceptions\n\n3. Error page renders all `messages`:\n- `src/pyload/webui/app/themes/modern/templates/base.html:217-219`\n  - loops over `messages` and prints them in response HTML\n\nSo any unhandled exception can disclose internal implementation details (stack frames, source paths, exception metadata) to remote unauthenticated clients. \n\nThis is a core behavior issue in default WebUI error handling\n\n### PoC\n```python\n#!/usr/bin/env python3\nfrom __future__ import annotations\n\nimport re\nimport shutil\nimport tempfile\nimport traceback\nfrom pathlib import Path\n\n\nROOT = Path(__file__).resolve().parent / \"pyload\" / \"src\" / \"pyload\"\n\n\ndef read_text(rel: str) -\u003e str:\n    return (ROOT / rel).read_text(encoding=\"utf-8\")\n\n\ndef route_has_no_login_required(app_blueprint: str) -\u003e bool:\n    m = re.search(\n        r'@bp\\\\.route\\\\(\"/web/\u003cpath:filename\u003e\", endpoint=\"web\"\\\\)\\\\s*'\n        r\"def render\\\\(filename\\\\):(?P\u003cbody\u003e.*?)(?:\\\\n\\\\n@bp\\\\.route|\\\\Z)\",\n        app_blueprint,\n        re.DOTALL,\n    )\n    if not m:\n        return False\n    block_start = max(0, m.start() - 200)\n    block = app_blueprint[block_start:m.end()]\n    return \"@login_required(\" not in block\n\n\ndef main() -\u003e None:\n    workdir = Path(tempfile.mkdtemp(prefix=\"pyload-traceback-infoleak-\"))\n    try:\n        app_blueprint = read_text(\"webui/app/blueprints/app_blueprint.py\")\n        handlers = read_text(\"webui/app/handlers.py\")\n        base_template = read_text(\"webui/app/themes/modern/templates/base.html\")\n\n        unauth_web_route = '/web/\u003cpath:filename\u003e' in app_blueprint and route_has_no_login_required(app_blueprint)\n        user_controlled_template_name = \"render_template(filename)\" in app_blueprint\n        handler_uses_traceback = \"traceback.format_exc()\" in handlers\n        handler_appends_trace = \"messages.extend(tb.split('\\\\n'))\" in handlers\n        global_exception_handler = \"(Exception, handle_exception_error)\" in handlers\n        template_renders_messages = \"{% for message in messages %}\" in base_template and \"{{message}}\" in base_template\n\n        leaked_traceback_keyword = False\n        leaked_exception_type = False\n        try:\n            raise RuntimeError(\"forced-poc-error\")\n        except Exception:\n            tb = traceback.format_exc()\n            messages = [f\"Error 500: forced-poc-error\"]\n            messages.extend(tb.split(\"\\\\n\"))\n            joined = \"\\\\n\".join(messages)\n            leaked_traceback_keyword = \"Traceback (most recent call last)\" in joined\n            leaked_exception_type = \"RuntimeError: forced-poc-error\" in joined\n\n        repro_success = all(\n            [\n                unauth_web_route,\n                user_controlled_template_name,\n                handler_uses_traceback,\n                handler_appends_trace,\n                global_exception_handler,\n                template_renders_messages,\n                leaked_traceback_keyword,\n                leaked_exception_type,\n            ]\n        )\n\n        print(\"unauth_web_route=\", unauth_web_route)\n        print(\"user_controlled_template_name=\", user_controlled_template_name)\n        print(\"handler_uses_traceback=\", handler_uses_traceback)\n        print(\"handler_appends_trace=\", handler_appends_trace)\n        print(\"global_exception_handler=\", global_exception_handler)\n        print(\"template_renders_messages=\", template_renders_messages)\n        print(\"leaked_traceback_keyword=\", leaked_traceback_keyword)\n        print(\"leaked_exception_type=\", leaked_exception_type)\n        print(\"traceback_infoleak_repro_success=\", repro_success)\n    finally:\n        shutil.rmtree(workdir, ignore_errors=True)\n        print(\"cleanup_done=True\")\n\n\nif __name__ == \"__main__\":\n    main()\n```\n\nObserved result:\n```text\nunauth_web_route= True\nuser_controlled_template_name= True\nhandler_uses_traceback= True\nhandler_appends_trace= True\nglobal_exception_handler= True\ntemplate_renders_messages= True\nleaked_traceback_keyword= True\nleaked_exception_type= True\ntraceback_infoleak_repro_success= True\ncleanup_done=True\n```\n\n### Impact\n- Vulnerability type: Information disclosure (stack trace / internal path leakage).\n- Attack surface: unauthenticated WebUI request path.\n- Exposes internal error details that help attackers map application internals and improve exploit reliability for follow-on attacks.","status":"published","published":"2026-05-06T17:54:20Z","modified":"2026-07-13T16:43:06.837973926Z","cvss":[{"version":"3.1","score":5.3,"vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:N/A:N","level":"medium"}],"cwes":["CWE-209"],"updated_at":"2026-11-02T15:46:44.571161Z"},{"id":"PYSEC-2026-2993","title":"PyLoad vulnerable to unauthenticated traceback disclosure via global exception handler in WebUI","description":"### Summary\n`pyload-ng` WebUI returns full Python traceback details to clients on unhandled exceptions.\n\nBecause `/web/\u003cpath:filename\u003e` is reachable without authentication and renders attacker-controlled template names, an unauthenticated user can reliably trigger a server exception (for example by requesting a non-existent template) and receive internal stack traces in the HTTP response.\n\n### Details\nThe issue is caused by the combination of:\n\n1. Unauthenticated template-render route:\n- `src/pyload/webui/app/blueprints/app_blueprint.py:32-36`\n  - `@bp.route(\"/web/\u003cpath:filename\u003e\", endpoint=\"web\")`\n  - `data = render_template(filename)` with user-controlled `filename`\n  - no `@login_required(...)` on this route\n\n2. Global exception handler exposes traceback to response:\n- `src/pyload/webui/app/handlers.py:14-27`\n  - `tb = traceback.format_exc()`\n  - `messages.extend(tb.split('\\n'))`\n  - returned in rendered error page for all exceptions\n\n3. Error page renders all `messages`:\n- `src/pyload/webui/app/themes/modern/templates/base.html:217-219`\n  - loops over `messages` and prints them in response HTML\n\nSo any unhandled exception can disclose internal implementation details (stack frames, source paths, exception metadata) to remote unauthenticated clients. \n\nThis is a core behavior issue in default WebUI error handling\n\n### PoC\n```python\n#!/usr/bin/env python3\nfrom __future__ import annotations\n\nimport re\nimport shutil\nimport tempfile\nimport traceback\nfrom pathlib import Path\n\n\nROOT = Path(__file__).resolve().parent / \"pyload\" / \"src\" / \"pyload\"\n\n\ndef read_text(rel: str) -\u003e str:\n    return (ROOT / rel).read_text(encoding=\"utf-8\")\n\n\ndef route_has_no_login_required(app_blueprint: str) -\u003e bool:\n    m = re.search(\n        r'@bp\\\\.route\\\\(\"/web/\u003cpath:filename\u003e\", endpoint=\"web\"\\\\)\\\\s*'\n        r\"def render\\\\(filename\\\\):(?P\u003cbody\u003e.*?)(?:\\\\n\\\\n@bp\\\\.route|\\\\Z)\",\n        app_blueprint,\n        re.DOTALL,\n    )\n    if not m:\n        return False\n    block_start = max(0, m.start() - 200)\n    block = app_blueprint[block_start:m.end()]\n    return \"@login_required(\" not in block\n\n\ndef main() -\u003e None:\n    workdir = Path(tempfile.mkdtemp(prefix=\"pyload-traceback-infoleak-\"))\n    try:\n        app_blueprint = read_text(\"webui/app/blueprints/app_blueprint.py\")\n        handlers = read_text(\"webui/app/handlers.py\")\n        base_template = read_text(\"webui/app/themes/modern/templates/base.html\")\n\n        unauth_web_route = '/web/\u003cpath:filename\u003e' in app_blueprint and route_has_no_login_required(app_blueprint)\n        user_controlled_template_name = \"render_template(filename)\" in app_blueprint\n        handler_uses_traceback = \"traceback.format_exc()\" in handlers\n        handler_appends_trace = \"messages.extend(tb.split('\\\\n'))\" in handlers\n        global_exception_handler = \"(Exception, handle_exception_error)\" in handlers\n        template_renders_messages = \"{% for message in messages %}\" in base_template and \"{{message}}\" in base_template\n\n        leaked_traceback_keyword = False\n        leaked_exception_type = False\n        try:\n            raise RuntimeError(\"forced-poc-error\")\n        except Exception:\n            tb = traceback.format_exc()\n            messages = [f\"Error 500: forced-poc-error\"]\n            messages.extend(tb.split(\"\\\\n\"))\n            joined = \"\\\\n\".join(messages)\n            leaked_traceback_keyword = \"Traceback (most recent call last)\" in joined\n            leaked_exception_type = \"RuntimeError: forced-poc-error\" in joined\n\n        repro_success = all(\n            [\n                unauth_web_route,\n                user_controlled_template_name,\n                handler_uses_traceback,\n                handler_appends_trace,\n                global_exception_handler,\n                template_renders_messages,\n                leaked_traceback_keyword,\n                leaked_exception_type,\n            ]\n        )\n\n        print(\"unauth_web_route=\", unauth_web_route)\n        print(\"user_controlled_template_name=\", user_controlled_template_name)\n        print(\"handler_uses_traceback=\", handler_uses_traceback)\n        print(\"handler_appends_trace=\", handler_appends_trace)\n        print(\"global_exception_handler=\", global_exception_handler)\n        print(\"template_renders_messages=\", template_renders_messages)\n        print(\"leaked_traceback_keyword=\", leaked_traceback_keyword)\n        print(\"leaked_exception_type=\", leaked_exception_type)\n        print(\"traceback_infoleak_repro_success=\", repro_success)\n    finally:\n        shutil.rmtree(workdir, ignore_errors=True)\n        print(\"cleanup_done=True\")\n\n\nif __name__ == \"__main__\":\n    main()\n```\n\nObserved result:\n```text\nunauth_web_route= True\nuser_controlled_template_name= True\nhandler_uses_traceback= True\nhandler_appends_trace= True\nglobal_exception_handler= True\ntemplate_renders_messages= True\nleaked_traceback_keyword= True\nleaked_exception_type= True\ntraceback_infoleak_repro_success= True\ncleanup_done=True\n```\n\n### Impact\n- Vulnerability type: Information disclosure (stack trace / internal path leakage).\n- Attack surface: unauthenticated WebUI request path.\n- Exposes internal error details that help attackers map application internals and improve exploit reliability for follow-on attacks.","status":"published","published":"2026-07-13T15:15:38.893973Z","modified":"2026-07-13T16:32:41.275333282Z","cvss":[{"version":"3.1","score":5.3,"vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:N/A:N","level":"medium"}],"updated_at":"2026-11-02T15:52:56.429374Z"}]}}
