OpenSSH 9.9 → 10.3: Why You Should Update Now
Quick summary: if you still operate clients or servers based on OpenSSH 9.9/9.9p1, upgrading to OpenSSH 10.3/10.3p1 is not just a routine version bump. Between 9.9 and 10.3, the project shipped security fixes affecting client-side flows, certificates, algorithm policies, legacy scp, and forwarding enforcement. Some depend on specific configuration; others reduce entire classes of operational risk.
This post focuses on defensive impact and safe laboratory reproductions. The PoCs below do not provide a remote exploit against third parties; they demonstrate risk conditions that help infrastructure, Blue Team, and AppSec teams validate exposure and prioritize upgrades.
What changed from 9.9 to 10.3?
The OpenSSH 9.9 branch, released in 2024, already introduced important improvements such as hybrid post-quantum key exchange and removal of pre-authentication compression. But the following releases fixed relevant issues that still affect environments that remained stuck on 9.9.
- 10.0: fixed the behavior of
DisableForwarding, which did not block X11 forwarding and agent forwarding as documented. - 10.1: fixed injection in flows where untrusted usernames or URIs were expanded into
ProxyCommandvia%r. - 10.3: fixed delayed validation of shell metacharacters in usernames used in expansions such as
%u, in addition to issues involving certificates,scp -O, ECDSA in accepted-algorithm lists, and multiplexing confirmation.
Why does this matter in real servers?
1. SSH is privileged infrastructure
SSH normally sits on the path of server administration, CI/CD, bastions, automation, backup, deployment, and emergency access. Even when the vulnerability is in the client, the risk can become an incident if an internal panel, wrapper, script, or support tool builds ssh commands from untrusted input.
2. Not every risk is “remote pre-auth RCE in sshd”
Many teams only prioritize SSH when there is a classic “pre-auth RCE” bug. That is a mistake. Failures in ProxyCommand, Match exec, agent forwarding, certificates, and scp can compromise administrative workstations, internal pivots, and automation chains.
3. The upgrade also improves cryptographic posture
The 10.x series removed weak DSA support, changed key exchange and cipher preferences, and strengthened the server authentication path with additional internal separation. Even when a specific fix does not apply to your environment, the overall set of changes reduces attack surface.
Main vulnerability classes fixed
Injection through the SSH client and configurations with ProxyCommand/Match exec
The 10.1 and 10.3 notes describe scenarios in which attacker-controlled data, when used to build an ssh command line, could reach configuration expansions such as %r or %u and trigger command execution in certain ProxyCommand or Match exec combinations.
This mainly affects environments that expose ssh to external input: support portals, inventory scripts, DevOps tools, jump wrappers, and integrations that accept user@host from a ticket, spreadsheet, or API.
SSH certificates and principal matching
OpenSSH 10.3 changes the handling of certificates with an empty principals list and fixes improper matching in cases involving commas in a principal when authorized_keys principals="..." is used. For environments with internal SSH CAs, this matters: a certificate issuance error should not turn into an unexpected wildcard.
DisableForwarding not blocking everything it claimed to
In 10.0, the project fixed DisableForwarding, which failed to disable X11 forwarding and agent forwarding as documented. If you use restricted keys, automation accounts, or temporary access, this kind of bug can break an isolation assumption.
Legacy scp and setuid/setgid bits
OpenSSH 10.3 fixes a long-standing behavior in legacy scp mode -O: when downloading files as root without -p, the setuid/setgid bits were not cleared as normally expected. In recovery flows, administrative copy operations, or legacy automation, that can leave dangerous files at the destination.
ECDSA and accepted algorithm lists
OpenSSH 10.3 also fixes incomplete application of PubkeyAcceptedAlgorithms and HostbasedAcceptedAlgorithms to ECDSA keys. Previously, the presence of one specific ECDSA algorithm could allow other ECDSA algorithms outside the intended list.
Safe PoC 1: demonstrating injection risk in an SSH wrapper
Goal: show why scripts that receive user/host and build ssh commands must sanitize input. Run only in a local lab. The PoC below does not connect to any real server; it uses ProxyCommand to demonstrate dangerous expansion.
mkdir -p /tmp/openssh-poc
cat > /tmp/openssh-poc/ssh_config <<'EOF'
Host lab
HostName 127.0.0.1
UserKnownHostsFile /dev/null
StrictHostKeyChecking no
ProxyCommand sh -c 'echo proxy-user=%r > /tmp/openssh-poc/proxy.log'
EOF
# Simulates attacker-controlled input being passed as the username.
# In vulnerable versions/configurations, metacharacters could reach the shell.
USER_INPUT='alice; echo INJECTION_TEST > /tmp/openssh-poc/injected.txt #'
ssh -F /tmp/openssh-poc/ssh_config -o BatchMode=yes -l "$USER_INPUT" lab true 2>/tmp/openssh-poc/ssh.err || true
ls -l /tmp/openssh-poc/
cat /tmp/openssh-poc/proxy.log 2>/dev/null || true
cat /tmp/openssh-poc/injected.txt 2>/dev/null || echo 'OK: injected.txt was not created'
In fixed versions, the client will tend to block or neutralize dangerous usernames before they are expanded in sensitive contexts. The point of the PoC is educational: if your automation accepts user, host, URI, or SSH destination from an untrusted source, treat it as hostile data.
Safe PoC 2: triaging configurations with ProxyCommand and Match exec
Use this check to find local configurations that deserve review after the upgrade. It does not exploit anything; it only lists places where expanded input may have impact.
#!/usr/bin/env bash
set -euo pipefail
FILES=(/etc/ssh/ssh_config "$HOME/.ssh/config")
for f in "${FILES[@]}"; do
[ -f "$f" ] || continue
echo "== $f =="
grep -nEi 'ProxyCommand|ProxyJump|Match[[:space:]].*exec|%r|%u|%h|User[[:space:]]' "$f" || true
echo
done
echo '[Checklist]'
echo '- Does ProxyCommand use %r, %u, or %h? Validate data origin.'
echo '- Does Match exec call an external shell? Prefer a fixed script with sanitized arguments.'
echo '- Does any wrapper build ssh user@host from API/ticket/form input? Sanitize it.'
echo '- Upgrade the OpenSSH client on administrative workstations, not only the server.'
Safe PoC 3: checking DisableForwarding and forwarding surface
If you use restricted accounts, verify that the expected policy is really applied on the server. This check helps identify configurations that depend on DisableForwarding, AllowAgentForwarding, and X11Forwarding.
sudo sshd -T | grep -Ei 'disableforwarding|allowagentforwarding|allowtcpforwarding|x11forwarding|permitopen|permitlisten'
# For restricted accounts, look for per-key options:
sudo grep -RInE 'DisableForwarding|no-agent-forwarding|no-X11-forwarding|permitopen|principals=' /etc/ssh ~/.ssh 2>/dev/null || true
If your policy depends on “disallow forwarding,” validate with updated client and server. The risk is not only an interactive shell: improper agent forwarding can expose credentials on pivots, and X11 forwarding can expand the local surface.
Safe PoC 4: checking legacy scp (-O) usage
Modern scp uses SFTP by default, but many scripts still force legacy mode with -O. Run a simple search:
grep -RInE 'scp[[:space:]].*-O|scp.*--' /etc /opt "$HOME" 2>/dev/null | head -100
If you find root copy operations, old playbooks, or recovery routines using scp -O, review them. The 10.3 fix reduces one dangerous surprise involving setuid/setgid bits, but the practical recommendation is to migrate flows to SFTP/rsync and reduce privileged copies.
How to update with less risk
- Inventory: collect client and server versions with
ssh -Vandsshd -Vwhere available. - Compatibility testing: validate jump hosts, bastions, CI/CD, Ansible, Git over SSH, SFTP, and
scp. - Review legacy algorithms: DSA and legacy finite-field Diffie-Hellman may break old integrations — that is good from a security perspective, but it needs a plan.
- Update administrative clients: the injection fixes are mostly client-side.
- Revalidate SSH CAs: review certificate issuance, principals, and
AuthorizedPrincipalsFilefiles. - Monitor logs: changes in internal binaries and messages can affect SIEM rules.
Quick checklist for Blue Team
# Local client version
ssh -V
# Effective server config
sudo sshd -T | sort | less
# Risky patterns in client configuration
grep -RInE 'ProxyCommand|Match[[:space:]].*exec|%r|%u|ProxyJump' ~/.ssh /etc/ssh 2>/dev/null
# Search for legacy scp
find /etc /opt "$HOME" -type f 2>/dev/null | xargs grep -nE 'scp[[:space:]].*-O' 2>/dev/null | head
Conclusion
OpenSSH 10.3 should not be seen only as “the newest version.” For anyone still on 9.9, it consolidates fixes in areas that tend to be neglected: the SSH client, wrappers, forwarding, certificates, algorithms, and legacy tools. In corporate environments, the upgrade reduces risk exactly where SSH is most sensitive: automation and administrative access.
The practical recommendation is to update administrative workstations and staging bastions first, run the triage PoCs above, fix unsafe wrappers, and only then expand to the rest of the fleet.
- How to enable MFA for SSH on Kali Linux — practical hardening for administrative access.
- Free VPN on Oracle Cloud with OpenVPN — a useful lab for testing remote access and bastions.
- Defensive validation checklist for Blue Team — a model for triage and safe validation.
Frequently asked questions about OpenSSH 10.3
Does OpenSSH 10.3 fix a direct RCE in sshd?
That is not the best framing. The most relevant fixes between 9.9 and 10.3 involve the SSH client, variable expansion, certificates, forwarding, legacy scp, and algorithm policies. The risk shows up mainly in automation, bastions, and poorly isolated wrappers.
Should I update servers or administrative workstations first?
In many environments, it makes sense to start with administrative workstations, jump hosts, and bastions, because they concentrate SSH client use, scripts, ProxyCommand, ProxyJump, and privileged access. Then move to servers under a controlled maintenance window.
Are the article’s PoCs safe for corporate environments?
Yes. They were written as defensive validations and lab reproductions. The goal is to identify configuration and automation patterns that deserve correction, not to exploit third parties or provide ready-made offensive payloads.
References
- OpenSSH 10.3 release notes
- OpenSSH 10.1 release notes
- OpenSSH 10.0 release notes
- OpenSSH 9.9 release notes
💜 Enjoyed this content? Support the blog with USDT (TRC20):
TX7obcjHQbDUXb4mGqoASEu1QFTKT2CFGG
