Feb 28, 2026·7 min read·53 visits
OpenSSH clients < 10.1 allow control characters in usernames. Attackers can inject newlines and shell syntax errors into a username (e.g., via a malicious git URL). When processed by `ProxyCommand`, this bypasses shell validation and executes arbitrary code on the victim's machine.
A critical input validation vulnerability exists in the OpenSSH client (versions prior to 10.1) regarding the handling of usernames. Specifically, the software fails to properly filter control characters, such as newlines, from user-supplied identity strings. When these usernames are expanded in configuration directives like 'ProxyCommand', the injected control characters can alter the structure of the command being executed by the underlying shell. This allows an attacker to inject arbitrary shell commands, leading to Remote Code Execution (RCE) on the client machine, provided specific shell behaviors and configuration conditions are met.
CVE-2025-61984 represents a subtle but significant flaw in how the OpenSSH client validates user identity inputs before passing them to the operating system's shell. The core issue resides in the ssh client's command-line argument parsing and configuration expansion logic. When a user initiates a connection, or when an automated tool (such as Git) invokes SSH, the username is often passed as a parameter. OpenSSH creates a command string based on the ssh_config file, expanding tokens like %r (remote username) into the final command line.
Prior to version 10.1, the OpenSSH client enforced a deny-list for certain dangerous shell metacharacters but failed to account for ASCII control characters, specifically the newline character (\n or 0x0A). This oversight turns the username field into a vector for argument injection. If an attacker can manipulate the username—for example, by forcing a user to connect to a specific URL or cloning a malicious repository—they can introduce multi-line payloads into the command buffer.
While the CVSS score is technically Low (3.6) due to the complexity of exploitation (requiring specific ProxyCommand configurations and victim interaction), the functional impact is critical. Successful exploitation results in arbitrary code execution in the context of the user running the SSH client. This places developer workstations and CI/CD pipelines at particular risk, as they frequently process untrusted URLs via Git submodules.
The vulnerability stems from the valid_ruser function within ssh.c. This function is responsible for sanitizing usernames to ensure they are safe for use in shell commands. Historically, this validation logic focused on preventing standard shell injection attacks by blocking characters such as backticks, semicolons, and ampersands. However, the logic explicitly allowed the full range of ASCII control characters (0x00–0x1F), assuming they were benign or would be handled correctly by downstream components.
When ssh_config utilizes the ProxyCommand directive, OpenSSH typically constructs a command string that looks like exec <proxy_command> <flags> %r@%h. The use of exec is intended to replace the shell process with the proxy process, ensuring clean signal handling and resource management. If a username contains a newline, the single exec command is split into two separate lines of shell code.
Crucially, the exploitation relies on the error-handling behavior of specific shells, particularly Bash. If the first line of the injected command contains a syntax error (which the attacker intentionally induces), certain non-interactive shell invocations will discard the failed line and proceed to execute the subsequent lines. This behavior, sometimes referred to as 'fail-open' on syntax errors in specific execution contexts, allows the injected payload on the second line to run despite the initial command failure.
The remediation for CVE-2025-61984 is concise, highlighting the exact nature of the oversight. The fix introduces a check using iscntrl() to reject any username containing control characters. Below is the comparison of the vulnerable and patched code in ssh.c.
Vulnerable Code (Prior to 10.1):
The original loop iterated through the username string s, checking only for a hardcoded set of dangerous characters. It permitted everything else, including \n.
/* ssh.c - valid_ruser function */
for (i = 0; s[i] != 0; i++) {
// Only checks for specific shell metacharacters
if (strchr("'`\";&<>|(){}", s[i]) != NULL)
return 0;
}
return 1;Patched Code (Version 10.1):
The patch adds an immediate check for control characters at the beginning of the loop. If iscntrl returns true for any character (casted to u_char to handle signedness correctly), the username is rejected immediately.
/* ssh.c - valid_ruser function */
for (i = 0; s[i] != 0; i++) {
// FIX: Reject control characters (0x00-0x1F, 0x7F)
if (iscntrl((u_char)s[i]))
return 0;
if (strchr("'`\";&<>|(){}", s[i]) != NULL)
return 0;
}
return 1;This simple addition effectively neutralizes the attack vector by ensuring that the username cannot break out of the single-line command structure expected by the ProxyCommand expansion.
To exploit this vulnerability, an attacker must construct a payload that achieves two goals: injecting a newline to break the command structure, and neutralizing the prefix of the command to ensure execution flow continues to the payload.
The Payload Structure
A standard ProxyCommand expansion results in: exec proxy_binary %r@%h. If the username (%r) is injected with $[+]\npayload, the shell sees:
exec proxy_binary $[+]payload@hostThe sequence $[+] is a deliberate arithmetic syntax error in Bash. In the context of ssh executing a command via $SHELL -c, this syntax error causes the first line to fail. However, because it is a syntax error rather than a command-not-found error, Bash (in this specific execution mode) may advance to the next line rather than exiting immediately.
Attack Vector: Malicious Git Submodules
The most viable delivery mechanism is a malicious Git repository. An attacker creates a .gitmodules file defining a submodule with the crafted username in the URL.
[submodule "exploit"]
path = exploit
url = "ssh://$[+]\ncalc.exe\n@attacker.com/repo"When a victim runs git clone --recursive <malicious-repo>, Git invokes SSH to clone the submodule. OpenSSH parses the URL, extracts the malicious username, and if the victim's SSH config uses ProxyCommand with %r, the payload calc.exe is executed on the victim's machine.
The impact of CVE-2025-61984 is context-dependent but potentially severe. While the default configuration of OpenSSH does not typically use ProxyCommand, it is a very common configuration for users accessing corporate networks via jump hosts or using tools like Teleport (tsh).
Confidentiality and Integrity: An attacker who successfully exploits this can execute arbitrary commands with the privileges of the local user. This allows for the exfiltration of SSH keys, signing certificates, and local files. It also permits the modification of source code or the installation of persistence mechanisms (backdoors).
Availability: While denial of service is possible (e.g., rm -rf ~), it is rarely the primary goal of such an attack. The primary risk is the silent compromise of developer environments, which serves as a pivot point for supply chain attacks.
Affected Shells: The vulnerability specifically impacts users whose default shell allows the syntax-error bypass (e.g., Bash, Fish, Csh). Users running Zsh are generally immune because Zsh enforces stricter parsing and exits immediately upon encountering the syntax error in the first line.
The primary and most effective mitigation is to upgrade the OpenSSH client. This vulnerability is completely resolved in OpenSSH 10.1. Users on Linux distributions should apply vendor-supplied patches immediately via their package managers (e.g., apt upgrade openssh-client, dnf update openssh-clients).
Configuration Workaround
For systems that cannot be immediately updated, the vulnerability can be mitigated by modifying the ~/.ssh/config or /etc/ssh/ssh_config files. The goal is to ensure that the expanded username is enclosed in quotes, preventing the shell from interpreting the newline as a command separator.
Change directives using %r from:
ProxyCommand /usr/bin/nc -X connect -x proxy:8080 %h %p %r
To:
ProxyCommand /usr/bin/nc -X connect -x proxy:8080 %h %p '%r'
Defensive Git Configuration
To prevent the specific vector of malicious Git submodules, developers can restrict which protocols Git is allowed to use. Disabling SSH for submodules or restricting it to known users can reduce the attack surface:
git config --global protocol.ssh.allow user
CVSS:3.1/AV:L/AC:H/PR:L/UI:N/S:U/C:L/I:L/A:N| Product | Affected Versions | Fixed Version |
|---|---|---|
OpenSSH OpenBSD | < 10.1 | 10.1 |
| Attribute | Detail |
|---|---|
| CWE ID | CWE-159 |
| Attack Vector | Local (User Interaction) |
| CVSS Score | 3.6 (Low) |
| CVSS Vector | CVSS:3.1/AV:L/AC:H/PR:L/UI:N/S:U/C:L/I:L/A:N |
| EPSS Score | 0.00008 |
| Exploit Status | PoC Available |
Improper Handling of Invalid Use of Special Elements