needhelp
← Back to blog

ssh-keysign-pwn: Reading Root-Owned Files via a ptrace Logic Bug

by xingwangzhe
Linux
Kernel
Security
Qualys
ptrace
CVE

ssh-keysign-pwn: A six-year-old logic bug in the kernel’s __ptrace_may_access() function, reported by Qualys and patched by Linus Torvalds on May 14, 2026. Unprivileged users can read root-owned files including SSH host private keys and /etc/shadow. All pre-31e62c2ebbfd kernels affected.


Another One

May 2026 has not been kind to the Linux kernel. Dirty Frag, Fragnesia, and now—ssh-keysign-pwn—disclosed by Qualys on May 14 and fixed by Linus Torvalds the same day.

This one is different from the “copy something into page cache” family. It’s a pure logic bug in __ptrace_may_access(), the kernel function that decides whether one process can inspect another.

MetricValue
Reported byQualys Security Advisory
Fixed byLinus Torvalds
Fix commit31e62c2ebbfd
Lurked for~6 years
First flagged byJann Horn (Google), October 2020
PoC published by_SiCk
ImpactRead root-owned files as unprivileged user
Exploit complexity100–2000 spawns per successful steal

How It Works

The bug lives in __ptrace_may_access(). This function is the gatekeeper for process introspection — it checks whether a process is allowed to poke around in another process’s state.

There’s a special case: when task->mm == NULL (the target process has no memory descriptor — happens when a thread is exiting, or for kernel threads), the function skips the dumpable check entirely.

Here’s the code path that makes this exploitable:

  1. A process calls do_exit() to terminate
  2. do_exit() runs exit_mm() first — this tears down the memory descriptor (mm)
  3. Then it runs exit_files() — but the file descriptors are still alive at this point
  4. With task->mm == NULL but file descriptors still open, pidfd_getfd(2) can steal those fds if the caller’s uid matches

Normally, ptrace access checks would block a less-privileged process from reaching into a root process’s open file handles. But the mm == NULL bypass knocks out the dumpable check, and pidfd_getfd(2) does the rest.

This is a textbook TOCTOU race, but the window is wide enough that the PoC hits in 100–2000 attempts.

Exploit Targets

Two tools were published:

sshkeysign_pwn — targets ssh-keysign, a helper binary that signs host authentication challenges. The binary opens SSH host key files (/etc/ssh/ssh_host_{ecdsa,ed25519,rsa}_key, mode 0600) before calling permanently_set_uid() to drop privileges. Then it checks whether EnableSSHKeysign is set in sshd_config — if not, it bails out with the key fds still open. This design has been in OpenSSH since 2002.

chage_pwn — targets chage -l <user>. The binary opens /etc/shadow via spw_open(O_RDONLY), then calls setreuid(ruid, ruid) — dropping all privileges since both arguments are the real uid. In the race window between the privilege drop and the file close, the fd is stealable.


Timeline

DateEvent
~2020Jann Horn identifies the FD-theft pattern and proposes a patch (not merged)
2026-05-14Qualys reports the vulnerability; Linus Torvalds commits the fix (31e62c2ebbfd)
2026-05-14Brad Spengler (@spendergrsec) publishes analysis
2026-05-14_SiCk publishes PoC exploits on GitHub
2026-05-15Qualys sends disclosure to oss-security

The gap from Jann Horn’s 2020 patch proposal to Qualys’s 2026 report is notable. The fix had been drafted five years ago but never made it upstream.


The Fix

Linus committed the fix as commit 31e62c2ebbfd. His commit message noted:

“we have one odd special case: ptrace_may_access() uses ‘dumpable’ to check various other things entirely independently of the MM (typically explicitly using flags like PTRACE_MODE_READ_FSCREDS), including for threads that no longer have a VM (and maybe never did, like most kernel threads). It’s not what this flag was designed for, but it is what it is.”

The patch adjusts ptrace behavior to properly handle the mm == NULL case. It effectively closes the bypass window.

Check Your Kernel

# Check if your kernel has the fix
uname -r
# Fixed kernels have commit 31e62c2ebbfd included
# All kernels before 2026-05-14 are vulnerable

What Makes This One Interesting

Three things stand out:

  1. It was fixed the same day it was reported. Linus turned this around in hours. That’s unusually fast for a kernel fix, suggesting the patch was straightforward once the bug was understood.

  2. Jann Horn found this in 2020. The community had a patch proposal sitting in the mailing list archives for five years. The FD-theft shape was known, but nobody pushed it through.

  3. The targets are ancient. ssh-keysign’s fd-leaving pattern dates to 2002. chage’s spw_open + setreuid sequence is similarly long-standing. This isn’t a bug in SSH or shadow-utils — it’s a kernel bug that weaponizes existing fd-handling patterns in privileged binaries.

This is also the third major Linux kernel vulnerability in May 2026, following Copy Fail and Dirty Frag. The pace of disclosure is accelerating — partly because AI-assisted auditing tools are finding old bugs faster, and partly because the disclosure ecosystem is fragmenting (embargoes being broken, PoCs released before patches).


References

Share this page