VYPR
Medium severity6.9GHSA Advisory· Published May 27, 2026· Updated May 27, 2026

AsyncSSH `AuthorizedKeysFile %u` path traversal allows attacker-selected authorized keys to authenticate a traversal username

CVE-2026-45309

Description

Summary

AsyncSSH 2.22.0 expands the OpenSSH-compatible AuthorizedKeysFile %u token with the raw SSH username during pre-authentication server config reload. A server configured with a documented per-user key pattern such as AuthorizedKeysFile authorized_keys/%u can be made to read an authorized-keys file outside the intended directory when the SSH username contains path traversal segments. If the attacker can place or reference a readable authorized-keys-format file containing their public key, the attacker can authenticate over SSH as the traversal username.

Affected

Product - Package: asyncssh - Ecosystem: pip - Affected versions: confirmed on 2.22.0; exact lower bound not finalized - Tested version: 2.22.0 - Audit commit/tag: tag v2.22.0, commit af5a81e669633d83d535163f93b6bf3f957c9238 - PyPI sdist SHA256: c3ce72b01be4f97b40e62844dd384227e5ff5a401a3793007c42f86a5c8eb537

Vulnerability

Details - CWE: CWE-22: Improper Limitation of a Pathname to a Restricted Directory - Component: AsyncSSH server config reload and public-key authentication (asyncssh/config.py, asyncssh/connection.py, asyncssh/auth_keys.py, asyncssh/misc.py) - Root cause: %u in AuthorizedKeysFile is expanded from the remote username without rejecting path separators or .. segments, and the resulting path is opened without constraining it to the intended authorized-keys directory. - Security boundary violated: the configured authorized-keys directory and public-key authentication trust boundary. - Direct impact: public-key authentication succeeds using an attacker-selected authorized-keys file outside the intended directory. - Chain impact, if any: none claimed; direct authentication impact is primary.

Attack

Preconditions - The AsyncSSH server uses a config or equivalent pattern where AuthorizedKeysFile contains %u, for example AuthorizedKeysFile authorized_keys/%u. - Public-key authentication is enabled. - The attacker can place or reference a readable authorized-keys-format file outside the intended directory, such as a file in a world-writable or application-writable location. - The application does not separately reject usernames containing /, \, or .. before AsyncSSH uses the username for key-file selection.

Reproduction

The run-scoped evidence contains a safe localhost proof:

1. Start the proof harness saved at harness_app.py

2. Run exploit_proof.py through run_proof.sh

  1. The harness creates sshd_config with AuthorizedKeysFile authorized_keys/%u, writes the attacker's public key to a file outside authorized_keys/, starts a real AsyncSSH server, and attempts two SSH logins.
  2. Expected result: the normal username victim fails, while the traversal username authenticates with the same attacker key.

Observed proof output:

[CONTROL] username=victim success=False
[ATTACK] username=../../../asyncssh-proof-exploit-proof-8b2bd23daeeb.pub success=True
[ATTACK] output=AUTH_BYPASS_SUCCESS username=../../../asyncssh-proof-exploit-proof-8b2bd23daeeb.pub
PASS: traversal username authenticated with attacker-controlled authorized_keys file

AI Insight

LLM-synthesized narrative grounded in this CVE's description and references.

AsyncSSH 2.22.0 path traversal in AuthorizedKeysFile %u expansion allows attacker to authenticate using arbitrary authorized-keys file outside intended directory.

Vulnerability

AsyncSSH 2.22.0 contains a path traversal vulnerability in the expansion of the %u token in the AuthorizedKeysFile configuration option. The bug resides in asyncssh/config.py, asyncssh/connection.py, asyncssh/auth_keys.py, and asyncssh/misc.py. When a server is configured with a pattern such as AuthorizedKeysFile authorized_keys/%u, the %u token is replaced with the raw SSH username without sanitizing path separators or .. segments. This allows an attacker to specify a username containing path traversal sequences, causing the server to read an authorized-keys file outside the intended directory. Public-key authentication must be enabled for exploitation. The vulnerability is confirmed on version 2.22.0; the exact lower bound of affected versions is not yet finalized [1][2].

Exploitation

An attacker must be able to place or reference a readable authorized-keys-format file outside the intended directory (e.g., in a world-writable location). The attacker then sends an SSH username containing path traversal sequences, such as ../path/to/attacker_authorized_keys, during the authentication handshake. The AsyncSSH server expands the %u token with this username, constructs the path, and reads the file as the authorized keys for that username. If the file contains the attacker's public key, the attacker can successfully authenticate over SSH as the traversal username [1][2].

Impact

Successful exploitation allows an attacker to authenticate via SSH public-key authentication using an attacker-controlled authorized-keys file. This bypasses the intended directory restriction for authorized keys, potentially granting unauthorized access to the server. The impact is authentication bypass, which could lead to further compromise depending on the server's configuration and the privileges of the authenticated user [1][2].

Mitigation

As of the publication date (2026-05-27), no official fix has been released for AsyncSSH. The advisory notes that the exact lower bound of affected versions is not finalized. Workarounds include avoiding the use of %u in the AuthorizedKeysFile configuration, or ensuring that the resulting path is constrained to the intended directory. If public-key authentication is not required, it can be disabled. Users should monitor the AsyncSSH repository for a patched version [1][2].

AI Insight generated on May 27, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.

Affected products

2

Patches

1
2af2382cce94

Catch unsafe user substitutions in config

https://github.com/ronf/asyncsshRon FrederickMay 9, 2026Fixed in 2.23.0via ghsa-release-walk
2 files changed · +12 2
  • asyncssh/config.py+4 1 modified
    @@ -34,7 +34,7 @@
     
     from .constants import DEFAULT_PORT
     from .logging import logger
    -from .misc import DefTuple, FilePath, ip_address
    +from .misc import DefTuple, FilePath, IllegalUserName, ip_address
     from .pattern import HostPatternList, WildcardPatternList
     
     try:
    @@ -712,6 +712,9 @@ def _match_val(self, match: str) -> object:
         def _set_tokens(self) -> None:
             """Set the tokens available for percent expansion"""
     
    +        if self._user == '..' or '/' in self._user or '\\' in self._user:
    +            raise IllegalUserName('Unsafe username substitution')
    +
             self._tokens.update({'u': self._user})
     
         _handlers = {option.lower(): (option, handler) for option, handler in (
    
  • tests/test_config.py+8 1 modified
    @@ -1,4 +1,4 @@
    -# Copyright (c) 2020-2024 by Ron Frederick <ronf@timeheart.net> and others.
    +# Copyright (c) 2020-2026 by Ron Frederick <ronf@timeheart.net> and others.
     #
     # This program and the accompanying materials are made available under
     # the terms of the Eclipse Public License v2.0 which accompanies this
    @@ -598,6 +598,13 @@ def test_match_address(self):
             config = self._parse_config('Match address 127.0.0.0/8\nPermitTTY no')
             self.assertEqual(config.get('PermitTTY'), False)
     
    +    def test_illegal_user(self):
    +        """Test update of match options"""
    +
    +        for user in ('..', '/xxx', '\\xxx'):
    +            with self.assertRaises(asyncssh.IllegalUserName):
    +                self._parse_config('AuthorizedKeysFile %u', user=user)
    +
         def test_reload(self):
             """Test update of match options"""
     
    

Vulnerability mechanics

Root cause

"Missing input validation on the SSH username when expanding the `%u` token in `AuthorizedKeysFile` allows path traversal sequences to escape the intended authorized-keys directory."

Attack vector

An attacker sends an SSH connection request with a username containing path traversal sequences such as `../` or absolute path components like `/xxx`. When the AsyncSSH server is configured with `AuthorizedKeysFile authorized_keys/%u`, the `%u` token is expanded to the attacker-supplied username, producing a path like `authorized_keys/../../../attacker_controlled_file.pub`. The server reads this file as the authorized-keys list, and if the attacker's public key is present there, public-key authentication succeeds [CWE-22][ref_id=1]. The attacker must be able to place or reference a readable file in authorized-keys format at the traversal target location [ref_id=2].

Affected code

The vulnerability resides in `asyncssh/config.py` where the `_set_tokens` method expands the `%u` token using the raw SSH username without sanitization. The resulting path is then used by `asyncssh/auth_keys.py` to locate the authorized-keys file during pre-authentication server config reload [ref_id=1][ref_id=2].

What the fix does

The patch in `asyncssh/config.py` adds a check in `_set_tokens` that raises `IllegalUserName` if the username equals `'..'` or contains `'/'` or `'\\'` [patch_id=2784775]. This prevents path traversal before the `%u` token is expanded into the authorized-keys file path. A corresponding unit test in `tests/test_config.py` verifies that usernames like `..`, `/xxx`, and `\\xxx` are rejected [patch_id=2784775].

Preconditions

  • configAsyncSSH server configured with AuthorizedKeysFile containing %u (e.g., authorized_keys/%u)
  • configPublic-key authentication is enabled on the server
  • inputAttacker can place or reference a readable authorized-keys-format file outside the intended directory
  • inputApplication does not separately reject usernames containing /, \, or .. before AsyncSSH processes them

Reproduction

1. Start the proof harness saved at [harness_app.py](https://github.com/user-attachments/files/27232526/harness_app.py). 2. Run [exploit_proof.py](https://github.com/user-attachments/files/27232538/exploit_proof.py) through [run_proof.sh](https://github.com/user-attachments/files/27232545/run_proof.sh). 3. The harness creates `sshd_config` with `AuthorizedKeysFile authorized_keys/%u`, writes the attacker's public key to a file outside `authorized_keys/`, starts a real AsyncSSH server, and attempts two SSH logins. 4. Expected result: the normal username `victim` fails, while the traversal username authenticates with the same attacker key [ref_id=1][ref_id=2].

Generated on May 27, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.

References

2

News mentions

0

No linked articles in our index yet.