Skip to main content

Enumeration

Automation​

We can use tools like LinPEAS to automate enumeration for privilege escalation, but below are manual methods for enumerating the system.


System Information​

uname -a               # Kernel version & system info
cat /etc/os-release # OS version
id # Current user info
whoami # Username
hostname # System hostname

User & Group Enumeration​

who                    # Logged-in users
w # Active sessions
last # Login history
groups # Current user's groups
cat /etc/passwd # List system users
cat /etc/group # List system groups

Sudo & SUID Checks​

sudo -l                                 # Check sudo permissions
find / -perm -4000 -type f 2>/dev/null # Find SUID binaries
find / -perm -2000 -type f 2>/dev/null # Find SGID binaries
cat /etc/sudoers # Sudo configuration

Environment & Path​

env                    # List environment variables
printenv # Another way to list environment variables
echo $PATH # Path variable (check for writable dirs)

Network Information​

ip a                   # Show network interfaces
ifconfig # Same as above
netstat -tulnp # List open ports
ss -tulnp # Another way to list open ports
iptables -L -v -n # Check firewall rules
cat /etc/hosts # Show local hostname resolution

Process & Service Enumeration​

ps aux                               # Running processes
ps -ef | grep root # Processes running as root
systemctl list-units --type=service # List system services

Scheduled Tasks & Cron Jobs​

crontab -l             # Current user's cron jobs
cat /etc/crontab # System-wide cron jobs
ls -la /etc/cron* # Scheduled cron jobs
systemctl list-timers --all # systemd timers (frequently run a job as root)
systemctl cat <name>.service # inspect the paired root-run unit
cat /etc/systemd/system/*.service # unit files: User=, ExecStart=, EnvironmentFile=
info

scheduled jobs are a prime privesc vector. Look for a root timer/oneshot that runs an interpreter on a file another service can write (e.g. ExecStart=/usr/bin/python3 /path/hook.py) - whoever can write that file gets code execution as root on the next tick. Pair this with any write primitive you already have.

File System & Writable Directories​

find / -type f -writable 2>/dev/null   # Find writable files
find / -type d -writable 2>/dev/null # Find writable directories
find / -name ".*" 2>/dev/null # Find hidden files and directories
df -h # Check disk usage
mount # Mounted filesystems

Temporary Directories​

ls -la /tmp            # List /tmp directory
ls -la /var/tmp # List /var/tmp directory
ls -la /dev/shm # List /dev/shm directory

SSH & Credentials​

cat ~/.ssh/id_rsa          # Check for SSH keys
cat /etc/ssh/sshd_config # SSH configuration

Logs & Interesting Files​

cat /var/log/auth.log  # Authentication logs
cat /var/log/syslog # System logs
cat ~/.bash_history # Bash command history
cat ~/.zsh_history # Zsh command history
find / -type f \( -name "*_hist" -o -name "*_history" \) -ls 2>/dev/null # Find history files

Installed Packages & Binaries​

dpkg -l                # List installed packages (Debian-based)
rpm -qa # List installed packages (RedHat-based)
sudo --version # Check sudo version
ls -la /bin /usr/bin /usr/sbin # List installed binaries

Kernel & Exploit Checks​

uname -r                 # Kernel version
lsmod # Loaded kernel modules
cat /proc/version # Kernel and OS details
searchsploit `uname -r` # Search for kernel exploits (requires exploitdb)

Secrets in /proc (Command Line & Environment)​

The /proc/<pid>/ files cmdline and environ are world-readable (unless hidepid is set). Jobs and services frequently pass secrets as arguments - --password, --token, API keys, connection strings - which leak to any user who can read the process.

# Read a running process's command line (NUL-separated -> replace with spaces)
tr '\0' ' ' < /proc/<PID>/cmdline ; echo
tr '\0' ' ' < /proc/<PID>/environ ; echo

# Snapshot sweep: any process exposing a password/token on its command line
for p in /proc/[0-9]*; do
cl=$(tr '\0' ' ' < "$p/cmdline" 2>/dev/null)
case "$cl" in *--password*|*--token*|*--pass*|*secret*) echo "$p: $cl";; esac
done

Short-lived processes (a cron job or a systemd Type=oneshot service) appear for only a moment, so a normal sweep misses them. Poll in a tight loop - a shell for loop over every PID is too slow; use Python or a tiny compiled watcher:

# Watch for a secret-bearing process that appears only briefly
import os, time
end = time.time() + 180
while time.time() < end:
for d in os.listdir('/proc'):
if not d.isdigit():
continue
try:
c = open('/proc/%s/cmdline' % d, 'rb').read()
except OSError:
continue
if b'client' in c: # tune the needle (e.g. b'--user')
print(d, c.replace(b'\x00', b' ').decode('utf-8', 'replace'))

pspy (a ready-made watcher)​

pspy watches process creation without root, which makes it ideal on an unprivileged foothold. Prefer it when you do not want to write your own poller.

wget https://github.com/DominicBreuker/pspy/releases/latest/download/pspy64
chmod +x pspy64
./pspy64 -pf # -p = process events, -f = print FULL command lines
  • -f is mandatory here. Without it pspy prints the executed binary but not its arguments, so a --password <value> shows only as e.g. clickhouse-client. With -f you get the whole command line.
  • -p monitors process executions and runs unprivileged. -i adds inotify file events but normally needs root (CONFIG_PROC_EVENTS); -c enables colour and -d shows created directories.
  • -p still polls, so a process that lives only ~100 ms can fall between iterations. Use the "feed it work" trick below, or flood the queue so the job spawns its secret-bearing command repeatedly and stays on screen.
info

To make a scheduled job fire during your watch window, feed it work if you can (e.g. push an entry into a queue it processes) so the secret-bearing child process actually spawns while you poll.

Related places to check:

ls -la /proc/<PID>/cwd /proc/<PID>/exe   # working dir / binary a service runs from
cat /proc/<PID>/maps | grep -i deleted # deleted-but-open files
unset HISTFILE; # and read other users' history if perms allow
warning

Service hardening changes what file-read primitives can reach: ProtectHome=true blocks /home, /root, /run/user; ProtectSystem=strict makes the rest read-only (but still readable). /proc/<pid>/cmdline itself is usually still readable.