This is a deadbolt that fails only after you already handed someone the front-door key
This is CVE-2025-32728 in sshd: in OpenSSH 7.4 through 9.9 / before 10.0, the DisableForwarding directive failed to block X11 forwarding and agent forwarding as documented. The bug lives in a policy control, not in authentication or packet parsing, so the vulnerable population is really the subset of hosts that both run an affected OpenSSH build and deliberately use DisableForwarding yes to constrain a user or match block.
Tenable calling this LOW is basically right. The real-world attack chain is narrow: the attacker already needs an SSH session or valid credentials, the environment has to rely on this specific directive, and OpenSSH upstream notes that X11 forwarding is disabled by default on the server while agent forwarding is off by default on the client. That combination crushes reachability and makes this a control-bypass bug, not an internet-scale SSH break.
4 steps from start to impact.
Get an SSH foothold with ssh
- Valid SSH credentials or another post-initial-access route
- Target runs affected OpenSSH
sshdbefore 10.0
- MFA, PAM controls, cert-based auth, and IP allowlists frequently gate SSH access
- This prerequisite already implies the attacker is past the outer perimeter
Land on an account constrained by DisableForwarding
DisableForwarding yes, often in a Match block or restricted account policy. If that directive is absent, there is no policy being bypassed and the version finding is mostly noise.DisableForwarding yesis configured for the target user/session- The account can establish a normal SSH session
- Many fleets never use
DisableForwardingat all - Some orgs use other restrictions like forced commands, jump hosts, or separate bastion roles instead
DisableForwarding is actually configured; host-side config review is required.Request forwarding with built-in ssh -A or ssh -X
ssh -A for agent forwarding or ssh -X / -Y for X11 forwarding. Because of the logic error, the server may still honor these channels despite DisableForwarding being set.- Client requests agent forwarding or X11 forwarding
- The relevant forwarding path is otherwise enabled in the environment
- OpenSSH upstream says agent forwarding is off by default in the client
- OpenSSH upstream says X11 forwarding is disabled by default in the server
- Even where technically possible, many enterprise users never invoke
-Aor-X
Turn the bypass into lateral movement
- The victim has a loaded SSH agent or actually uses X11 workflows
- Downstream systems accept the forwarded agent or X11 path
- Many server-side sessions have no agent loaded at all
- Modern bastion patterns, session recording, and short-lived certs reduce the usefulness of forwarded auth material
The supporting signals.
| CVE / bug class | CVE-2025-32728, CWE-440 Expected Behavior Violation. This is a documented-behavior failure in sshd, not memory corruption. |
|---|---|
| Disclosure | Public on 2025-04-09 via OpenSSH 10.0 release / security notes; NVD shows publication on 2025-04-09. |
| Affected versions | OpenSSH upstream says 7.4 to 9.9 inclusive are affected; NVD models this as 7.4 up to, but excluding, 10.0. |
| Fixed versions | Upstream fixed in 10.0. Backports exist, including Ubuntu 24.04: 1:9.6p1-3ubuntu13.11, Ubuntu 22.04: 1:8.9p1-3ubuntu0.13, Debian 12 bookworm-security: 1:9.2p1-2+deb12u5, Debian 11 bullseye-security/LTS: 1:8.4p1-5+deb11u5. |
| Vendor scoring mismatch | Tenable rates the plugin LOW / 3.8 using the NVD-style vector, while MITRE/Ubuntu show 4.3 MEDIUM. The disagreement is mostly about whether the flaw is modeled as needing privileges; in practice, it is still a narrow post-auth policy bypass. |
| Defaults that cut risk | OpenSSH upstream explicitly notes X11 forwarding is disabled by default in the server and agent forwarding is off by default in the client. That strips out a huge chunk of nominally vulnerable hosts. |
| In-the-wild status | I found no KEV listing and no credible active-exploitation reporting for this CVE as of 2026-06-01. CISA's weekly bulletin listed it as a newly published vulnerability, not as a known-exploited one. |
| PoC / weaponization | No major Metasploit or vendor-backed exploit tooling surfaced in the sources reviewed. Public discussion exists, but exploitation is basically just using stock ssh -A / ssh -X against a host that trusted DisableForwarding to block them. |
| EPSS | Primary FIRST EPSS query results were not directly retrievable here; third-party mirrors place it around 0.27% EPSS and roughly 50th percentile, i.e. low-to-middle exploit-likelihood signal, which matches the weak real-world reachability. |
| Exposure / scanner reality | SSH is broadly internet-facing in enterprise and provider environments, so version scanners will flag a lot of systems. But this is not an exposure-driven CVE: public port 22 alone does not make the bug materially exploitable unless the host uses DisableForwarding and the attacker already has a session. |
noisgate verdict.
The decisive factor is the attacker-position requirement: this only matters after the adversary already has SSH access or valid credentials. On top of that, the bug only bites where defenders intentionally depend on DisableForwarding, and upstream defaults leave both X11 and agent forwarding off in many real deployments.
Why this verdict
- Step-1 friction: requires authenticated remote access — that means this is already post-initial-access or dependent on valid credentials, which is immediate downward pressure from the vendor baseline.
- Step-2 friction: requires a specific policy dependency — only hosts that actually use
DisableForwarding yesare exposed in practice. Version-only scanner hits overcount badly. - Step-3 friction: the forwarded features are not universally on — upstream says X11 forwarding is off by default server-side and agent forwarding is off by default client-side, so the reachable population shrinks again.
Why not higher?
There is no unauthenticated remote entry point here, no memory corruption, no pre-auth parser bug, and no direct code execution path. Even successful abuse typically yields a policy bypass or lateral-movement helper, not instant host takeover.
Why not lower?
I would not mark it IGNORE because some enterprises really do use DisableForwarding to fence in contractors, vendors, or semi-trusted shell users on bastions. If you rely on that control, this bug defeats a security boundary you thought you had.
What to do — in priority order.
- Turn off agent forwarding where you can — Set
AllowAgentForwarding noserver-side for sensitive roles and keepForwardAgent noas the client default. For a LOW finding there is no mitigation SLA; deploy this as backlog hygiene in the next normal hardening cycle, prioritizing bastions and shared admin jump boxes. - Disable X11 forwarding on servers that do not need it — Set
X11Forwarding nobroadly and only carve out exceptions for known legacy workflows. Again, for a LOW verdict there is no mitigation SLA; fold this into your next routine SSH baseline review. - Audit every use of
DisableForwarding yes— Find the users, groups, andMatchblocks that rely on this control and treat those hosts as the only meaningful patch population. With a LOW verdict, do this during normal config hygiene rather than emergency change windows. - Prefer stronger account restrictions for semi-trusted shells — Where possible, pair SSH restrictions with forced commands, limited groups, session recording, or bastion-only access so that a single forwarding-control bug does not become your only containment layer. For LOW, implement as part of planned access-control improvement work.
- A WAF does nothing here; the abuse happens after SSH authentication inside encrypted SSH channels.
- Simply hiding SSH behind a non-standard port does not matter; the key issue is post-auth policy bypass, not opportunistic internet scanning.
- Version-only scanning alone is weak evidence because distro backports and the absence of
DisableForwardingboth create false urgency.
Crowdsourced verification payload.
Run this on the target Linux host that provides sshd, ideally as root or with sudo so package metadata and /etc/ssh can be read reliably. Save as check-cve-2025-32728.sh and run sudo bash check-cve-2025-32728.sh; it prints exactly VULNERABLE, PATCHED, or UNKNOWN based on package/backport status plus whether DisableForwarding yes is actually used.
#!/usr/bin/env bash
# check-cve-2025-32728.sh
# Purpose: assess practical exposure to OpenSSH DisableForwarding bug (CVE-2025-32728)
# Output: VULNERABLE / PATCHED / UNKNOWN
# Exit codes: 0=PATCHED, 1=VULNERABLE, 2=UNKNOWN
set -u
status_unknown() { echo "UNKNOWN"; exit 2; }
status_vuln() { echo "VULNERABLE"; exit 1; }
status_patched() { echo "PATCHED"; exit 0; }
have_cmd() { command -v "$1" >/dev/null 2>&1; }
# Convert OpenSSH upstream version like 9.9p1 or 10.0p2 to sortable integer.
ver_to_int() {
local v="$1"
v="${v%%p*}"
local major minor patch
major="${v%%.*}"
minor="${v#*.}"
if [[ "$minor" == "$v" ]]; then minor=0; fi
patch=0
printf "%03d%03d%03d\n" "$major" "$minor" "$patch"
}
upstream_ge_10() {
local raw="$1"
[[ -z "$raw" ]] && return 1
local cur target
cur="$(ver_to_int "$raw")"
target="$(ver_to_int "10.0")"
[[ "$cur" -ge "$target" ]]
}
sshd_ver_raw=""
if have_cmd sshd; then
sshd_ver_raw="$(sshd -V 2>&1 | sed -n 's/.*OpenSSH_\([0-9][0-9.]*p\{0,1\}[0-9]*\).*/\1/p' | head -n1)"
fi
if [[ -z "$sshd_ver_raw" ]] && have_cmd ssh; then
sshd_ver_raw="$(ssh -V 2>&1 | sed -n 's/.*OpenSSH_\([0-9][0-9.]*p\{0,1\}[0-9]*\).*/\1/p' | head -n1)"
fi
[[ -z "$sshd_ver_raw" ]] && status_unknown
# Detect whether the host actually relies on DisableForwarding.
uses_disableforwarding="no"
for f in /etc/ssh/sshd_config /etc/ssh/sshd_config.d/*.conf; do
[[ -r "$f" ]] || continue
if grep -Eiq '^\s*DisableForwarding\s+yes\b' "$f"; then
uses_disableforwarding="yes"
break
fi
done
# If distro packaging tells us it is fixed via backport, trust that over upstream version.
if have_cmd dpkg-query && have_cmd dpkg; then
pkg_ver="$(dpkg-query -W -f='${Version}\n' openssh-server 2>/dev/null | head -n1)"
if [[ -n "$pkg_ver" ]]; then
. /etc/os-release 2>/dev/null || true
distro_id="${ID:-}"
distro_ver="${VERSION_ID:-}"
case "$distro_id:$distro_ver" in
ubuntu:24.04)
dpkg --compare-versions "$pkg_ver" ge '1:9.6p1-3ubuntu13.11' && status_patched
;;
ubuntu:22.04)
dpkg --compare-versions "$pkg_ver" ge '1:8.9p1-3ubuntu0.13' && status_patched
;;
ubuntu:20.04)
dpkg --compare-versions "$pkg_ver" ge '1:8.2p1-4ubuntu0.13' && status_patched
;;
ubuntu:24.10)
dpkg --compare-versions "$pkg_ver" ge '1:9.7p1-7ubuntu4.3' && status_patched
;;
ubuntu:25.04|ubuntu:25.10|ubuntu:26.04)
dpkg --compare-versions "$pkg_ver" ge '1:9.9p1-3ubuntu3.1' && status_patched
;;
debian:12)
dpkg --compare-versions "$pkg_ver" ge '1:9.2p1-2+deb12u5' && status_patched
;;
debian:11)
dpkg --compare-versions "$pkg_ver" ge '1:8.4p1-5+deb11u5' && status_patched
;;
esac
fi
fi
# Generic upstream check. Note: backported RPM builds may be fixed even if upstream version < 10.0.
if upstream_ge_10 "$sshd_ver_raw"; then
status_patched
fi
# Practical exposure decision:
# - vulnerable version + DisableForwarding in use => VULNERABLE
# - vulnerable version + DisableForwarding unused => PATCHED (not exposed in practice)
if [[ "$uses_disableforwarding" == "yes" ]]; then
status_vuln
else
status_patched
fi
If you remember one thing.
DisableForwarding yes and isolate the true patch population to bastions, jump hosts, vendor-access shells, and any semi-trusted SSH accounts that rely on that control. If you do not use the directive, document the finding as low-value version noise and move on; if you do use it, patch those hosts in the next normal maintenance cycle and consider disabling agent/X11 forwarding where unnecessary. Under the noisgate mitigation SLA, there is no mitigation SLA — treat this as backlog hygiene; under the noisgate remediation SLA, there is no SLA for LOW findings, so close it during routine patching rather than emergency change windows.Sources
What defenders are saying.
Crowdsourced verification outputs.
Results submitted by users who ran the verification payload against their environment.