Un servidor lento, con la CPU al 100 % o con miles de conexiones abiertas puede estar sufriendo un ataque, o puede que ya esté comprometido. En esta guía seguirás un procedimiento ordenado para averiguarlo en Ubuntu 24.04: primero la red y los accesos, después los procesos, la persistencia y los ficheros, y por último la contención y la prevención. Todos los comandos son de solo lectura hasta llegar al paso de contención.
Requisitos previos
- Un servidor con Ubuntu 24.04 LTS, por ejemplo un VPS de CubePath. En Debian 12 los comandos son los mismos.
- Un usuario no root con privilegios
sudo. - Saber qué es "normal" en tu servidor (servicios, puertos y usuarios esperados). Sin esa referencia es difícil distinguir un ataque de un pico de tráfico legítimo.
Importantesi sospechas que un atacante tiene acceso root, no confíes al 100 % en las herramientas del propio sistema, ya que pueden haber sido sustituidas. Recoge evidencias, aísla el servidor y valora reinstalarlo desde una copia de seguridad limpia.
Paso 1: Instalar las herramientas de diagnóstico
La mayoría de comandos de esta guía vienen con Ubuntu, pero algunos (tráfico por conexión, estadísticas históricas, captura de paquetes) necesitan paquetes adicionales:
sudo apt update
sudo apt install sysstat iftop tcpdump lsof debsums
Averigua también el nombre de tu interfaz de red pública, porque lo usarás en varios comandos:
ip -br addr
lo UNKNOWN 127.0.0.1/8 ::1/128
eth0 UP 203.0.113.25/24 2001:db8::25/64
En los ejemplos se usa eth0. Sustitúyelo por el nombre que veas (por ejemplo ens18).
Paso 2: Hacer una evaluación rápida
Antes de profundizar, estos comandos te dan una foto del estado del servidor en menos de un minuto. Carga del sistema y usuarios conectados:
uptime
w
10:42:01 up 12 days, 3:10, 1 user, load average: 7.85, 6.20, 3.11
USER TTY FROM LOGIN@ IDLE JCPU PCPU WHAT
admin pts/0 198.51.100.7 10:40 0.00s 0.03s 0.00s w
Resumen de sockets y procesos que más CPU y memoria consumen:
ss -s
ps -eo pid,user,%cpu,%mem,etime,cmd --sort=-%cpu | head -10
Fíjate en tres cosas: una carga muy superior al número de núcleos (nproc), un número de conexiones TCP muy por encima de lo habitual y procesos que no reconoces o que se ejecutan con un usuario inesperado. Cada una te lleva a uno de los pasos siguientes.
Paso 3: Analizar las conexiones de red
Conexiones por IP de origen
Un ataque de denegación de servicio a nivel de aplicación suele verse como muchas conexiones desde pocas IP. Este comando cuenta las conexiones establecidas por IP remota:
ss -Htn state established | awk '{print $4}' | sed 's/:[0-9]*$//' | sort | uniq -c | sort -rn | head -15
842 198.51.100.44
611 198.51.100.45
12 203.0.113.80
3 192.0.2.10
Cientos de conexiones desde una sola IP hacia un servidor web pequeño no son normales. Si las conexiones vienen de miles de IP distintas con pocas conexiones cada una, se trata de un ataque distribuido y bloquear IP una a una no servirá.
Inundación SYN
En un ataque SYN flood el servidor acumula conexiones a medio abrir en estado SYN-RECV:
ss -Htn state syn-recv | wc -l
Unas pocas son normales; cientos o miles de forma sostenida indican un ataque. Comprueba que las SYN cookies están activas (en Ubuntu lo están por defecto), porque permiten seguir aceptando conexiones legítimas durante la inundación:
sysctl net.ipv4.tcp_syncookies
net.ipv4.tcp_syncookies = 1
Ancho de banda y paquetes
Para ver en tiempo real qué conexiones consumen más ancho de banda, usa iftop sin resolución de nombres (-n) y mostrando puertos (-P). Pulsa q para salir:
sudo iftop -i eth0 -nP
Para ver el tráfico por segundo de la interfaz, usa sar (del paquete sysstat), que muestra 5 muestras de 1 segundo:
sar -n DEV 1 5
Si sospechas de un SYN flood, captura 2000 paquetes SYN y cuenta sus orígenes con tcpdump:
sudo tcpdump -ni eth0 -c 2000 'tcp[tcpflags] & tcp-syn != 0 and tcp[tcpflags] & tcp-ack == 0' 2>/dev/null \
| awk '{print $3}' | cut -d. -f1-4 | sort | uniq -c | sort -rn | head -10
Si el volumen de tráfico satura el enlace del servidor, ninguna regla local lo va a solucionar, porque los paquetes ya han consumido el ancho de banda al llegar. En ese caso la mitigación tiene que hacerse antes de que el tráfico llegue a tu servidor: contacta con el soporte de CubePath con las IP de destino, los puertos y la hora de inicio del ataque.
Tráfico saliente
Tu servidor también puede ser el origen del ataque si está comprometido y forma parte de una botnet. Revisa las conexiones salientes y qué proceso las abre:
sudo ss -tunp state established | grep -v -E ':22 |:80 |:443 ' | head -30
Conexiones a puertos como 3333, 4444 o 14444 suelen corresponder a pools de minería de criptomonedas.
Paso 4: Detectar ataques de fuerza bruta contra SSH
Los intentos de login fallidos se registran en /var/log/auth.log. Este comando cuenta los intentos fallidos por IP de origen, incluidos los de usuarios que no existen:
sudo grep -hE 'Failed password|Invalid user' /var/log/auth.log \
| grep -oE 'from [0-9a-fA-F.:]+' | awk '{print $2}' | sort | uniq -c | sort -rn | head -10
1893 198.51.100.23
744 203.0.113.199
35 192.0.2.61
Para ver cuántos intentos ha habido en la última hora, usa el journal del servicio SSH (en Ubuntu 24.04 la unidad se llama ssh):
sudo journalctl -u ssh --since "1 hour ago" | grep -c 'Failed password'
Qué usuarios intentan adivinar:
sudo grep -h 'Invalid user' /var/log/auth.log | awk '{for (i=1;i<=NF;i++) if ($i=="user") print $(i+1)}' | sort | uniq -c | sort -rn | head
Los intentos fallidos en sí no son un problema si solo permites autenticación por clave. Lo que de verdad importa es si alguno ha tenido éxito. Revisa los inicios de sesión aceptados y el historial de accesos:
sudo grep 'Accepted' /var/log/auth.log | tail -20
last -n 20
sudo lastb -n 20
Cualquier Accepted password o Accepted publickey desde una IP que no reconoces es una señal de compromiso. Comprueba también que no se han añadido claves SSH desconocidas:
sudo find /root /home -name authorized_keys -exec ls -l {} \; -exec cat {} \;
Paso 5: Buscar procesos sospechosos
Un servidor comprometido casi siempre tiene algún proceso ajeno: un minero, un bot de DDoS o una shell inversa. Empieza por los procesos que escuchan en puertos:
sudo ss -tulpn
Cada puerto debe corresponder a un servicio que conoces (sshd, nginx, mysqld...). Después busca procesos cuyo binario se ejecuta desde directorios temporales o ha sido borrado del disco, dos técnicas habituales del malware:
sudo ls -l /proc/[0-9]*/exe 2>/dev/null | grep -E '\(deleted\)|/tmp/|/var/tmp/|/dev/shm/'
lrwxrwxrwx 1 www-data www-data 0 Sep 25 10:12 /proc/48213/exe -> /tmp/.x/kworkerd (deleted)
Si encuentras un PID sospechoso, examínalo antes de matarlo. Sustituye PID por el número del proceso:
ps -fp PID
sudo ls -l /proc/PID/exe /proc/PID/cwd
sudo tr '\0' ' ' < /proc/PID/cmdline; echo
sudo lsof -nP -p PID -i
Estas son señales típicas de un proceso malicioso:
- Nombre que imita un hilo del kernel (
kworkerd,[kthreadd]) pero con un binario en disco. - Se ejecuta como
www-datau otro usuario de servicio y no pertenece a ese servicio. - Consume casi el 100 % de la CPU de forma constante (típico de los mineros como
xmrig). - Tiene conexiones abiertas hacia IP externas desconocidas.
Paso 6: Revisar los mecanismos de persistencia
Tras entrar, los atacantes suelen asegurarse de volver aunque reinicies el servidor o mates sus procesos. Revisa las tareas cron de todos los usuarios y del sistema:
sudo ls -la /var/spool/cron/crontabs/
sudo cat /var/spool/cron/crontabs/* 2>/dev/null
sudo cat /etc/crontab /etc/cron.d/*
Busca líneas que descarguen algo con curl o wget, que ejecuten ficheros de /tmp o que contengan cadenas en base64.
Revisa los temporizadores y servicios de systemd, y en especial las unidades modificadas en los últimos días:
systemctl list-timers --all
sudo find /etc/systemd/system /usr/lib/systemd/system -type f -mtime -7 -ls
Comprueba que no hay usuarios nuevos con UID 0 y que no existe /etc/ld.so.preload, un fichero que se usa a menudo para ocultar procesos:
awk -F: '$3 == 0 {print $1}' /etc/passwd
ls -l /etc/ld.so.preload
root
ls: cannot access '/etc/ld.so.preload': No such file or directory
Solo debe aparecer root, y lo normal es que /etc/ld.so.preload no exista.
Paso 7: Comprobar la integridad de los ficheros
Binarios del sistema
debsums compara los ficheros instalados con las sumas MD5 de los paquetes Debian. La opción -c muestra solo los ficheros que han cambiado:
sudo debsums -c
Una salida vacía es la situación esperada. Si aparece un binario como /usr/bin/ps o /usr/bin/ss, el sistema está comprometido. Ten en cuenta que los ficheros de configuración de /etc que tú has editado no se comprueban con -c, así que no generarán ruido.
Directorios temporales y web
Revisa los ficheros recientes en los directorios con permiso de escritura para todos, donde el malware suele dejar sus binarios:
sudo find /tmp /var/tmp /dev/shm -type f -mtime -3 -ls
Si alojas aplicaciones web, busca ficheros PHP modificados en la última semana y funciones típicas de las web shells:
sudo find /var/www -type f -name '*.php' -mtime -7 -ls
sudo grep -rlE 'eval\s*\(\s*(base64_decode|gzinflate|str_rot13)' /var/www --include='*.php'
Un fichero PHP dentro de un directorio de subidas (uploads/) casi nunca es legítimo.
Escáneres de rootkits
rkhunter y chkrootkit buscan firmas de rootkits conocidos y configuraciones anómalas:
sudo apt install rkhunter chkrootkit
sudo rkhunter --check --skip-keypress --report-warnings-only
sudo chkrootkit -q
Ambos generan falsos positivos (por ejemplo, avisos sobre scripts en /usr/bin o ficheros ocultos legítimos). Úsalos como pista para investigar, no como veredicto.
Paso 8: Analizar los logs del servidor web
Muchos ataques se dirigen a la aplicación y no al sistema. Con el formato de log por defecto de Nginx (combined), estas consultas muestran las IP con más peticiones y las peticiones por minuto:
sudo awk '{print $1}' /var/log/nginx/access.log | sort | uniq -c | sort -rn | head -10
sudo awk '{print $4}' /var/log/nginx/access.log | cut -d: -f1-3 | uniq -c | tail -10
4210 [25/Sep/2026:10:38
4388 [25/Sep/2026:10:39
9876 [25/Sep/2026:10:40
Un salto brusco de peticiones por minuto marca el inicio del ataque. Para ver qué URL se atacan y con qué códigos responde el servidor:
sudo awk '{print $7}' /var/log/nginx/access.log | sort | uniq -c | sort -rn | head -10
sudo awk '{print $9}' /var/log/nginx/access.log | sort | uniq -c | sort -rn
Miles de peticiones a /wp-login.php o /xmlrpc.php indican fuerza bruta contra WordPress; muchas respuestas 404 a rutas como /.env o /.git/config indican un escaneo automatizado. Con Apache, los logs están en /var/log/apache2/access.log y usan el mismo formato.
Paso 9: Contener el ataque
Cuando tengas claro qué ocurre, actúa por este orden.
Recoger evidencias
Guarda el estado del sistema antes de cambiar nada, porque matar procesos o reiniciar borra información útil:
INCIDENT_DIR="/root/incident-$(date +%Y%m%d-%H%M%S)"
sudo mkdir -p "$INCIDENT_DIR"
ps auxf | sudo tee "$INCIDENT_DIR/processes.txt" > /dev/null
sudo ss -tunap | sudo tee "$INCIDENT_DIR/connections.txt" > /dev/null
last -F | sudo tee "$INCIDENT_DIR/logins.txt" > /dev/null
sudo cp /var/log/auth.log /var/log/syslog "$INCIDENT_DIR/"
Si hay un proceso malicioso, copia también su binario, que sigue accesible desde /proc aunque se haya borrado del disco:
sudo cp /proc/PID/exe "$INCIDENT_DIR/malware.bin"
Bloquear IP atacantes
Con UFW, inserta la regla de denegación en la primera posición para que se evalúe antes que las reglas allow:
sudo ufw insert 1 deny from 198.51.100.23
sudo ufw status numbered
Para bloquear una red completa, usa la notación CIDR (198.51.100.0/24).
Detener procesos y cuentas comprometidas
Mata el proceso y bloquea la cuenta que lo lanzó, cerrando además todas sus sesiones (sustituye your_user por el usuario afectado):
sudo kill -9 PID
sudo usermod -L -e 1 your_user
sudo pkill -KILL -u your_user
Elimina después las entradas de cron, unidades de systemd y claves SSH que encontraste en los pasos 4 y 6. Si el atacante obtuvo root o debsums detectó binarios modificados, no intentes limpiar el sistema: restaura desde una copia de seguridad anterior al compromiso o reinstala, y cambia todas las credenciales.
Paso 10: Prevenir nuevos ataques
Bloqueo automático con Fail2ban
Fail2ban lee los logs y bloquea temporalmente las IP que fallan demasiadas veces. En Ubuntu 24.04 conviene que lea directamente del journal, para lo que necesita python3-systemd:
sudo apt install fail2ban python3-systemd
Crea la configuración local de la jaula de SSH:
sudo nano /etc/fail2ban/jail.local
[sshd]
enabled = true
backend = systemd
maxretry = 5
findtime = 10m
bantime = 1h
Activa el servicio, reinícialo para que cargue la nueva configuración y comprueba que la jaula está funcionando:
sudo systemctl enable fail2ban
sudo systemctl restart fail2ban
sudo fail2ban-client status sshd
Status for the jail: sshd
|- Filter
| |- Currently failed: 3
| |- Total failed: 27
| `- Journal matches: _SYSTEMD_UNIT=sshd.service + _COMM=sshd
`- Actions
|- Currently banned: 2
|- Total banned: 2
`- Banned IP list: 198.51.100.23 203.0.113.199
Endurecer SSH
La medida más eficaz contra la fuerza bruta es desactivar las contraseñas. Asegúrate primero de que puedes entrar con tu clave SSH, y después crea un fichero de configuración adicional:
sudo nano /etc/ssh/sshd_config.d/10-hardening.conf
PasswordAuthentication no
PermitRootLogin no
Valida la sintaxis y recarga el servicio:
sudo sshd -t
sudo systemctl reload ssh
Guardar histórico de métricas
Para poder comparar la próxima vez con lo "normal", activa la recogida periódica de sysstat. Edita /etc/default/sysstat y cambia ENABLED="false" por ENABLED="true", y después:
sudo systemctl enable --now sysstat
A partir de ahí, sar -n DEV y sar -q muestran el tráfico y la carga de todo el día.
Solución de problemas
/var/log/auth.logno existe: el sistema no tienersyslog. Usasudo journalctl -u sshen su lugar, o instalarsyslog.ssmuestra direcciones como[::ffff:198.51.100.44]: son conexiones IPv4 aceptadas por un socket IPv6. Es normal y el conteo sigue siendo válido.- Fail2ban no arranca con el error
Have not found any log file for sshd jail: faltabackend = systemden la jaula o no está instaladopython3-systemd. - Te has bloqueado a ti mismo con UFW: accede por la consola del servidor y elimina la regla con
sudo ufw delete NUMERO, usando el número que muestrasudo ufw status numbered.
Conclusión
Ya tienes un procedimiento para diagnosticar un posible ataque: identificar el origen del tráfico y de los accesos, localizar procesos y mecanismos de persistencia ajenos, verificar la integridad del sistema y contener el problema sin perder evidencias. Como siguientes pasos, configura copias de seguridad automáticas que te permitan restaurar un sistema limpio, limita el acceso a SSH por IP con UFW y pon en marcha una monitorización que te avise de picos de conexiones o de CPU antes de que los notes tú.
