Ungated /proc/<pid>/file and /exe readlink discloses every process's full executable path (root daemons included) to unprivileged users
Summary
The Pfile target is registered with pt_valid == NULL (unlike regs/map/etype) and procfs_readlink's Pfile case performs only pfs_pfind + a p_ucred NULL check - no uid check, no CHECKIO/p_trespass, no prison check, no ps_showallprocs gate; readlink(2) applies no mode check on VLNK. Any user reads the authoritative executable path of every process. procfs_getattr's Pfile case leaks the same via size. The kernel's own comment (procfs_mem.c:212-219) admits this 'exposes an information leak that shouldn't happen'. cmdline is gated by exactly the CHECKIO/p_trespass policy this path lacks (procfs_status.c:203-205), and the KERN_PROC argv sysctl is uid-gated - so this exceeds what ps reveals by default, with no sysctl knob. Sibling of DF-0921 (map). VERIFIED guest uid 1001: readlink /proc/1/file -> /sbin/init; sweep of uid-0 pids -> dhclient, devd, syslogd, hammer2, sshd. Fix: apply the cmdline policy to both Pfile paths (CHECKIO/p_trespass else 'unknown').
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-3081 Β· 7 files| File | Type | Description | Size | |
|---|---|---|---|---|
| procfs_file_leak.sh | β | 1.5 KB | view raw | |
| run.sh | β | 90 B | view raw | |
| run.log | β | 491 B | view raw | |
| env.txt | β | 272 B | view raw | |
| fix.diff | β | 943 B | view raw | |
| VERDICT.md | β | 2.0 KB | β raw | |
| verdict.json | β | 2.2 KB | view raw |
DF-3081 β VERDICT
Status: reproduced (deterministic; guest stayed up).
Impact: leak β unprivileged disclosure of every process's executable
path (root daemons included) via /proc/<pid>/file and /proc/<pid>/exe.
How it reproduces
- Resolving
/proc/710/filewalksprocfs_lookupPproc for pid 710; withsecurity.ps_showallprocs=1(default) the directory resolves for any user (sys/vfs/procfs/procfs_vnops.c:830-835). - The
Pfiletarget haspt_valid == NULL(sys/vfs/procfs/procfs_vnops.c:147-148) so the per-target loop at :837-842 always matchesfile/exeβ no validity filter. readlink(2)performs no mode check on VLNK;procfs_readlink's Pfile case (sys/vfs/procfs/procfs_vnops.c:1128-1162) runspfs_pfind, checks onlyp_ucred != NULL, thencache_fullpath()onp_textnchanduiomoves the full path out. No CHECKIO / p_trespass / prison / showallprocs gate anywhere on this path (contrastprocfs_docmdline, procfs_status.c:203-205, which gates argv reads exactly this way).- Observed as uid 1001:
/sbin/init,/sbin/dhclient,/sbin/devd,/usr/sbin/syslogd,/sbin/hammer2,/usr/sbin/sshd(run.log).
The in-tree comment at sys/vfs/procfs/procfs_mem.c:212-219 acknowledges
"the /proc/pid/file object exposes an information leak that shouldn't
happen" β the pt_valid == NULL registration keeps it live.
Why Low (not higher)
Information-only: path strings, no memory contents. ps already exposes
comm names and (with kern.ps_argsopen=1, as on this guest) full argv;
the delta is the authoritative binary path for every process regardless
of that sysctl. Valuable for recon (admin tooling paths, home-dir
binaries), consistent with Low/C:L.
Fix validation
Not rebuilt (Low info-leak finding). fix.diff mirrors the docmdline
policy: CHECKIO(curproc, procp) || p_trespass(...) fails β emit
"unknown" (readlink) / size of "unknown" (getattr). Root and same-uid
debuggers keep full resolution.
fix_status: not_testable (authored only).
Fix verification
not_testablefix.diff authored against read-only sys/ tree; kernel not rebuilt (Low info-leak finding).
['fix.diff']
Confirmed kernel references
Detail
Evidence (decisive lines)
['run.log: readlink /proc/1/file -> /sbin/init; uid-0 sweep yields /sbin/dhclient, /sbin/devd, /usr/sbin/syslogd, /sbin/hammer2, /usr/sbin/sshd']
PoC changes
Written fresh; no seed existed.
Verified recommended fix
Gate Pfile readlink/getattr with CHECKIO+p_trespass (same policy as procfs_docmdline), falling back to 'unknown' for processes the caller may not examine.
Verdict
procfs_readlink's Pfile case (vnops.c:1128-1162) and procfs_getattr's Pfile case (:642-665) return the full executable path of any process with no authorization gate β pt_valid is NULL for Pfile (:147-148), and the readlink path performs only pfs_pfind + p_ucred NULL check (no uid/CHECKIO/p_trespass/prison/showallprocs). Reproduced as uid 1001: readlink of /proc/1/file, /proc/390/file, /proc/466/file, /proc/68/file, /proc/710/file yields /sbin/init, /sbin/devd, /usr/sbin/syslogd, /sbin/hammer2, /usr/sbin/sshd. The in-tree comment at procfs_mem.c:212-219 admits this leak 'shouldn't happen'. Info-only impact: path disclosure of every binary on the system (more than ps reveals by default). Sibling of DF-0921 (map).
No comments yet.