Linux Privilege Escalation — Services & Internals Enumeration
After enumerating the environment and user/group permissions on files, scripts, binaries, and directories, the next step is digging into the internals of the host OS itself. This phase informs most of the privilege escalation attacks covered later, and answers:
- What services/applications are installed, and what’s actually running?
- What sockets are in use?
- What users, admins, and groups exist? Who’s logged in now, and who logged in recently?
- What password policies are enforced? Is the host domain-joined?
- What’s interesting in history, log, and backup files?
- Which files were modified recently/frequently (cron job hijack candidates)?
- Current IP addressing,
/etc/hostsentries, and any interesting network connections? - What offensive-useful tools are already installed (netcat, perl, python, ruby, nmap, tcpdump, gcc)?
- Can we read any user’s
bash_historyfor passwords or other leads? - Are any cron jobs hijackable?
Network Interfaces
rnemeth@htb[/htb]$ ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
2: ens192: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP group default qlen 1000
link/ether 00:50:56:b9:ed:2a brd ff:ff:ff:ff:ff:ff
inet 10.129.203.168/16 brd 10.129.255.255 scope global dynamic ens192
ip a (or ifconfig if net-tools is present) shows the current IP and whether additional interfaces exist that could pivot into a previously-unreachable subnet.
/etc/hosts
rnemeth@htb[/htb]$ cat /etc/hosts
127.0.0.1 localhost
127.0.1.1 nixlpe02
::1 ip6-localhost ip6-loopback
Custom entries here can reveal internal hostnames for other systems in scope.
Users & Sessions
Last Login Per User
rnemeth@htb[/htb]$ lastlog
Username Port From Latest
mrb3n pts/1 10.10.14.15 Tue Aug 2 19:33:16 +0000 2022
cliff.moore pts/0 127.0.0.1 Tue Aug 2 19:32:29 +0000 2022
stacey.jenkins pts/0 10.10.14.15 Tue Aug 2 18:29:15 +0000 2022
htb-student pts/0 10.10.14.15 Wed Aug 3 13:37:22 +0000 2022
lastlog shows how frequently/widely used the box is, which hints at how likely misconfigurations or “messy” home directories/histories are.
Currently Logged-In Users
rnemeth@htb[/htb]$ w
12:27:21 up 1 day, 16:55, 1 user, load average: 0.00, 0.00, 0.00
USER TTY FROM LOGIN@ IDLE JCPU PCPU WHAT
cliff.mo pts/0 10.10.14.16 Tue19 40:54m 0.02s 0.02s -bash
who and (where available) finger provide similar information.
Command History
rnemeth@htb[/htb]$ history
1 id
2 cd /home/cliff.moore
3 exit
4 touch backup.sh
5 tail /var/log/apache2/error.log
6 ssh ec2-user@dmz02.inlanefreight.local
7 history
Users frequently pass passwords/tokens as CLI arguments, reference internal hosts, or leave clues about cron jobs and git repos in their history — always worth a manual read-through.
Other History Files
rnemeth@htb[/htb]$ find / -type f \( -name *_hist -o -name *_history \) -exec ls -l {} \; 2>/dev/null
-rw------- 1 htb-student htb-student 387 Nov 27 14:02 /home/htb-student/.bash_history
Some monitoring scripts/programs create their own history files outside the standard shell history filenames — worth a broader search.
Cron Jobs
rnemeth@htb[/htb]$ ls -la /etc/cron.daily/
total 48
drwxr-xr-x 2 root root 4096 Aug 2 17:36 .
-rwxr-xr-x 1 root root 376 Dec 4 2019 apport
-rwxr-xr-x 1 root root 1478 Apr 9 2020 apt-compat
...
Cron jobs are the Linux analog to Windows scheduled tasks and are frequently used for maintenance/backups. Combined with relative paths or weak file permissions, a cron job that runs as a privileged user is a classic privesc vector (hijack the script it calls, or a binary in its $PATH).
/proc Filesystem
/proc (procfs) is a virtual filesystem, dynamically generated by the kernel, exposing process state, hardware info, and kernel parameters. It’s the primary way to inspect running processes and can also be used to tweak scheduling/priority/memory settings.
rnemeth@htb[/htb]$ find /proc -name cmdline -exec cat {} \; 2>/dev/null | tr " " "\n"
...SNIP...
root@10.129.14.200ssh
root@10.129.14.200sshd:
htb-student
[priv]
Reading every process’s cmdline can reveal credentials or connection details passed as command-line arguments (SSH sessions, sudo invocations, scripts run with secrets inline) — this only works for processes still running at the time of the read.
Services
Installed Packages
rnemeth@htb[/htb]$ apt list --installed | tr "/" " " | cut -d" " -f1,3 | sed 's/[0-9]://g' | tee -a installed_pkgs.list
accountsservice 0.6.55-0ubuntu12~20.04.5
acl 2.2.53-6
...SNIP...
Older distros (and sometimes current ones with stale packages) frequently carry at least one vulnerable package. Build this list first — it feeds the GTFOBins comparison below.
Sudo Version
rnemeth@htb[/htb]$ sudo -V
Sudo version 1.8.31
Sudoers policy plugin version 1.8.31
Sudoers file grammar version 46
Sudoers I/O plugin version 1.8.31
Check the installed sudo version against known CVEs (e.g., CVE-2021-3156 “Baron Samedit”, CVE-2019-14287).
Binaries
rnemeth@htb[/htb]$ ls -l /bin /usr/bin/ /usr/sbin/
Some systems run compiled programs directly with no package manager entry at all — worth listing binary directories even if package enumeration comes up short.
GTFOBins Cross-Reference
rnemeth@htb[/htb]$ for i in $(curl -s https://gtfobins.org/api.json | jq -r '.executables | keys[]'); do if grep -q "$i" installed_pkgs.list; then echo "Check for GTFO: $i"; fi; done
Check for GTFO: ab
Check for GTFO: apt
Check for GTFO: ar
Check for GTFO: bash
Check for GTFO: busybox
Check for GTFO: curl
...SNIP...
Cross-references the installed package list against every binary in the GTFOBins database. Anything that matches is worth manually checking on GTFOBins for a SUID/sudo/capability-based privesc technique.
strace — System Call Tracing
rnemeth@htb[/htb]$ strace ping -c1 10.129.112.20
execve("/usr/bin/ping", ["ping", "-c1", "10.129.112.20"], 0x7ffdc8b96cc0 /* 80 vars */) = 0
...SNIP...
socket(AF_INET, SOCK_DGRAM, IPPROTO_ICMP) = 3
connect(5, {sa_family=AF_INET, sin_port=htons(1025), sin_addr=inet_addr("10.129.112.20")}, 16) = 0
sendto(3, "\10\0\31\303\0\0\0\1eX\327c..." , 64, 0, {sa_family=AF_INET, ...}, 16) = 64
...SNIP...
exit_group(0) = ?
+++ exited with 0 +++
strace traces system calls and signal handling for a process, showing exactly how it touches files, sockets, and other resources. Output can be redirected to a file for later review. Useful for watching a SUID binary or cron script to see if it reads a world-writable config, calls another binary without a full path, or sends credentials/tokens to a remote host.
Configuration Files
rnemeth@htb[/htb]$ find / -type f \( -name *.conf -o -name *.config \) -exec ls -l {} \; 2>/dev/null
-rw-r--r-- 1 root root 448 Nov 28 12:31 /run/tmpfiles.d/static-nodes.conf
-rw-r--r-- 1 systemd-resolve systemd-resolve 736 Nov 28 12:31 /run/systemd/resolve/stub-resolv.conf
...SNIP...
World-readable config files can reveal service setup, secrets (keys, paths), or the layout of directories the current user can’t otherwise list. Key point: if a file itself is world-readable, it can still be read even when the containing directory isn’t.
Scripts
rnemeth@htb[/htb]$ find / -type f -name "*.sh" 2>/dev/null | grep -v "src\|snap\|share"
/home/htb-student/automation.sh
/etc/wpa_supplicant/action_wpa.sh
/etc/init.d/keyboard-setup.sh
...SNIP...
Admin-authored scripts often reveal internal processes even without exploitable permissions on the script itself — and sometimes the permissions are exploitable (world-writable script called by root via cron/systemd).
Running Services by User
rnemeth@htb[/htb]$ ps aux | grep root
root 1 2.0 0.2 168196 11364 ? Ss 12:31 0:01 /sbin/init splash
root 778 0.0 0.0 18052 2992 ? Ss 12:31 0:00 /usr/sbin/cron -f
root 784 0.4 0.5 273512 21680 ? Ssl 12:31 0:00 /usr/sbin/NetworkManager --no-daemon
root 1257 0.0 0.2 250528 9388 ? Ssl 12:31 0:00 /usr/lib/packagekit/packagekitd
The process list shows which scripts/binaries are running and as which user — a script in an unrestricted admin-writable path being run as root is directly abusable without needing root to execute it yourself.
Takeaways
- Build the installed-package list early — it’s the input for the GTFOBins one-liner and for CVE lookups against package/sudo versions.
/proc/*/cmdlineandstraceboth capture in-flight secrets (arguments, socket data) that static file enumeration will never show.- History files (
.bash_historyand custom*_hist/*_historyfiles) and cron jobs are consistently high-value: real engagements leak credentials and hijackable scheduled tasks here more often than through binary exploitation. - World-readable files remain readable even inside a directory you can’t list — don’t skip a
find-based sweep for.conf/.config/.shfiles just becauselson the parent directory fails.