GHSA-xjvp-7243-rg9h: Wish has SCP Path Traversal that allows arbitrary file read/write
SummaryThe SCP middleware in `charm.land/wish/v2` is vulnerable to path traversal attacks. A malicious SCP client can read arbitrary files from the server, write arbitrary files to the server, and create directories outside the configured root directory by sending crafted filenames containing `../` sequences over the SCP protocol.Affected Versions`charm.land/wish/v2` — all versions through commit `72d67e6` (current `main`)`github.com/charmbracelet/wish` — likely all v1 versions (same code pattern)DetailsRoot CauseThe `fileSystemHandler.prefixed()` method in `scp/filesystem.go:42-48` is intended to confine all file operations to a configured root directory. However, it fails to validate that the resolved path remains within the root:When `path` contains `../` components, `filepath.Clean` resolves them but does not reject them. The subsequent `filepath.Join(h.root, path)` produces a path that escapes the root directory.Attack Vector 1: Arbitrary File Write (scp -t)When receiving files from a client (`scp -t`), filenames are parsed from the SCP protocol wire using regexes that accept arbitrary strings:The captured filename is used directly in `filepath.Join(path, name)` without sanitization (`scp/copy_from_client.go:90,140`), then passed to `fileSystemHandler.Write()` and `fileSystemHandler.Mkdir()`, which call `prefixed()` — allowing the attacker to write files and create directories anywhere on the filesystem.Attack Vector 2: Arbitrary File Read (scp -f)When sending files to a client (`scp -f`), the requested path comes from the SSH command arguments (`scp/scp.go:284`). This path is passed to `handler.Glob()`, `handler.NewFileEntry()`, and `handler.NewDirEntry()`, all of which call `prefixed()` — allowing the attacker to read any file accessible to the server process.Attack Vector 3: File Enumeration via GlobThe `Glob` method passes user input containing glob metacharacters (`*`, `?`, `[`) to `filepath.Glob` after `prefixed()`, enabling enumeration of files outside the root.Proof of ConceptAll three vectors were validated with end-to-end integration tests against a real SSH server using the public `wish` and `scp` APIs.Vulnerable ServerAny server using `scp.NewFileSystemHandler` with `scp.Middleware` is affected. This is the pattern shown in the official `examples/scp` example:Write Traversal — Write arbitrary files outside /srv/dataAn attacker crafts SCP protocol messages with `../` in the filename. This can be done with a custom SCP client or by sending raw bytes over an SSH channel. The following Go program connects to the vulnerable server and writes a file to `/tmp/pwned`:Or equivalently using standard `scp` with a symlink trick, or by patching the openssh `scp` client to send a crafted filename.Read Traversal — Read arbitrary files outside /srv/dataNo custom tooling needed. Standard `scp` passes the path directly:The server resolves `../../../etc/passwd` through `prefixed()`:`filepath.Clean("../../../etc/passwd")` → `"../../../etc/passwd"`Not prefixed with `/srv/data`, so: `filepath.Join("/srv/data", "../../../etc/passwd")` → `"/etc/passwd"`File contents of `/etc/passwd` are sent to the attacker.Glob Traversal — Enumerate and read files outside /srv/dataValidated Test OutputThese were confirmed with integration tests using `wish.NewServer`, `scp.Middleware`, and `scp.NewFileSystemHandler` against temp directories. The tests created a root directory and a sibling "secret" directory, then verified files were read/written across the boundary:Tests used the real SSH handshake via `golang.org/x/crypto/ssh`, real SCP protocol parsing, and real filesystem operations — confirming the vulnerability is exploitable end-to-end.ImpactAn authenticated SSH user can:**Write arbitrary files** anywhere on the filesystem the server process can write to, leading to remote code execution via cron jobs, SSH `authorized_keys`, shell profiles, or systemd units.**Read arbitrary files** accessible to the server process, including `/etc/shadow`, private keys, database credentials, and application secrets.**Create arbitrary directories** on the filesystem.**Enumerate files** outside the root via glob patterns.If the server uses the default authentication configuration (which accepts all connections — see `wish.go:19`), these attacks are exploitable by unauthenticated remote attackers.RemediationFix `prefixed()` to enforce root containmentSanitize filenames in `copy_from_client.go`SCP filenames should never contain path separators or `..` components:Validate `info.Path` in `GetInfo` or at the middleware entry pointCreditEvan MORVAN (evnsh) — me@evan.sh (Research)
Claude Haiku (formatting the report)
CVSS v3.1: 9.6