Cuando sospechas que un servidor ha sido comprometido, el orden en el que actúas importa: si reinicias o limpias antes de mirar, pierdes las evidencias que explican cómo entró el atacante. Este playbook recoge los pasos que debes seguir en un servidor Ubuntu 24.04 (los comandos valen también para Debian 12) desde la sospecha inicial hasta la recuperación: registrar el caso, capturar el estado en memoria, aislar la máquina, buscar mecanismos de persistencia, reconstruir y endurecer. Puedes copiarlo a tu wiki interna y adaptarlo a tu equipo.
Requisitos previos
- Acceso
sudoal servidor afectado y, a ser posible, acceso por consola fuera de banda (por ejemplo, la consola VNC del panel de CubePath) por si pierdes el SSH durante la contención. - Una máquina de análisis separada (tu equipo o un servidor limpio) con espacio para guardar las evidencias. Nunca guardes las evidencias solo en el servidor comprometido.
- Tu IP de administración (
your_admin_ip) para no bloquearte al aislar el servidor. - Copias de seguridad recientes y probadas de los datos del servidor.
Importantesi el incidente puede implicar datos personales o tiene consecuencias legales, avisa a tu responsable de seguridad antes de tocar nada. Puede que haya que preservar el disco completo para un análisis forense profesional.
Paso 1: Registrar el caso y clasificar la severidad
Antes de ejecutar ningún comando, abre un registro del incidente (un ticket, un documento compartido) y anota la hora de cada acción. Ese registro será la base del informe posterior. Usa una escala sencilla:
| Severidad | Criterio | Ejemplo |
|---|---|---|
| P1 | Compromiso confirmado en producción o datos expuestos | Webshell activa, usuario root desconocido |
| P2 | Acceso no autorizado probable | Login SSH desde una IP y país inesperados |
| P3 | Actividad sospechosa sin acceso confirmado | Proceso minando criptomonedas lanzado por www-data |
| P4 | Alerta aislada | Escaneo de puertos o fuerza bruta bloqueada |
Anota como mínimo: servidor afectado, quién lo detectó y cómo, primera evidencia, severidad y quién coordina la respuesta.
Paso 2: Recoger evidencias volátiles
Procesos, conexiones y sesiones desaparecen al reiniciar o aislar el servidor, así que se capturan primero. Para no escribir en el disco del servidor comprometido, guarda la salida en /dev/shm, que está en memoria. Define el nombre del caso:
CASO="INC-$(date +%Y%m%d)-$(hostname -s)"
sudo mkdir -p "/dev/shm/$CASO"
Captura el estado del sistema. Cada comando escribe un archivo separado:
date -u | sudo tee "/dev/shm/$CASO/00-hora-utc.txt"
sudo ps auxwwf | sudo tee "/dev/shm/$CASO/procesos.txt" > /dev/null
sudo ss -tunap | sudo tee "/dev/shm/$CASO/conexiones.txt" > /dev/null
sudo ss -tulpn | sudo tee "/dev/shm/$CASO/puertos-escucha.txt" > /dev/null
sudo lsof -nP | sudo tee "/dev/shm/$CASO/archivos-abiertos.txt" > /dev/null
w | sudo tee "/dev/shm/$CASO/sesiones.txt" > /dev/null
sudo last -Fwx | sudo tee "/dev/shm/$CASO/logins.txt" > /dev/null
ip addr | sudo tee "/dev/shm/$CASO/ip-addr.txt" > /dev/null
ip route | sudo tee "/dev/shm/$CASO/ip-route.txt" > /dev/null
sudo lsmod | sudo tee "/dev/shm/$CASO/modulos-kernel.txt" > /dev/null
Si ves un proceso sospechoso, guarda su binario y su línea de comandos antes de detenerlo. Aunque el atacante haya borrado el archivo del disco, sigue accesible a través de /proc mientras el proceso viva. Sustituye 1234 por su PID:
sudo cp /proc/1234/exe "/dev/shm/$CASO/pid-1234.bin"
sudo tr '\0' ' ' < /proc/1234/cmdline | sudo tee "/dev/shm/$CASO/pid-1234-cmdline.txt"
sudo ls -l /proc/1234/cwd | sudo tee "/dev/shm/$CASO/pid-1234-cwd.txt"
Copia los logs antes de que roten o de que el atacante los borre:
sudo journalctl --no-pager -o short-iso | sudo tee "/dev/shm/$CASO/journal.txt" > /dev/null
sudo cp /var/log/auth.log* /var/log/syslog* "/dev/shm/$CASO/"
Empaqueta las evidencias, calcula su hash y envíalas a tu máquina de análisis por SSH. El archivo nunca toca el disco del servidor:
sudo tar -C /dev/shm -czf - "$CASO" | ssh analyst@your_analysis_host "cat > $CASO.tar.gz"
En la máquina de análisis, calcula el hash y anótalo en el registro del caso. Así podrás demostrar después que las evidencias no se han modificado:
sha256sum INC-*.tar.gz
3f1c9a...e27b INC-20260925-web-01.tar.gz
Si tu proveedor permite hacer un snapshot del disco, hazlo ahora: es la mejor copia del estado del sistema para analizar con calma sin tocar el original.
Paso 3: Contener el incidente
El objetivo es cortar la actividad del atacante sin destruir evidencias. Primero congela los procesos maliciosos en lugar de matarlos, así se conserva su memoria para un análisis posterior:
sudo kill -STOP 1234
Cierra las sesiones de cuentas sospechosas y bloquea su contraseña:
sudo pkill -KILL -u usuario_sospechoso
sudo usermod -L -e 1 usuario_sospechoso
Aísla el servidor en la red dejando solo tu IP de administración por SSH. Añade primero la regla de administración para no perder el acceso:
sudo ufw allow from your_admin_ip to any port 22 proto tcp
sudo ufw default deny incoming
sudo ufw default deny outgoing
sudo ufw enable
Revisa las reglas que quedan activas y elimina las que abran otros servicios (sudo ufw delete <número>):
sudo ufw status numbered
To Action From
-- ------ ----
[ 1] 22/tcp ALLOW IN 203.0.113.5
UFW es stateful, así que tu sesión SSH actual sigue funcionando aunque el tráfico saliente nuevo quede bloqueado. Comprueba que el servidor ya no puede abrir conexiones hacia fuera:
curl -m 5 -sI https://example.com || echo "salida bloqueada"
salida bloqueada
Si el servidor forma parte de un clúster o usa claves y tokens compartidos (claves de API, credenciales de base de datos, claves SSH de despliegue), empieza ya a rotarlos desde un equipo limpio: da por hecho que el atacante los ha leído.
Paso 4: Buscar el acceso inicial y la persistencia
Con el servidor aislado, busca cómo entró el atacante y qué dejó para volver. Revisa primero los accesos SSH aceptados, agrupados por usuario e IP de origen. zgrep lee también los logs rotados y comprimidos:
sudo zgrep -h "Accepted" /var/log/auth.log* | awk '{print $7, $9}' | sort | uniq -c | sort -rn
42 deploy 203.0.113.5
3 root 198.51.100.77
Cualquier usuario o IP que no reconozcas marca el punto de partida de la investigación.
Busca cuentas con UID 0 además de root y usuarios creados recientemente:
sudo awk -F: '$3 == 0 {print $1}' /etc/passwd
sudo zgrep -hE "useradd|usermod|groupadd" /var/log/auth.log*
root
Revisa todas las claves SSH autorizadas del sistema:
sudo find / -xdev -name authorized_keys -exec ls -l {} \; -exec cat {} \;
Los atacantes suelen persistir con tareas programadas y servicios de systemd. Lista los crontabs de todos los usuarios y los timers y unidades añadidos a mano:
sudo ls -la /var/spool/cron/crontabs/ /etc/cron.d/
sudo systemctl list-timers --all
sudo find /etc/systemd/system /usr/lib/systemd/system -type f -mtime -30 \( -name "*.service" -o -name "*.timer" \)
Comprueba /etc/ld.so.preload, que permite cargar una librería en todos los procesos y es un truco habitual de rootkits en espacio de usuario. En un sistema limpio no existe:
ls -l /etc/ld.so.preload
ls: cannot access '/etc/ld.so.preload': No such file or directory
Verifica la integridad de los archivos instalados por paquetes. dpkg --verify compara cada archivo con el checksum registrado al instalarlo; una 5 en la tercera columna indica que el contenido ha cambiado:
sudo dpkg --verify
??5?????? /usr/bin/ssh
Los archivos de configuración (marcados con c) cambian legítimamente; los binarios de /usr/bin o /usr/sbin no deberían aparecer nunca. Por último, busca archivos modificados en los últimos días fuera de los directorios del sistema virtual y archivos PHP sospechosos en la raíz web:
sudo find / -xdev -type f -mtime -3 -not -path "/proc/*" -not -path "/var/log/*" -not -path "/var/lib/*" 2>/dev/null | head -100
sudo grep -rlE "eval\(base64_decode|gzinflate\(|shell_exec\(" /var/www 2>/dev/null
Anota cada hallazgo con su ruta, fecha de modificación (stat archivo) y hash en el registro del caso.
Paso 5: Erradicar y recuperar
Un servidor en el que el atacante ha tenido privilegios de root no se puede considerar limpio aunque borres todo lo que has encontrado. Para incidentes P1 y P2 la única recuperación fiable es reconstruir:
- Despliega un servidor nuevo desde una imagen limpia del sistema operativo.
- Aplica las actualizaciones:
sudo apt update && sudo apt full-upgrade. - Reinstala las aplicaciones desde tu repositorio de código o gestión de configuración, no copiando binarios del servidor comprometido.
- Restaura los datos desde una copia de seguridad anterior al compromiso y revísalos (por ejemplo, busca webshells en los archivos subidos por usuarios).
- Usa credenciales nuevas: contraseñas de base de datos, claves de API, claves SSH de usuarios y tokens de despliegue.
- Corrige el vector de entrada identificado en el paso 4 (plugin vulnerable, contraseña débil, servicio expuesto) antes de publicar el servidor.
Cambia el tráfico al servidor nuevo y conserva el comprometido apagado, o su snapshot, hasta cerrar la investigación.
Para incidentes P3 y P4 sin escalada a root (por ejemplo, un proceso de www-data), puede bastar con eliminar los archivos maliciosos, actualizar la aplicación vulnerable y rotar las credenciales de esa aplicación.
Paso 6: Mejorar la detección para el próximo incidente
Deja en el servidor nuevo un registro de auditoría que te habría ayudado en esta investigación. Instala auditd:
sudo apt install auditd
sudo systemctl enable --now auditd
Crea un archivo de reglas que vigile cambios en cuentas, sudo y claves SSH, y los comandos ejecutados como root:
sudo nano /etc/audit/rules.d/50-incidentes.rules
-w /etc/passwd -p wa -k cuentas
-w /etc/shadow -p wa -k cuentas
-w /etc/group -p wa -k cuentas
-w /etc/sudoers -p wa -k sudo
-w /etc/sudoers.d/ -p wa -k sudo
-w /etc/ssh/sshd_config -p wa -k ssh
-w /root/.ssh/ -p wa -k ssh
-w /etc/cron.d/ -p wa -k cron
-w /etc/systemd/system/ -p wa -k systemd
-a always,exit -F arch=b64 -S execve -F euid=0 -F auid>=1000 -F auid!=unset -k comandos_root
Carga las reglas y comprueba que están activas:
sudo augenrules --load
sudo auditctl -l | head
Genera un evento de prueba y búscalo:
sudo touch /etc/sudoers.d/prueba && sudo rm /etc/sudoers.d/prueba
sudo ausearch -k sudo -ts recent
La salida debe mostrar dos eventos con key="sudo". Envía también los logs a un servidor syslog centralizado: un atacante con root puede borrar los logs locales, pero no los que ya han salido de la máquina.
Cerrar el caso
Termina con una revisión sin culpables en la que el registro del paso 1 se convierte en informe: línea temporal, vector de entrada, datos afectados, qué funcionó en la respuesta, qué no, y acciones de mejora con responsable y fecha.
Conclusión
Has seguido un playbook completo: registrar el caso, capturar evidencias volátiles sin escribir en el disco comprometido, aislar el servidor sin perder el acceso, buscar la entrada y la persistencia, reconstruir desde una imagen limpia y añadir auditoría para el futuro. Como siguientes pasos, ensaya el playbook en un servidor de pruebas para medir cuánto tardas, automatiza el paso 2 en un script revisado que tengas listo antes de necesitarlo y configura alertas sobre las claves de auditd que acabas de definir.
