GHSA-2WXC-X7RJ-HG8F

Vulnerability from github – Published: 2026-08-26 15:34 – Updated: 2026-08-26 15:34
VLAI
Summary
asyncssh has SCP Path Traversal to Arbitrary File Write
Details
Product asyncssh (all versions through 2.23.0)
Related CVE-2019-6111 (same class in OpenSSH)
Fix AsyncSSH 2.23.1

A 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.


Steps to exploit:

Step 1 - Normal usage: Application calls await asyncssh.scp((conn, 'file'), '/home/user/downloads/'). This is the standard, documented API.

Step 2 - SCP protocol: asyncssh opens an SSH exec channel, runs scp -f file. The server controls the filename field:

C0644 100 ../pwned.txt\n       (simple traversal)

D0755 0 ..\n                   (traverse up, repeat as needed)
C0644 47 .bashrc\n             (write payload)
E\n

Step 3 - _parse_cd_args (scp.py:134-142) returns the filename verbatim:

def _parse_cd_args(args: bytes) -> Tuple[int, int, bytes]:
    permissions, size, name = args.split(None, 2)
    return int(permissions, 8), int(size), name  # no sanitization

The returned name is not passed through basename() and is not checked for .. or / components.

Step 4 - _recv_files (scp.py:706-713) joins the unsanitized name:

new_dstpath = posixpath.join(dstpath, name)

With dstpath=b'/home/user/downloads/subdir' and name=b'../pwned.txt', this resolves to /home/user/downloads/pwned.txt, outside the target.

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.

Step 6 - RCE chains:

Target Execution trigger Reliability
~/.bashrc Next terminal open High
~/.profile Next login High
~/.ssh/rc Next SSH connection (requires sshd) High
~/.ssh/authorized_keys Attacker logs in with command= Medium

Reproduction:

Link to reproduction script: path_traversal_poc.zip

docker build -t asyncssh-scp-traversal -f Dockerfile .
docker run --rm asyncssh-scp-traversal

The attached poc_scp_traversal.py starts a malicious SSH server in-process using asyncssh's own API, then downloads from it via asyncssh.scp().

Expected Output:

image

Show details on source website

{
  "affected": [
    {
      "database_specific": {
        "last_known_affected_version_range": "\u003c= 2.23.0"
      },
      "package": {
        "ecosystem": "PyPI",
        "name": "asyncssh"
      },
      "ranges": [
        {
          "events": [
            {
              "introduced": "0"
            },
            {
              "fixed": "2.23.1"
            }
          ],
          "type": "ECOSYSTEM"
        }
      ]
    }
  ],
  "aliases": [
    "CVE-2026-54591"
  ],
  "database_specific": {
    "cwe_ids": [
      "CWE-22"
    ],
    "github_reviewed": true,
    "github_reviewed_at": "2026-08-26T15:34:22Z",
    "nvd_published_at": "2026-07-08T21:16:49Z",
    "severity": "HIGH"
  },
  "details": "| | |\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\u0027s 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, \u0027file\u0027), \u0027/home/user/downloads/\u0027)`. 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) -\u003e 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\u0027/home/user/downloads/subdir\u0027` and `name=b\u0027../pwned.txt\u0027`, 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, \u0027wb\u0027)` 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\u0027s own API, then downloads from it via `asyncssh.scp()`.\n\n*Expected Output:*\n\n\u003cimg width=\"1400\" height=\"815\" alt=\"image\" src=\"https://github.com/user-attachments/assets/496745e4-d11d-4ed8-bddd-d15dd13d1751\" /\u003e",
  "id": "GHSA-2wxc-x7rj-hg8f",
  "modified": "2026-08-26T15:34:22Z",
  "published": "2026-08-26T15:34:22Z",
  "references": [
    {
      "type": "WEB",
      "url": "https://github.com/ronf/asyncssh/security/advisories/GHSA-2wxc-x7rj-hg8f"
    },
    {
      "type": "ADVISORY",
      "url": "https://nvd.nist.gov/vuln/detail/CVE-2026-54591"
    },
    {
      "type": "WEB",
      "url": "https://github.com/ronf/asyncssh/commit/d730803b8e4e94c20c7580d90f94d1e05f9f58de"
    },
    {
      "type": "PACKAGE",
      "url": "https://github.com/ronf/asyncssh"
    },
    {
      "type": "WEB",
      "url": "https://github.com/ronf/asyncssh/releases/tag/v2.23.1"
    }
  ],
  "schema_version": "1.4.0",
  "severity": [
    {
      "score": "CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:N/I:H/A:H",
      "type": "CVSS_V3"
    }
  ],
  "summary": "asyncssh has SCP Path Traversal to Arbitrary File Write"
}



Log in or create an account to share your comment.




Tags
Taxonomy of the tags.


Loading…

Loading…

Loading…

Forecast uses a logistic model when the trend is rising, or an exponential decay model when the trend is falling. Fitted via linearized least squares.

Sightings

Author Source Type Date Other

Nomenclature

  • Seen: The vulnerability was mentioned, discussed, or observed by the user.
  • Confirmed: The vulnerability has been validated from an analyst's perspective.
  • Published Proof of Concept: A public proof of concept is available for this vulnerability.
  • Exploited: The vulnerability was observed as exploited by the user who reported the sighting.
  • Patched: The vulnerability was observed as successfully patched by the user who reported the sighting.
  • Not exploited: The vulnerability was not observed as exploited by the user who reported the sighting.
  • Not confirmed: The user expressed doubt about the validity of the vulnerability.
  • Not patched: The vulnerability was not observed as successfully patched by the user who reported the sighting.

Loading…

Detection rules are retrieved from Rulezet.

Loading…

Loading…

Loading…