# Joro: Unauthenticated Cross-Origin Plugin Upload Leads to RCE

Joro proxy mode's unauthenticated localhost API with wildcard CORS let any visited page upload a Go plugin and restart, executing code as the operator.

- Payout: Research
- Severity: critical
- Program: Joro
- Found by: stover-BF
- Disclosed: 2026-07-08
- CVE: CVE-2026-53649
- Write-up quality: 8/10
- Original: https://github.com/advisories/GHSA-xqhv-chqm-fhcc
- Library page: https://bugbountylibrary.com/r/ghsa%3AGHSA-xqhv-chqm-fhcc
- Tags: area:os domain:desktop impact:rce kind:research platform:ghsa sev:critical tech:go technique:chained technique:source-review vuln:auth-bypass vuln:cors vuln:file-upload vuln:rce

**Target:** Localhost HTTP API (127.0.0.1:9090) plugin upload and restart endpoints
**Root cause:** Proxy mode applied no auth middleware and set Access-Control-Allow-Origin: * on all responses, wrongly assuming the 127.0.0.1 bind protected it; multipart/form-data is CORS-safelisted so cross-origin POSTs skip preflight. Plugin init() runs on plugin.Open() before symbol lookup.
**Steps:**
1. Operator visits attacker page in Firefox.
2. Page JS fetches `pwn.so` from attacker server (same-origin).
3. JS POSTs `pwn.so` to `http://127.0.0.1:9090/api/v1/plugins/upload` as `multipart/form-data` — no auth, no preflight.
4. JS POSTs to `http://127.0.0.1:9090/api/v1/system/restart`.
5. On restart `plugin.Open("pwn.so")` runs init(), dialing back to attacker listener.
6. Interactive `/bin/bash -i` shell as operator's user.
**How found:** Noticed AuthMiddleware was only applied when listenerMode was true, and that SECURITY.md justified wildcard CORS by the loopback bind — then reasoned that multipart/form-data is CORS-safelisted so cross-origin JS reaches localhost without preflight, and that plugin.Open() runs init() before symbol lookup.
**Exploitation:** Browser-mediated pivot: attacker page uploads an ABI-matched Go plugin built via Joro's own `--build-plugin` against the public v1.1.0 release tarball, then triggers restart; plugin init() opens a reverse shell goroutine to the attacker. One .so works against every operator on that release.
**Escalation:** Chained missing auth + wildcard CORS + CORS-safelisted multipart to bypass preflight, then plugin init() execution on restart to reach RCE from a single page visit.
**Insight:** A loopback bind is not an auth boundary against browsers: any CORS-safelisted content type (multipart/form-data, text/plain, urlencoded) lets cross-origin JS hit localhost APIs without preflight, so check local daemons for wildcard ACAO plus unauthenticated state-changing endpoints.

