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
AllowAgentForwarding no server-side for sensitive roles and keep ForwardAgent no as 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.X11Forwarding no broadly 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.DisableForwarding yes — Find the users, groups, and Match blocks 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.- 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.
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. |
Sources.
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.
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