← Back to Feed CACHED · 2026-07-13 09:21:36 · CACHE_KEY tenable:269984
tenable:269984 · CWE-88 · Disclosed 2025-10-06

OpenSSH < 10

ASSESSED — NOISGATE V0.5
Vendor
Reassessed
Verdict:
Do you agree?
01 · The Real Story

A malicious .gitmodules file whispers shell metacharacters into your SSH client's ear

This Tenable plugin bundles CVE-2025-61984 and CVE-2025-61985 — two closely related client-side flaws in the OpenSSH ssh binary, fixed in 10.1 / 10.1p1 (released 6 October 2025). The first allows control characters (including newlines) in usernames sourced from the command line or %r expansion inside ProxyCommand. The second allows a NUL byte inside an ssh:// URI. Both funnel unsanitized bytes into a shell that ProxyCommand invokes, giving an attacker code execution *on the machine running ssh* — not on the remote host. The realistic delivery vector, per finder David Leadbeater, is a poisoned .gitmodules file with a hostile username; a git clone --recurse-submodules or git submodule update then triggers the injection.

Tenable's LOW rating is *correct* and matches upstream. The bug is client-side, requires the victim to interact with attacker-controlled content, and only weaponizes when ProxyCommand is configured with an unquoted %r — the default OpenSSH build does not set ProxyCommand. CVSS 3.6 (AV:L/AC:H/PR:L/UI:N/C:L/I:L/A:N) reflects reality: local attack vector, high complexity, low impact. Nothing here threatens a production SSH server. Do not treat this like a sshd RCE — the plugin title 'Multiple Vulnerabilities' is misleading if you skim it.

"Client-side ProxyCommand injection via poisoned git submodules. Real attack surface is developer workstations, not your SSH server fleet."
02 · The Attack Path

4 steps from start to impact.

STEP 01

Attacker publishes a poisoned repository

Attacker crafts a public git repo containing a .gitmodules entry whose submodule URL uses an ssh:// scheme with a username containing control characters (CVE-2025-61984) or an embedded \0 (CVE-2025-61985). The malicious payload is a shell fragment that will land in argv when ProxyCommand expands %r. Reference PoC: David Leadbeater's disclosure writeup and the openssh-unix-announce thread.
Conditions required:
  • Attacker can host or contribute to a repo the victim will clone
  • Victim's ~/.ssh/config sets ProxyCommand with unquoted %r
Where this breaks in practice:
  • Most enterprise SSH clients do not use ProxyCommand at all
  • Modern secure configs quote %r as '%r' per OpenSSH 8.x hardening guidance
Detection/coverage: No scanner catches the config-side prerequisite; version-only detection (Nessus 269984) will flag every unpatched host regardless of ProxyCommand use.
STEP 02

Victim clones or updates submodules

Developer runs git clone --recurse-submodules <repo> or CI runs git submodule update --init. Git resolves the ssh:// URL and hands it to ssh for the transport, passing the crafted username through argv.
Conditions required:
  • Victim runs git with submodule recursion enabled
  • Git is configured to allow the ssh protocol for submodules (default in git < 2.38 and in many CI runners)
Where this breaks in practice:
  • protocol.ssh.allow=user upstream default since git 2.38 blocks user-controlled submodule URLs from using ssh unless the top-level clone did
  • CI runners that pin GIT_ALLOW_PROTOCOL block this outright
Detection/coverage: EDR file-write telemetry on .gitmodules alone won't flag it; look for ssh process launches with argv containing control bytes.
STEP 03

ssh expands ProxyCommand %r into a shell

OpenSSH's ssh binary substitutes the attacker-controlled username into the ProxyCommand template and forks /bin/sh -c. Control characters or the embedded NUL let the payload break out of the intended token and inject arbitrary commands. Weaponized handler: any interactive shell reachable via sh -c.
Conditions required:
  • ProxyCommand set with %r unquoted
  • Non-interactive shell available
Where this breaks in practice:
  • Default OpenSSH install has no ProxyCommand
  • Bastion-style configs typically use %h/%p and quote %r
Detection/coverage: Auditd rule on execve where parent is ssh and child argv contains \x00 or control chars is effective; add a Sigma-style rule.
STEP 04

Code executes as the developer / CI runner user

The injected commands run with the privileges of whoever invoked git — usually a developer laptop user or a CI service account. Persistence would require a follow-on step (cron, systemd user unit, SSH key drop). No privilege escalation is granted by the bug itself.
Conditions required:
  • Injected payload avoids interactive prompts
Where this breaks in practice:
  • No priv-esc primitive included; blast radius is the invoking UID
  • MacOS/Linux EDRs (CrowdStrike, SentinelOne, Defender for Endpoint) will flag sh -c spawned from ssh with anomalous argv
Detection/coverage: Behavioral EDR + CI runner secret access anomalies catch the follow-on.
03 · Intelligence Metadata

The supporting signals.

CVEs bundledCVE-2025-61984, CVE-2025-61985
In-the-wild exploitationNone observed. No CISA KEV listing; no threat actor campaigns tied to this CVE pair as of July 2026.
Public PoCYes — David Leadbeater's disclosure includes reproduction steps via .gitmodules. No Metasploit/Nuclei module.
EPSS0.20% / 11th percentile — well below action threshold
CVSS 3.13.6 LOWAV:L/AC:H/PR:L/UI:N/S:U/C:L/I:L/A:N (local vector, high complexity, low impact)
Affected versionsAll OpenSSH prior to 10.1 / 10.1p1; distro backports available for RHEL (RHSA-2025:23481), Debian, Ubuntu, Alpine, Rocky, Alma
Fixed versionsOpenSSH 10.1 / 10.1p1 (released 2025-10-06); vendor backports across major distros
Exposure telemetryClient-side only — Shodan/Censys/GreyNoise don't index ssh client installs; scanner surfaces every host with the binary regardless of risk
Disclosure6 October 2025 via openssh-unix-announce
CreditDavid Leadbeater (independent researcher)
04 · The Call

noisgate verdict.

Final Verdict
= UNCHANGED to LOW (3.4/10)

The single decisive factor is that this is a client-side flaw in ssh that requires an unquoted ProxyCommand %r in the victim's config plus victim interaction with an attacker-controlled repo — the vast majority of installs have no ProxyCommand and the bug cannot touch sshd. Blast radius is capped at the invoking user's UID with no priv-esc primitive.

HIGH severity bucket (LOW)
HIGH exploitation prerequisites
MEDIUM installed-base exposure to `ProxyCommand` use

Why this verdict

  • Client-side, not server-side: The bug is in ssh, not sshd. Your 10,000-host SSH server fleet is not exposed by these CVEs. The Nessus plugin flags every host with the OpenSSH package, which vastly overstates real risk.
  • Requires opt-in config: ProxyCommand with unquoted %r is the trigger. Default OpenSSH deployments do not set ProxyCommand, so the chain terminates at step 3 for most of the fleet.
  • User interaction required: Victim must clone or update a hostile git repo with submodule recursion, or paste a malicious ssh:// URI. This is a social-engineering / supply-chain chain, not a scanner-and-pop primitive.
  • No priv-esc: Execution lands as the invoking UID with no included escalation. On a dev workstation that means user-level RCE; on a CI runner, secrets exposure — but neither is fleet-scale.
  • Role multiplier: Bastion / jump-host role — even if a jump host uses ProxyCommand, the attacker still needs to induce an admin to run ssh with a hostile username. Chain succeeds but blast radius = one admin session, not fleet compromise. Not a floor-raising role.
  • Role multiplier: CI/CD runner cloning untrusted repos — chain succeeds with more automation, and secrets in the runner environment can leak. This is the one non-trivial scenario, but it's mitigated by protocol.ssh.allow=user (git ≥ 2.38 default) and by pinning submodule sources. Blast radius = one runner's secrets, not the CI plane.
  • EPSS and KEV agree: 0.2% EPSS, no KEV, no in-the-wild activity — the exploit economy has correctly priced this as low-value.

Why not higher?

There is no path to MEDIUM without an unquoted ProxyCommand in your standard build AND a workflow that clones untrusted repos with submodule recursion. Even then the outcome is single-user RCE, not lateral movement or fleet impact. Nothing here threatens sshd, key material, or Kerberos/GSSAPI trust.

Why not lower?

Not IGNORE because CI/CD runners that clone third-party code with submodules ARE a plausible target, and the fix is trivially bundled into distro package updates you're already shipping. Leaving unpatched OpenSSH clients on build infrastructure is unnecessary residual risk when the remediation is free.

05 · Compensating Control

What to do — in priority order.

  1. Quote %r in every ProxyCommand — Change ProxyCommand ssh %r@%h ... to ProxyCommand ssh '%r'@%h ... in /etc/ssh/ssh_config and user configs. This neutralizes both CVEs by preventing shell interpretation of control characters. No mitigation SLA at LOW severity — apply during routine config hygiene.
  2. Enforce protocol.ssh.allow=user on CI runners and dev workstations — Set git config --global protocol.ssh.allow user (or GIT_ALLOW_PROTOCOL=file:git:http:https) so submodule URLs cannot force the ssh transport. Cuts off the primary delivery vector regardless of OpenSSH version. Deploy via config management within the 365-day remediation window.
  3. Roll distro OpenSSH package updates through normal patching — RHEL, Debian, Ubuntu, Alpine, Rocky, and Alma have all backported 10.1 fixes. Include in the next monthly package baseline — no emergency change window needed.
  4. Audit ~/.ssh/config sprawl on developer laptops — Run a one-shot inventory for ProxyCommand directives across your MDM-managed endpoints. Any hit gets remediated with the quoted-%r fix regardless of OpenSSH version.
What doesn't work
  • Blocking port 22 at the firewall — this is a client-side flaw, not a sshd bug. Perimeter controls are irrelevant.
  • Disabling password auth / enforcing keys — auth mode has no bearing; the injection happens before any protocol handshake.
  • Rotating host keys — the bug does not touch host keys or MITM primitives (that was CVE-2025-26465 earlier in 2025, a separate issue).
  • SSH bastion enforcement — bastions may in fact be MORE exposed if they use ProxyCommand; enforcement without quoting %r is neutral at best.
06 · Verification

Crowdsourced verification payload.

Run on any Linux/macOS host to check both the OpenSSH client version and whether a risky ProxyCommand pattern is present. Invoke as sudo bash check-openssh-10.1.sh on the target host — root is needed only to read /etc/ssh/ssh_config and per-user configs.

noisgate-verify.sh
BASHREAD-ONLYSAFE
#!/usr/bin/env bash
# noisgate verification for CVE-2025-61984 / CVE-2025-61985 (OpenSSH < 10.1)
# Exit 0=PATCHED, 1=VULNERABLE, 2=UNKNOWN
set -u

FIXED_MAJOR=10
FIXED_MINOR=1

if ! command -v ssh >/dev/null 2>&1; then
  echo "UNKNOWN: ssh client not found"
  exit 2
fi

VER_RAW=$(ssh -V 2>&1)
# Parse OpenSSH_X.Y[pN]
VER=$(printf '%s' "$VER_RAW" | sed -n 's/^OpenSSH_\([0-9][0-9]*\)\.\([0-9][0-9]*\).*/\1 \2/p')
if [ -z "$VER" ]; then
  echo "UNKNOWN: could not parse version from: $VER_RAW"
  exit 2
fi
MAJ=$(echo $VER | awk '{print $1}')
MIN=$(echo $VER | awk '{print $2}')

VULN=0
if [ "$MAJ" -lt "$FIXED_MAJOR" ]; then
  VULN=1
elif [ "$MAJ" -eq "$FIXED_MAJOR" ] && [ "$MIN" -lt "$FIXED_MINOR" ]; then
  VULN=1
fi

# Config exposure check: unquoted %r in any ProxyCommand
CFG_HIT=0
for f in /etc/ssh/ssh_config /etc/ssh/ssh_config.d/*.conf $HOME/.ssh/config; do
  [ -r "$f" ] || continue
  # Look for ProxyCommand lines containing %r that is NOT already single-quoted as '%r'
  if grep -Ei '^[[:space:]]*ProxyCommand.*%r' "$f" 2>/dev/null | grep -Ev "'%r'" >/dev/null; then
    CFG_HIT=1
    echo "CONFIG-RISK: unquoted %r in ProxyCommand -> $f"
  fi
done

if [ $VULN -eq 1 ] && [ $CFG_HIT -eq 1 ]; then
  echo "VULNERABLE: OpenSSH $MAJ.$MIN with unquoted ProxyCommand %r"
  exit 1
elif [ $VULN -eq 1 ]; then
  echo "VULNERABLE: OpenSSH $MAJ.$MIN (< $FIXED_MAJOR.$FIXED_MINOR) — no risky ProxyCommand detected; still patch"
  exit 1
else
  echo "PATCHED: OpenSSH $MAJ.$MIN"
  exit 0
fi
07 · Bottom Line

If you remember one thing.

TL;DR
This is a LOW that stays LOW. Don't wake anyone up. Roll the OpenSSH 10.1 backport into your next scheduled distro patch cycle — noisgate remediation SLA is ≤ 365 days and there is no noisgate mitigation SLA at LOW severity, so treat this as backlog hygiene rather than an urgent change. Do two things this quarter that are cheaper than patching: (1) push a git config baseline setting protocol.ssh.allow=user to all CI runners and developer endpoints, and (2) grep your standard ssh_config templates for unquoted %r in ProxyCommand and quote them. If your CI plane clones untrusted third-party repos with submodule recursion, prioritize those runners first — they are the only place this CVE pair has a realistic weaponization story.

Sources

  1. Tenable Plugin 269984
  2. OpenSSH Release Notes (10.1)
  3. Wiz CVE-2025-61984 writeup
  4. Broadcom KB — CVE-2025-61984 / -61985 in ESXi / vCenter
  5. Red Hat RHSA-2025:23481
  6. runZero — impacted OpenSSH systems
  7. Trellix advisory — CVE-2025-61984 / -61985
  8. OpenSSH Security Page
Peer Review

What defenders are saying.

Submit a review attribution: handle + country only
0 flags selected · stored anonymously
Validation Results

Crowdsourced verification outputs.

Results submitted by users who ran the verification payload against their environment.