Post-Exploitation Enumeration: Know the Box
Enumeration is the first thing you do the moment you land on a system, before you reach for a single exploit. You might have arrived through a critical bug that dropped you straight to root, or through a low-privilege account that can barely run commands. Either way, real engagements don't stop at "I got a shell." Unlike a CTF, the job is only starting: enumeration matters just as much after you're in as it did before. This lesson is the checklist you run on every fresh foothold.
The pattern is always the same, learn three things: what the system is, who you are on it, and what the network around it looks like.
Read the system
hostname , the machine's name. It's often meaningless (ubuntu-3487340239), but sometimes it leaks a role in the network, SQL-PROD-01, dc01, web-dmz, which tells you what the box is for.
uname -a , full system and kernel information. This is your first stop for kernel-exploit hunting: an old kernel can have a public local-root bug.
cat /proc/version , the kernel version again, plus extras like which compiler built it (a GCC on the box means you can compile an exploit locally). The /proc filesystem (procfs) exposes live info about the system and its processes and exists on virtually every Linux flavour.
cat /etc/issue and cat /etc/os-release , identify the distribution and version. /etc/issue is a pre-login banner that can be customised, so cross-check it; os-release is the more reliable one. Any file that describes the system can be edited, so read several and compare.
Processes: ps
ps (process status) shows what's running. Its output columns:
| Column | Meaning |
|---|---|
| PID | unique process ID |
| TTY | the terminal the process is attached to |
| TIME | CPU time used (not wall-clock runtime) |
| CMD | the executable (bare ps hides arguments) |
The two you'll actually use:
ps aux # every process, all users, with the owning user
ps axjf # the same as a process TREE (parent -> child)
ps aux breaks down as a (all users), u (show who launched each one), x (include processes with no controlling terminal). Scan it for services running as root, database daemons, or something custom, each is a potential escalation target.
Read yourself
whoami and id , who you are and, crucially, your groups. Membership in sudo, docker, adm, lxd, or disk is frequently a direct road to root. id also works on other users: id bob.
sudo -l , what you may run as root. The single highest-value command on any box; read it carefully (see the privilege escalation lesson for how to decode each line).
env , environment variables. Watch the PATH: if it includes writable directories, or a language/compiler is available, that can become an escalation path.
history (and cat ~/.bash_history) , earlier commands. People paste passwords, tokens, and connection strings straight into the shell; occasionally it's all sitting right there.
Listing files properly: ls -la
Always enumerate with ls -la, never bare ls. The -a reveals dotfiles (.ssh, .env, .bash_history, .git) and -l shows owner, group, and permission bits. A juicy secret.txt hidden as a dotfile is invisible to ls or ls -l and obvious with ls -la.
The user database: /etc/passwd
/etc/passwd is world-readable and lists every account, an instant enumeration win:
cat /etc/passwd # all accounts
grep -vE 'nologin|false' /etc/passwd # accounts with a real login shell
cat /etc/passwd | cut -d: -f1 # just the usernames (feed a brute-force list)
Most entries are system/service users with no shell; filter to the ones with /home directories and real shells, those are the humans. The password hashes are not here; they live in root-only /etc/shadow.
Read the network
A compromised box is often a pivot into networks you can't otherwise reach.
Interfaces: ip a (modern) or ifconfig (classic). Multiple interfaces (eth0, tun0, tun1) mean the host straddles several networks, some you may only reach through this box.
Routes: ip route (or route -n) shows which networks the box knows how to reach, your map for pivoting.
Connections and listeners: ss is the modern tool; netstat is the classic (identical flags). The one to memorise:
ss -tlnp # TCP, Listening, Numeric, with Program/PID
netstat -tlnp # same thing, older tool
Useful flag combinations (they work on both ss and netstat):
-a, all sockets (listening and established).-t/-u, TCP only / UDP only.-l, only listening ports (open and waiting for connections).-n, numeric, don't waste time resolving names.-p, show the program/PID behind each socket. This column is empty unless the process is yours or you're root, another reason to escalate.netstat -ano(all, numeric, owning-PID/timers) is the combo you'll see most in write-ups.
A service listening only on 127.0.0.1 is invisible from outside but reachable from your shell, an internal-only admin panel or database you can now attack.
find: the enumeration workhorse
find sweeps the filesystem for exactly the things privesc depends on. Send errors to /dev/null to keep the output readable, find complains loudly about directories it can't enter.
Locate files:
find / -name 'flag.txt' 2>/dev/null # by name, anywhere
find /home -user frank 2>/dev/null # everything owned by frank
find / -mtime -1 2>/dev/null # modified in the last day
find / -size +100M 2>/dev/null # larger than 100 MB
Writable or executable places (where you can drop tools or hijack a script):
find / -writable -type d 2>/dev/null # world-writable directories
find / -perm -222 -type d 2>/dev/null # same, via the permission bits
Interpreters and compilers (for building or running an exploit):
find / -name 'python*' 2>/dev/null
find / -name 'perl*' 2>/dev/null
find / -name 'gcc*' 2>/dev/null
The big one, SUID binaries (run as their owner, so a root-owned one runs as root):
find / -perm -u=s -type f 2>/dev/null # SUID bit set (same as -perm -4000)
find / -perm -g=s -type f 2>/dev/null # SGID bit set
An SUID binary that shouldn't be one, an editor, find, nmap, python, a custom script, is a classic ticket to root, which the GTFOBins lesson turns into a shell.
Try it (terminal)
Run the checklist on the box beside you and read what comes back:
hostname,uname -a,cat /etc/os-release, what is this machine?idandsudo -l, what are you allowed to do?ps aux, anything interesting running as root?ip aandss -tlnp, any extra networks or internal-only services?find / -perm -u=s -type f 2>/dev/null, spot the odd SUID binary.ls -laandcat /etc/passwd, hidden files and the user list.
Common questions
"That's a lot of commands. Do I really run all of them by hand?"
When you're learning, yes, so you understand what each one tells you. On a real box you'll run LinPEAS to fire all these checks at once. But a tool's output is noise until you know what sudo -l or a stray SUID binary means, which is why you drill the manual checks first.
"What am I actually looking for in all this output?"
Anything that connects you to root: a command you can run as root (sudo -l), a file root trusts that you can write, a binary that runs as its owner (SUID), a password someone left behind. You're not reading for fun, you're hunting for one thread to pull.
"netstat isn't installed on this box. Now what?"
Use ss, it's the modern replacement and ships on almost every current Linux. The flags are the same (ss -tlnp). Minimal boxes sometimes strip old tools, so knowing both saves you when one is missing.
"Why bother with the network stuff if I just want root on this one box?"
Because the engagement rarely ends at one box. Extra interfaces and internal-only listeners (127.0.0.1 services) are your map to the next target, the whole reason a single foothold can unravel a network.
Why this matters
Escalation almost never comes from a flash of genius, it comes from reading the box carefully. Nine times out of ten the way up is already sitting in the output of sudo -l, find / -perm -u=s, or a config someone forgot to lock down. Enumeration is the boring, patient work that every root shell is built on, and it's exactly what a defender is trying to give you nothing to find.