# Kopia: RCE via SSH ProxyCommand Injection

Kopia server started with --without-password exposes /api/v1/repo/exists, letting unauthenticated attackers inject -oProxyCommand into SFTP sshArguments for RCE.

- Payout: Research
- Severity: critical
- Program: Kopia
- Found by: berardinellidaniele
- Disclosed: 2026-05-19
- CVE: CVE-2026-45695
- Write-up quality: 7/10
- Original: https://github.com/advisories/GHSA-2q4c-3mrw-63c3
- Library page: https://bugbountylibrary.com/r/ghsa%3AGHSA-2q4c-3mrw-63c3
- Tags: area:cloud domain:infra impact:rce kind:research platform:ghsa sev:critical tech:go technique:source-review vuln:auth-bypass vuln:command-injection vuln:misconfiguration vuln:rce

**Target:** Kopia HTTP server API endpoint /api/v1/repo/exists with SFTP storage backend
**Root cause:** Server run without credentials authorizes all handleUIPossiblyNotConnected endpoints; the SFTP backend splits attacker-supplied sshArguments on spaces and appends tokens to ssh argv, so -oProxyCommand=<cmd> is executed by OpenSSH via $SHELL -c.
**Steps:**
1. Start Kopia server with `--without-password` (no --server-username/--server-password), so getAuthenticator() returns nil and requireUIUser authorizes everything.
2. Send an unauthenticated POST to `/api/v1/repo/exists` with a storage config selecting SFTP backend, `externalSSH: true`, and `sshArguments` containing `-oProxyCommand=<cmd>`.
3. Handler forwards config to blob.NewStorage; sftp_storage.go splits sshArguments on literal spaces and appends to `exec.CommandContext("ssh")` argv.
4. OpenSSH runs `<cmd>` via `$SHELL -c` before any TCP connection, executing as the Kopia process user.
**How found:** Code review of the authz path (server_authz_checks.go) showing nil authenticator authorizes all UI endpoints, combined with reading sftp_storage.go where sshArguments is naively space-split into ssh argv, then recognizing OpenSSH's -oProxyCommand shell execution semantics.
**Exploitation:** Single unauthenticated HTTP request to /api/v1/repo/exists with a crafted SFTP storage config; the ProxyCommand runs before SSH connects, so no reachable host is needed. Command executes as the Kopia server user.
**Insight:** When a server has a 'no auth' mode, every endpoint behind the shared authz helper becomes an unauthenticated sink; look for config fields passed into exec argv (sshArguments, proxy settings) where -o directives like ProxyCommand/LocalCommand give shell execution before any network I/O.

