Aug 26, 2026·6 min read·20 visits
An incomplete path-traversal fix in AsyncSSH allows unauthenticated remote attackers to escape AuthorizedKeysFile directories using tilde expansion and environment variable manipulation, enabling potential authentication bypass.
An incomplete input sanitization fix in AsyncSSH version 2.23.0 allows unauthenticated remote attackers to bypass directory restriction controls and perform path-traversal attacks. When the system is configured to perform username token substitution inside its AuthorizedKeysFile directive, attackers can manipulate downstream path resolution mechanisms via tilde expansion and environment variable references. This flaw permits authentication bypasses by forcing the server to read public keys from unauthorized file locations outside the restricted environment.
AsyncSSH is an asynchronous client and server implementation of the SSHv2 protocol, developed on top of Python's asyncio framework. The framework is commonly utilized in custom SSH services, automation tooling, and cloud controller nodes. Within this architecture, the server configurations handle dynamic client-supplied strings, including username data, which are evaluated during the public-key authentication phase.
When configured with the AuthorizedKeysFile directive containing the %u token, the library performs string substitution using the client-supplied username. In version 2.23.0, developers implemented a sanitization mechanism to prevent directory traversal exploits, originally discovered under CVE-2026-45309. This block-list validation checked for directory separators and parent directory indicators to ensure username expansion remained within a designated keys directory.
However, this sanitization check was functionally incomplete and failed to address alternative path-traversal avenues. By supplying usernames crafted with tilde user indicators or environment variable tokens, remote attackers can bypass the initial sanitization boundary. The server subsequently resolves these strings into paths outside the restricted base directory, leading to unauthorized public key evaluations.
The underlying vulnerability arises from the sequencing of input validation relative to file path resolution. In AsyncSSH v2.23.0, the validation in SSHServerConfig._set_tokens checked the incoming self._user string to ensure it was not equal to .. and did not contain the / or \ characters. This logic successfully blocked basic relative traversal paths like ../../ but did not account for downstream path-resolution operations.
During subsequent path resolution, Python's native pathlib.Path library is invoked, which calls the expanduser() method. In a standard operating environment, Path('~root/.ssh/authorized_keys').expanduser() evaluates the leading tilde token and expands to the home directory of the root user. Because the string ~root contains no slashes and is not equal to .., it completely bypasses the v2.23.0 sanitization block and escapes the intended authorized-keys folder.
Furthermore, the application performs recursively evaluated environment variable expansion via _expand_val. An attacker can supply a username consisting of an environment variable lookup, such as ${HOME}. Because this input is evaluated and expanded after the character-level sanitization step, the resulting absolute folder path contains slashes that are never subjected to security validation, enabling directory escapes.
The vulnerable code path in asyncssh/config.py at version 2.23.0 relied on a simple conditional block that only analyzed raw string matching. This is illustrated in the snippet below:
# Vulnerable validation logic in v2.23.0
if self._user == '..' or '/' in self._user or '\\' in self._user:
raise IllegalUserName('Unsafe username substitution')To correct this deficiency, version 2.23.1 transitioned to an explicit regular expression pattern named _unsafe_user_pattern to evaluate incoming usernames. The updated validation logic verifies multiple expansion types, as shown below:
# Patched validation logic in v2.23.1
_unsafe_user_pattern = re.compile(r'^\.\.$|^~|^[A-Za-z]:|[\\/]|\$\{.*?\}')
def _set_tokens(self) -> None:
"""Set the tokens available for percent expansion"""
if _unsafe_user_pattern.search(self._user):
raise IllegalUserName('Unsafe username substitution')
self._tokens.update({'u': self._user})The introduction of _unsafe_user_pattern addresses all four bypass vectors. First, ^~ blocks any leading tilde to prevent home directory expansion. Second, ^[A-Za-z]: blocks Windows drive letter targets. Third, \$\{.*?\} matches environment variable syntax to prevent delayed evaluation exploits. Finally, forward and backward slashes continue to be intercepted.
Exploitation of this vulnerability requires that the target SSH server is configured with dynamic username path mapping. This occurs when the AuthorizedKeysFile directive utilizes the %u substitution token. The target must be running version 2.23.0 of AsyncSSH, where the directory-traversal block-list is active but incomplete.
During authentication, the unauthenticated client initiates a connection and submits a crafted username string, such as ~root. The server receives this connection request and routes the username through the substitution engine. The target file path template, e.g., /var/lib/ssh/keys/authorized_keys_%u, is updated to /var/lib/ssh/keys/authorized_keys_~root.
When the system attempts to open this path, Python's path-resolution libraries process the path using user-expansion procedures. The operating system resolves the trailing ~root suffix to the actual root directory path /root. The server evaluates the keys under this root directory instead of the restricted public-key repository, matching the attacker's key against keys stored in alternative folders, which can lead to an authentication bypass.
The impact of CVE-2026-54590 is significant for environments leveraging multi-tenant or containerized SSH environments with dynamic configuration lookups. Successful exploitation allows an attacker to manipulate file path resolution. This capability permits attackers to redirect the key lookup process to file paths containing public keys under their control or to read globally readable files.
The CVSS v3.1 base score is calculated at 5.9 (Medium severity). The Attack Complexity is classified as High (AC:H) because exploitation depends on the server utilizing %u within the AuthorizedKeysFile configuration, and the target operating system must support the corresponding user-expansion or environment variable lookup.
The Integrity impact of this vulnerability is High (I:H) because bypassing authorized-key limitations directly compromises the authentication boundary of the SSH server. However, there is no direct impact on Confidentiality (C:N) or Availability (A:N), as the traversal vector is limited to altering file resolution targets rather than achieving direct arbitrary file extraction or service denial.
The recommended remediation is upgrading the asyncssh package to version 2.23.1 or newer. The update fully patches the path validation logic inside config.py by incorporating regex-based input auditing that neutralizes tilde expansion, environment variables, and Windows-specific drive designations before variable expansion.
If patching the library is not immediately possible, administrators can mitigate the risk by modifying server configuration files. Disabling the use of the %u username substitution token inside the AuthorizedKeysFile directive will prevent the vulnerability. Implementing static, absolute directories for public keys prevents users from influencing the filesystem resolution paths.
Furthermore, enforcing the principle of least privilege on the runtime environment of the application is a key defense-in-depth practice. Running the asyncssh server daemon inside a sandboxed, unprivileged container with a read-only filesystem limits the availability of target system accounts and directory structures, reducing the likelihood of a successful traversal escape.
CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:N/I:H/A:N| Product | Affected Versions | Fixed Version |
|---|---|---|
asyncssh ronf | <= 2.23.0 | 2.23.1 |
| Attribute | Detail |
|---|---|
| CWE ID | CWE-22 |
| Attack Vector | Network |
| CVSS | 5.9 (Medium) |
| EPSS Score | 0.00393 |
| Impact | Integrity (High) |
| Exploit Status | Proof of Concept |
| KEV Status | Not Listed |
The software uses external input to construct a pathname that is intended to identify a file or directory that is located underneath a restricted parent directory, but the software does not properly neutralize special elements within the pathname that can cause the pathname to resolve to a location that is outside of the restricted directory.
CVE-2026-109619 (PHP2Shell) is a critical Use-After-Free vulnerability in the core PHP engine caused by improper global state cleanup during out-of-memory exception handling across persistent worker requests.
In Python virtualenv prior to version 21.14.9, activation script generators and configuration writers use Path.exists() before writing files. Because Path.exists() resolves symbolic links, it returns False for dangling symlinks. Consequently, virtualenv skips unlinking pre-planted symbolic links or hard links, writing data directly to target locations outside the intended virtual environment directory.
A remote code execution vulnerability exists in Microsoft .NET due to untrusted data deserialization within the PresentationCore component. The flaw stems from implicit auto-conversion flags during clipboard data retrieval operations.
An out-of-bounds write vulnerability in Microsoft .NET Framework, .NET runtime, and Visual Studio allows unauthenticated remote attackers to execute arbitrary code via crafted pixel-shader bytecode or auto-converted clipboard payloads.
ASP.NET Core contains a thread deadlock vulnerability (CWE-833) that allows an unauthenticated remote attacker to cause a denial of service over the network by exhausting server thread pool resources.
CVE-2026-70035 is a high-severity Remote Code Execution (RCE) vulnerability in Microsoft .NET Framework, .NET runtime, and Visual Studio caused by improper handling during the deserialization of untrusted data (CWE-502). An unauthenticated attacker can execute arbitrary code within the context of the affected process by delivering a crafted serialized payload.