AsyncSSH `AuthorizedKeysFile %u` path traversal allows attacker-selected authorized keys to authenticate a traversal username
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
- The harness creates
sshd_configwithAuthorizedKeysFile authorized_keys/%u, writes the attacker's public key to a file outsideauthorized_keys/, starts a real AsyncSSH server, and attempts two SSH logins. - Expected result: the normal username
victimfails, 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
2Patches
12af2382cce94Catch unsafe user substitutions in config
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
2News mentions
0No linked articles in our index yet.