The 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.
charm.land/wish/v2 — all versions through commit 72d67e6 (current main)github.com/charmbracelet/wish — likely all v1 versions (same code pattern)The 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:
func (h *fileSystemHandler) prefixed(path string) string {
path = filepath.Clean(path)
if strings.HasPrefix(path, h.root) {
return path
}
return filepath.Join(h.root, path)
}
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.
When receiving files from a client (scp -t), filenames are parsed from the SCP protocol wire using regexes that accept arbitrary strings:
reNewFile = regexp.MustCompile(`^C(\d{4}) (\d+) (.*)$`)
reNewFolder = regexp.MustCompile(`^D(\d{4}) 0 (.*)$`)
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.
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...
2.0.1Exploitability
AV:NAC:LPR:LUI:NScope
S:CImpact
C:HI:HA:N9.6/CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:H/A:N