{"id":"CVE-2026-54591","aliases":["GHSA-2wxc-x7rj-hg8f","PYSEC-2026-3808"],"title":"asyncssh has SCP Path Traversal to Arbitrary File Write","summary":"asyncssh has SCP Path Traversal to Arbitrary File Write","severity":"high","cvss":8.1,"cvssVector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:N/I:H/A:H","vendor":"asyncssh","product":"asyncssh","ecosystem":"pip","affected":["asyncssh < 2.23.1"],"patched":["asyncssh 2.23.1"],"published":"2026-08-26","updated":"2026-09-10","sourceUpdated":"2026-09-10T12:25:50.570417065Z","source":"OSV","sourceUrl":"https://osv.dev/vulnerability/GHSA-2wxc-x7rj-hg8f","references":[{"url":"https://github.com/ronf/asyncssh/security/advisories/GHSA-2wxc-x7rj-hg8f"},{"url":"https://nvd.nist.gov/vuln/detail/CVE-2026-54591"},{"url":"https://github.com/ronf/asyncssh/commit/d730803b8e4e94c20c7580d90f94d1e05f9f58de"},{"url":"https://github.com/ronf/asyncssh"},{"url":"https://github.com/ronf/asyncssh/releases/tag/v2.23.1"},{"url":"https://pypi.org/project/asyncssh"},{"url":"https://github.com/advisories/GHSA-2wxc-x7rj-hg8f"}],"tags":["osv","pip","ghsa"],"epss":0.00492,"epssPercentile":0.41237,"cwe":["CWE-22"],"ingestedAt":"2026-08-26T15:45:52.189Z","slug":"CVE-2026-54591","body":"## Overview\n\n| | |\n|---|---|\n| Product | asyncssh (all versions through 2.23.0) |\n| Related | CVE-2019-6111 (same class in OpenSSH) |\n| Fix | AsyncSSH 2.23.1 |\n\nA malicious SSH server can write arbitrary files on the asyncssh SCP client's filesystem by sending filenames containing `../` traversal sequences. The SCP receive path does not currently sanitize server-provided filenames. By chaining directory traversals via the `D` (directory) action, an attacker can escape any target directory and overwrite `~/.bashrc`, `~/.ssh/rc`, or `~/.ssh/authorized_keys`, achieving code execution. This is the same vulnerability class as CVE-2019-6111. The mitigation applied in OpenSSH does not appear to have been adopted in asyncssh.\n\n---\n\n**Steps to exploit:**\n\n**Step 1 - Normal usage:** Application calls `await asyncssh.scp((conn, 'file'), '/home/user/downloads/')`. This is the standard, documented API.\n\n**Step 2 - SCP protocol:** asyncssh opens an SSH exec channel, runs `scp -f file`. The server controls the filename field:\n\n```\nC0644 100 ../pwned.txt\\n       (simple traversal)\n\nD0755 0 ..\\n                   (traverse up, repeat as needed)\nC0644 47 .bashrc\\n             (write payload)\nE\\n\n```\n\n**Step 3 - `_parse_cd_args`** (`scp.py:134-142`) returns the filename verbatim:\n\n```python\ndef _parse_cd_args(args: bytes) -> Tuple[int, int, bytes]:\n    permissions, size, name = args.split(None, 2)\n    return int(permissions, 8), int(size), name  # no sanitization\n```\n\nThe returned `name` is not passed through `basename()` and is not checked for `..` or `/` components.\n\n**Step 4 - `_recv_files`** (`scp.py:706-713`) joins the unsanitized name:\n\n```python\nnew_dstpath = posixpath.join(dstpath, name)\n```\n\nWith `dstpath=b'/home/user/downloads/subdir'` and `name=b'../pwned.txt'`, this resolves to `/home/user/downloads/pwned.txt`, outside the target.\n\n**Step 5 - File write:** `_recv_file` opens the traversed path via `self._fs.open(dstpath, 'wb')` and writes attacker-controlled content. The resolved path is not checked against the target directory boundary.\n\n**Step 6 - RCE chains:**\n\n| Target | Execution trigger | Reliability |\n|---|---|---|\n| `~/.bashrc` | Next terminal open | High |\n| `~/.profile` | Next login | High |\n| `~/.ssh/rc` | Next SSH connection (requires sshd) | High |\n| `~/.ssh/authorized_keys` | Attacker logs in with `command=` | Medium |\n\n---\n\n**Reproduction:**\n\nLink to reproduction script: [path_traversal_poc.zip](https://github.com/user-attachments/files/28160665/path_traversal_poc.zip)\n\n```bash\ndocker build -t asyncssh-scp-traversal -f Dockerfile .\ndocker run --rm asyncssh-scp-traversal\n```\n\nThe attached `poc_scp_traversal.py` starts a malicious SSH server in-process using asyncssh's own API, then downloads from it via `asyncssh.scp()`.\n\n*Expected Output:*\n\n<img width=\"1400\" height=\"815\" alt=\"image\" src=\"https://github.com/user-attachments/assets/496745e4-d11d-4ed8-bddd-d15dd13d1751\" />\n\n## Affected packages\n\n- `asyncssh < 2.23.1`\n\n## Remediation\n\nUpgrade to a patched release:\n\n- `asyncssh 2.23.1`","depth":"twilight","depthScore":45,"depthScoreParts":{"impact":44.6,"likelihood":0.1,"exploitation":0,"ransomware":0},"changes":[]}