Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

🏠 Back to Blog

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/hosts entries, 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_history for 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/*/cmdline and strace both capture in-flight secrets (arguments, socket data) that static file enumeration will never show.
  • History files (.bash_history and custom *_hist/*_history files) 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/.sh files just because ls on the parent directory fails.