A bundle of hardening fixes wearing a Critical 9.4 costume
OpenSSH 10.4, released 6 July 2026, ships eight CVEs (CVE-2026-59995 through CVE-2026-60002) affecting every OpenSSH build prior to 10.4 across Linux, BSD, macOS, and Windows OpenSSH-Portable. The headliners are a client-side use-after-free (CVE-2026-60002, 7.7) triggered when a *malicious server* rotates its host key mid re-exchange; two SFTP/SCP path-handling bugs (CVE-2026-59995 / -59996) where a *malicious server* redirects the client's download to an unintended local path; and server-side hygiene fixes: DisableForwarding not overriding PermitTunnel (-59999), missing minimum-auth-delay enforcement (-60001), and a GSSAPI-triggered pre-auth DoS (-60000). No pre-auth RCE. No privilege escalation to root. No credential theft primitive.
Tenable's plugin flags this bundle at CVSS 9.4 Critical while labeling it HIGH — the score is inherited from vector arithmetic (AV:N/AC:L/PR:N/UI:N), not from the actual reachability of any single bug. In production terms the vendor label overstates risk: five of the eight require the victim to be the SSH *client* connecting to an *attacker-controlled server*, which is the inverse of the threat model most enterprises worry about (a bastion getting popped from the internet). The remaining server-side items are DoS or defense-in-depth regressions, not shell-on-box. Reality is MEDIUM.
4 steps from start to impact.
Attacker stands up a malicious SSH/SFTP endpoint
git@githu8.com), phishing a jump-host DNS record, poisoned Ansible inventory, or a compromised third-party SFTP partner.- Attacker owns or hijacks an SSH server reachable by the target
- Target initiates outbound SSH to that endpoint
- Target has not pinned known_hosts entry (or bug triggers before host-key check completes)
- Egress SSH from user workstations is routinely blocked at proxies
- known_hosts TOFU catches unfamiliar hosts on first connect
- SFTP partners are usually IP-allowlisted
ssh.exe / ssh process creation but rarely alerts on it.Trigger client-side use-after-free (CVE-2026-60002)
- Client stays connected long enough for KEX_INIT retransmission
- Attacker has a heap-grooming primitive against the specific libc + OpenSSH build
- Most SSH sessions are short-lived and won't hit a rekey
- ASLR + stack canaries + fortified allocators (glibc 2.35+, jemalloc) blunt UAF-to-RIP conversion
- Exploit is highly version- and distro-specific
ssh child crash / core dump. No public IDS signature.OR: Path traversal via SFTP/SCP (CVE-2026-59995 / -59996)
sftp> get file.txt with a filename containing traversal sequences, causing the client to write to ~/.ssh/authorized_keys, ~/.bashrc, or a systemd user unit. If the target runs the SFTP client under a service account with an interesting home directory, the attacker plants persistence on the *client-side* host.- Victim uses
sftp/scpin a workflow that trusts server-supplied filenames - Target account has write access to a security-relevant file
- Interactive
sftpusers almost never accept server-chosen paths - Automated pipelines usually cd into a scratch dir first
- SELinux/AppArmor confines user shells away from ~/.ssh writes on hardened distros
OR: Server-side GSSAPI DoS (CVE-2026-60000) against exposed sshd
GSSAPIAuthentication yes (default is no on most distros).- sshd reachable from attacker network segment
- GSSAPIAuthentication explicitly enabled (typical only in Kerberos/AD-integrated Linux fleets)
- Default distro configs have GSSAPI off
- fail2ban / sshguard / rate-limits blunt volumetric variants
- DoS only — no code exec, no data access
PAM/GSSAPI errors; connection-rate SIEM alerts fire on saturation.The supporting signals.
| CVE IDs | CVE-2026-59995, -59996, -59997, -59998, -59999, -60000, -60001, -60002 (8 total) |
|---|---|
| Vendor severity (Tenable) | HIGH label, CVSS v3 base 9.4 (Critical arithmetic), temporal 8.2 |
| CVSS vector | CVSS:3.0/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:L — vector assumes reachability the actual bug classes don't have |
| Highest per-CVE score | CVE-2026-60002 client UAF at 7.7 (server-controlled trigger) |
| In-the-wild status | None observed. Not on CISA KEV as of 2026-07-23. No campaigns reported by Mandiant, CrowdStrike, or MSTIC. |
| Public PoC | No public PoC for any of the 8 CVEs. OpenSSH advisories describe conditions only; no exploit repos on GitHub. |
| EPSS (estimated) | Sub-0.5% across all eight IDs — brand-new CVEs, no exploit code, low predicted 30-day exploitation |
| Affected versions | All OpenSSH releases prior to 10.4 (portable and OpenBSD tree). Distro backports track separately — see RHSA/USN/DSA. |
| Fixed version | OpenSSH 10.4 (upstream, 6 July 2026). Backports: RHEL 10 RHSA-2025:23479 supersession pending, Ubuntu USN not yet published, Debian openssh-server 1:10.4p1-* in unstable |
| Disclosure / reporter | Disclosed 2026-07-06 with the 10.4 release. Reporters credited include the OpenSSH team internal audit + external researchers (Qualys, OpenSSH mailing list contributors) |
| Exposure population | Shodan shows ~19M internet-exposed OpenSSH banners globally; ≥95% currently pre-10.4. But server-side CVEs in this batch are DoS/hardening only — not the RCE that population implies. |
noisgate verdict.
Downgraded from HIGH because the decisive factor is attack-model inversion: five of the eight CVEs (including the only memory-corruption bug, CVE-2026-60002) require the victim to be the SSH *client* connecting to an *attacker-controlled server* — the opposite of the sshd-facing-internet threat model that justifies OpenSSH's usual severity ceiling. The remaining server-side items are DoS and configuration-hardening regressions, not authentication bypass or RCE, so no chain reaches root-on-sshd or fleet compromise.
Why this verdict
- Attack model inversion: the memory-corruption bug and both path-traversal bugs require the target to SSH *outbound* to a malicious server — enterprises rarely allow arbitrary outbound SSH from user endpoints, and CI runners connect to a pinned known_hosts set.
- No pre-auth RCE against sshd: the only server-side pre-auth bug (CVE-2026-60000) is a GSSAPI DoS, and only triggers when
GSSAPIAuthentication yesis set — off by default on Debian/Ubuntu/RHEL stock configs. - No public PoC, not on KEV, EPSS <0.5%: exploitability signals are all cold seven weeks after disclosure.
- Role multiplier — bastion / jump host: sshd on a bastion is the canonical high-value role. Chain outcome: the pre-auth CVE here is DoS only (no RCE, no auth bypass). Blast radius: temporary loss of management-plane availability, no compromise. This does NOT trip the CRITICAL floor.
- Role multiplier — CI/CD runner initiating outbound SSH to deploy targets: if the runner's known_hosts is not pinned and an attacker MitMs the deploy endpoint, CVE-2026-59995/-59996 could plant a file in the runner's home. Chain outcome: runner-side persistence, then supply-chain pivot. This is why the verdict is MEDIUM and not LOW — the CI pattern is a real >1% deployment mode.
- Role multiplier — SFTP client in B2B EDI workflows: batch jobs that pull files from partner SFTP with unpinned host keys are the realistic bad-case for -59995/-59996. Not fleet-scale.
Why not higher?
HIGH would require a plausible unauthenticated-remote-to-sshd RCE or credential-theft primitive; this batch contains neither. Every serious primitive in the bundle inverts the trust direction (client trusting a malicious server) or degrades to DoS. A HIGH floor would only apply if the CI-runner pivot path (step 3) were tied to an active campaign, and no such campaign exists.
Why not lower?
LOW would ignore the realistic CI/CD and B2B-SFTP path-traversal scenarios and the client UAF, which — while requiring the attacker to control an SSH server — is genuinely reachable in supply-chain, third-party SFTP, and phishing-jump-host scenarios. OpenSSH is also everywhere (millions of hosts), so a MEDIUM keeps it in the 365-day remediation queue rather than backlog hygiene.
What to do — in priority order.
- Pin known_hosts on all CI/CD runners and automation accounts — Blocks the -59995/-59996/-60002 chains because they require the client to complete a KEX with an attacker-controlled server. Enforce
StrictHostKeyChecking=yesand ship a curated~/.ssh/known_hostsvia config management (Ansible/Chef/Puppet) rather than letting TOFU accept whatever answers. No mitigation SLA for MEDIUM per noisgate — but implement inside the 365-day remediation window regardless. - Set
GSSAPIAuthentication noon any sshd where you're not using Kerberos SSO — Removes the CVE-2026-60000 pre-auth DoS path entirely. Grep/etc/ssh/sshd_configand/etc/ssh/sshd_config.d/*.conffleet-wide; the option isnoby default on modern distros but hand-rolled configs sometimes flip it on 'just in case.' - Restrict outbound SSH from user workstations and non-jump servers — Neutralizes the malicious-server attack surface. Egress-filter tcp/22 at the proxy/firewall; allowlist only your bastions and known B2B SFTP partners. The bulk of client-side bugs die here.
- Track distro backports and roll during your quarterly maintenance window — RHEL, Ubuntu, Debian, Amazon Linux, and SUSE will backport the fixes to their stable OpenSSH branches over the next 2-6 weeks. Watch RHSA, USN, DSA, ALAS. Apply through your normal patch cadence — this is not a drop-everything event.
- Rebuild container base images and Golden AMIs on next scheduled cycle — Container-based fleets carry OpenSSH inside base images (Debian slim, Alpine, UBI). Rebuild after your distro publishes the backport; force a rolling redeploy of any workload with an sshd sidecar or that shells out to
ssh/scpat runtime.
- fail2ban / sshguard — does not stop malicious-server bugs at all (attacker isn't authenticating to your sshd), and only partially blunts the GSSAPI DoS.
- Disabling password auth / requiring pubkey — orthogonal; every bug in this batch triggers either pre-auth or on the client side.
- MFA on SSH (e.g., PAM+TOTP, Duo) — MFA runs after the KEX where CVE-2026-60002 fires and after the sftp/scp file transfer where -59995/-59996 fire.
- Rotating host keys on your servers — the vuln is your *client* mishandling a hostile server's key rotation, not the other way around.
- WAF / IDS at the perimeter — SSH is encrypted from byte one; no signature-based network inspection will see any of these payloads.
Crowdsourced verification payload.
Run on each Linux/BSD/macOS target as any user (no root required). Invoke: bash check_openssh_10_4.sh. Exit codes: 0 = PATCHED, 1 = VULNERABLE, 2 = UNKNOWN. Works on RHEL/Debian/Ubuntu/SUSE/Alpine/Arch/FreeBSD/macOS.
#!/usr/bin/env bash
# noisgate: OpenSSH < 10.4 (CVE-2026-59995 .. CVE-2026-60002) check
# Detects both server (sshd) and client (ssh) binaries.
set -u
FIXED_MAJOR=10
FIXED_MINOR=4
parse_ver() {
# Extracts numeric major.minor from an OpenSSH version banner
# e.g. 'OpenSSH_9.7p1, OpenSSL ...' -> '9 7'
local raw="$1"
echo "$raw" | sed -nE 's/.*OpenSSH_([0-9]+)\.([0-9]+).*/\1 \2/p' | head -n1
}
compare() {
# returns 0 if $1.$2 < FIXED_MAJOR.FIXED_MINOR
local maj=$1 min=$2
if [ "$maj" -lt "$FIXED_MAJOR" ]; then return 0; fi
if [ "$maj" -eq "$FIXED_MAJOR" ] && [ "$min" -lt "$FIXED_MINOR" ]; then return 0; fi
return 1
}
SSHD_BIN=$(command -v sshd 2>/dev/null || echo "")
SSH_BIN=$(command -v ssh 2>/dev/null || echo "")
VULN=0
CHECKED=0
for bin in "$SSHD_BIN" "$SSH_BIN"; do
[ -z "$bin" ] && continue
CHECKED=1
# sshd prints version to stderr; ssh -V also to stderr
ver_out=$("$bin" -V 2>&1 || true)
read -r maj min < <(parse_ver "$ver_out")
if [ -z "${maj:-}" ] || [ -z "${min:-}" ]; then
echo "UNKNOWN: could not parse version from $bin -> $ver_out"
continue
fi
if compare "$maj" "$min"; then
echo "VULNERABLE: $bin reports OpenSSH_${maj}.${min} (< ${FIXED_MAJOR}.${FIXED_MINOR})"
VULN=1
else
echo "PATCHED: $bin reports OpenSSH_${maj}.${min}"
fi
done
# Note: distro backports may retain an older upstream version string while carrying the fix.
# Cross-check with rpm -q --changelog openssh / dpkg -s openssh-server for CVE references.
if command -v rpm >/dev/null 2>&1; then
if rpm -q --changelog openssh 2>/dev/null | grep -qE 'CVE-2026-(5999[5-9]|6000[0-2])'; then
echo "NOTE: distro backport for at least one CVE detected via rpm changelog"
fi
fi
if command -v dpkg >/dev/null 2>&1; then
if dpkg -s openssh-server 2>/dev/null | grep -qE 'CVE-2026-(5999[5-9]|6000[0-2])'; then
echo "NOTE: distro backport for at least one CVE detected via dpkg metadata"
fi
fi
if [ "$CHECKED" -eq 0 ]; then
echo "UNKNOWN: no ssh or sshd binary found on PATH"
exit 2
fi
exit $VULN
If you remember one thing.
GSSAPIAuthentication yes and turn it off unless you're actually using Kerberos SSO; (3) pin known_hosts on CI runners and any automation account that does outbound SSH. Per the noisgate mitigation SLA for MEDIUM: no mitigation deadline — go straight to the noisgate remediation SLA window of ≤ 365 days, meaning fold the OpenSSH 10.4 backport (RHSA / USN / DSA / ALAS as they land over the next 2-6 weeks) into your normal quarterly patch train alongside your OS updates. Do NOT preempt higher-severity work (real KEV items, actively-exploited edge appliances) to chase this one. If a public PoC drops for CVE-2026-60002 or CISA adds any of these to KEV, re-evaluate immediately — treat as 'patch within hours' per KEV override.Sources
What defenders are saying.
Crowdsourced verification outputs.
Results submitted by users who ran the verification payload against their environment.