Bloquear escaneo de puertos en Linux
Seguro en algún momento de tu carrera profesional, o como aficionado, escaneaste algún servidor con nmap en busca de puertos abiertos ¿Quién no? A veces es divertido. Pero cuando un desconocido con oscuras intensiones escanea los puertos de nuestros servidores, ahí sí la cosa ya no es tan divertida. Por eso en este artículo te explico un método para bloquear escaneo de puertos en Linux.
Existen herramientas en GNU/Linux que detectan algunas técnicas de escaneo de puertos, y bloquearlas. Algunas son Snort y Psad, incluso podemos recurrir a IPTABLES para hacerlo con el mismo kernel. Con estas herramientas podemos detectar tres tipos de escaneos, llamados así: Escaneo Null, Escaneo Fin y Escaneo Xmas.
Podemos cercar nuestro servidor de esos sondeos con IPTABLES, así:
Escaneo Null
iptables -A INPUT -i enp0s9 -p tcp --tcp-flags ALL NONE -m recent --name blacklist_3600 --set -m comment --comment "REJECT/Blacklist Null scan" -j REJECT
Escaneo Xmas
iptables -A INPUT -i enp0s9 -p tcp --tcp-flags ALL SYN,RST,ACK,FIN,URG -m recent --name blacklist_3600 --set -m comment --comment "Bloquea escaneo Xmas/PSH" -j REJECT
iptables -A INPUT -i enp0s9 -p tcp --tcp-flags ALL FIN,PSH,URG -m recent --name blacklist_3600 --set -m comment --comment "Bloquea escaneo Xmas/PSH" -j REJECT
iptables -A INPUT -i enp0s9 -p tcp --tcp-flags ALL ALL -m recent --name blacklist_3600 --set -m comment --comment "Bloquea escaneo Xmas/PSH" -j REJECT
Para Escaneo Fin
iptables -A INPUT -i enp0s9 -p tcp --tcp-flags ALL FIN -m recent --name blacklist_3600 --set -m comment --comment "Bloquea escaneo FIN" -j REJECT
Cada una de las anteriores bloquea la IP del atacante durante 3600 segundos.
Sin embargo, hay otro tipo de escaneo que es difícil de detectar, y es el más simple de todos: el escaneo simple ó SYN Stealth Scan. Se trata un sencillo nmap y la IP objetivo:
nmap "dirección IP"
Es difícil de detectar porque usa el paquete SYN por cada puerto sondeado, el mismo que usan los dispositivos para iniciar una comunicación TCP normal.
Bloquear escaneo de puertos en Linux
Vamos a implementar esta medida de seguridad con un script tipo Bash, y toma cinco pasos:
1- Configurar el demonio rsyslog para que registre las bitácoras de IPTABLES en el archivo /var/log/iptables/iptables.log (puede ser el que tu quieras).
- Creamos el archivo 10-iptables.conf
sudo touch /etc/rsyslog.d/10-iptables.conf
- Le ponemos esta configuración (son dos líneas)
:msg,regex,"iptables: " -/var/log/iptables/iptables.log
& stop
- Reiniciamos el demonio rsyslog
sudo service rsyslog restart
2- Creamos dos reglas IPTABLES. Una para que los paquetes SYN que lleguen a nuestro servidor sean registrados en /var/log/iptables/iptables.log, excepto los que van al puerto 22, 20, 21, 80 y 2256 (pues el puerto 22 es de uso oficial para mí caso, y el 20, 21, 80 y 2256 ya los tengo bloqueados con otra regla). Así, no se registrará nuestra IP al conectarnos desde afuera (con esta regla evitamos caer en la trampa que estamos colocando para los escaneadores). No registrar en el log los puertos que ya tenemos bloqueados por política, evitará saturar nuestro control con bloqueos innecesarios.
iptables -A INPUT -i enp0s9 -p tcp --syn --match multiport ! --dports 22,20,21,80,2256 -j LOG --log-tcp-options --log-prefix "iptables: "
La segunda regla IPTABLES es para reducir la velocidad de llegada de los paquetes SYN, a 17 paquetes/segundo, excepto a los puertos de uso oficial. Esto debido a que NMAP puede enviar más de 1200 paquetes por segundo. Con esta regla le damos tiempo a nuestro script de reaccionar:
iptables -A INPUT -i enp0s9 -p tcp --syn --match multiport ! --dports 22,20,21,2256 -m hashlimit --hashlimit-above 17/sec --hashlimit-mode srcip --hashlimit-name all -j DROP
3- Creamos la cadena SOSPECHOSOS en IPTABLES. Ahí vamos a «encerrar» las direcciones IP que intentan escanearnos:
iptables -N SOSPECHOSOS
iptables -I INPUT 1 -j SOSPECHOSOS
4- Instalamos las librerías de Inotify Tools:
sudo apt install inotify-tools
Inotify Tools es una solución a nivel que de kernel que actúa como vigilante de los cambios que se producen en los archivos que le indiquemos. Esta solución la vamos a usar dentro del siguiente script:
5- Creamos el script scan_brake.sh (puede quedar en nuestro home directory):
#!/bin/bash
bantime=600
interfaz=enp0s9
### VACIAMOS EL LOG DE IPTABLES PORQUE PUEDE ESTAR SATURADO Y NOS PUEDE COLAPSAR EL SISTEMA
> /var/log/iptables/iptables.log
while true; do
while inotifywait -q -q -e modify /var/log/iptables/iptables.log; do
### CONTAMOS SI ALGUIEN LANZÓ MÁS DE 15 PAQUETES SYN EN UN SEGUNDO, TIPICO DE UN ESCANEO NMAP SIMPLE
cuenta=`awk -F' ' '{print $11}' /var/log/iptables/iptables.log | awk -F'=' '{print $2}' | wc -l`
if [ $cuenta -gt 15 ]; then
### COMENZAMOS A BLOQUEAR A TODA IP REGISTRADA QUE HA INTENTADO ESCANEAR
for f in $(awk -F' ' '{print $11}' /var/log/iptables/iptables.log | awk -F'=' '{print $2}' | uniq)
do
iptables -A SOSPECHOSOS -i $interfaz -s $f -j DROP
echo "LOG VACIADO" >> detectados.txt
done
> /var/log/iptables/iptables.log
sleep $bantime
/sbin/iptables -F SOSPECHOSOS
fi
done
done
Nota: El campo correspondiente a la dirección IP origen en el archivo iptables.log, puede diferir de posición de un kernel a otro.
Funcionamiento del script scan_brake.sh
Un escaneo de puertos simple, producido por ejemplo con nmap, envía a cada puerto un paquete SYN en busca de una respuesta. Si la obtiene, pues el puerto está disponible. Este mismo mecanismo lo usa una conexión TCP normal, por eso es que es difícil discernir entre un escaneo y el inicio de una conexión normal.
Lo único que diferencia una conexión normal de un escaneo simple, es que este último puede lanzar más de 1200 paquetes SYN/segundo a los puertos más conocidos. Así es como nmap obtiene sus resultados en 9 segundos (de un escaneo simple). Es ahí donde podemos detectarlo y atajarlo.
Así pues, la primera regla IPTABLES que creamos (punto 2), registra en el archivo iptables.log todos los paquetes SYN que llegan a nuestro servidor. El script por su parte mantiene verificando algún cambio producido en dicho archivo, por medio de inotifywait (línea 7).
Entonces llega un paquete SYN y se registra. La línea 10 del script verifica si además se han registrado más de 15 paquetes SYN en ese instante. Entonces, de ser cierto, toma las direcciones IP registradas en iptables.log y las bloquea (línea 14).
El detalle aquí es que las direcciones IP que esten registradas en iptables.log serán bloqueadas, sin importar su origen. Por eso en la regla IPTABLES somos claros en no registrar paquetes SYN a puertos o servicios que nosotros necesitamos usar (solo nosotros lo sabemos, nadie más tiene por qué, esa es la razón de bloquear los escaneos de puertos).
La variable bantime define el tiempo (en segundos) en que las direcciones IP quedarán bloqueadas. Un valor recomendable es de 2400 segundos, o mucho más.
Herramientas para el escaneo de puertos
Podemos usar comandos tales como nmap, nping ó hping3 para verificar puertos activos. Por ejemplo, para escanear el puerto 80 con el comando hping3, escribimos:
sudo hping3 -A direccion_ip_objetivo -c 2 -p 80
Contener Intentos de Conexión
Por otro lado, en caso de que ya sepan de nuestros puertos y desplieguen una oleada de intentos de logueo, típicos de fuerza bruta, acudimos a soluciones tipo Fail2Ban.
Otra práctica recomendada es cambiar el número puerto estándar (bien conocido por todo el mundo), a otro diferente para despistar. Por ejemplo, para SSH cambiando el 22 por el 8122.
Posible Contramedida
La única contramedida posible que yo veo para este caso, y a la que puede acudir un atacante, es hacer un escaneo simple más suave, obteniendo un informe individual por puerto, y no en conjunto como normalmente se hace (para evitar el envío masivo de SYN).
De todas formas quedará la IP origen registrada y tarde que temprano será bloqueada si continúa su comportamiento, y si hay más atacantes haciendo lo mismo, peor para ellos, serán bloqueados más rápidamente. Claro, con esta contramedida se puede obtener información de a un puerto a la vez, pero así les tomará más tiempo, de manera que no se las dejamos tan fácil 😉
Alternativas a este script
Bueno, después de todo esto que estamos viendo, podemos afirmar que existe otra solución llamada Portsentry, también con capacidad de detectar y bloquear un escaneo simple. Pero Portsentry siempre deja pasar el primer escaneo del atacante para identificarlo y proceder a bloquearlo. Y con el primer escaneo el atacante ya tiene suficiente información de nuestros puertos. Como puedes comprobar, esta solución que ofrezco no deja pasar ni a la primera.
Variante 1 del Script scan_brake.sh
El siguiente código es una variante del script que acabamos de ver:
#!/bin/bash
bantime=50
interfaz=enp0s9
### VACIAMOS EL LOG DE IPTABLES PORQUE PUEDE ESTAR SATURADO Y NOS PUEDE COLAPSAR EL SISTEMA
> /var/log/iptables/iptables.log
while true; do
while inotifywait -q -q -e modify /var/log/iptables/iptables.log; do
cuenta=`awk -F' ' '{print $11}' /var/log/iptables/iptables.log | awk -F'=' '{print $2}' | wc -l`
if [ $cuenta -gt 15 ]; then
for f in $(awk -F' ' '{print $11}' /var/log/iptables/iptables.log | awk -F'=' '{print $2}' | uniq)
do
route add -host $f reject
echo "LOG VACIADO" >> detectados.txt
done
> /var/log/iptables/iptables.log
function desbloquea(){
for f in $(route -n | awk '/!H/ {print $1}')
do
route del -host $f reject
done
}
#sleep $bantime /sbin/iptables -F SOSPECHOSOS
sleep $bantime && desbloquea
fi
done
done
Lo único que hace este cambio es bloquear la dirección IP del escáner de puertos, desde la tabla de enrutamiento de Linux, y no desde IPTABLES. Luego, pasado el tiempo de bantime, se desbloquean por medio de la función desbloquea (línea 20).
Dato curioso
El moderno kernel de Linux puede gestionar más de 4 millones de rutas estáticas en su tabla de enrutamiento. Incluso puede registrar rutas estáticas a razón de 300000/hora, u 83/segundo, bueno, al menos en una máquina con Intel Xeon E5410 Quad Core, a 2,4 GHz y 33 GB de RAM, como la mía. Registrar 1000000 (un millón) de rutas estáticas representa un costo de memoria de 1,5 GB.
Variante 2 del script para bloquear escaneo de puertos
Si vamos a definir un bantime superior a los 900, 1800, 3600 segundos, entonces separemos el fragmento del código que se encarga de desbloquear las IP de los escaneadores, en otro script (lo llamaré contadorban.sh), o de lo contrario el scan_brake.sh se quedará esperando todo el tiempo de bantime para seguir vigilando el archivo iptables.log:
#!/bin/bash
interfaz=enp0s9
contador=0
### VACIAMOS EL LOG DE IPTABLES PORQUE PUEDE ESTAR SATURADO Y NOS PUEDE COLAPSAR EL SISTEMA
> /var/log/iptables/iptables.log
while true; do
while inotifywait -q -q -e modify /var/log/iptables/iptables.log; do
cuenta=`awk -F' ' '{print $11}' /var/log/iptables/iptables.log | awk -F'=' '{print $2}' | wc -l`
if [ $cuenta -gt 15 ]; then
for f in $(awk -F' ' '{print $11}' /var/log/iptables/iptables.log | awk -F'=' '{print $2}' | uniq)
do
route add -host $f reject
echo "LOG VACIADO" >> detectados.txt
done
/var/log/iptables/iptables.log
if [ $contador -eq 0 ]; then
./contadorban.sh &
contador=1
fi
fi
done
done
El script contadorban.sh:
#!/bin/bash
while true
do
bantime=10200
function desbloquea(){
for f in $(route -n | awk '/!H/ {print $1}')
do
route del -host $f reject
done
}
sleep $bantime && desbloquea
done
Variante 3 para Bloquear Escaneo de Puertos
El punto débil de la variante 2 es que los bots escaneadores de puertos «spoofean» todo el espectro del direccionamiento IP, por lo que la IP de nuestra casa, así como la de nuestro smartphone, o la de nuestros clientes, algún día caerá en la trampa, por suplantación.
La siguiente variante soluciona dicha debilidad, acudiendo nuevamente a IPTABLES, «baneando» las direcciones IP excepto a nuestros puertos oficiales. Así, si nuestras direcciones IP caen por suplantación, seguiremos teniendo acceso a nuestros servicios.
#!/bin/bash
excepcion="130.120.22.124"
log_iptables="/var/log/iptables/iptables.log"
detectados="/home/sistemas/detectados.txt"
contador=0
# Limpieza inicial
> "$log_iptables"
# Monitoreo eficiente con inotify
while inotifywait -q -q -e modify "$log_iptables"; do
# Extraemos IPs únicas que aparecen como SRC= en el log
# 'grep -oP' es mucho más rápido que múltiples pipes de awk
ips_sospechosas=$(grep -oP 'SRC=\K[0-9.]+' "$log_iptables" | sort -u)
# Contamos líneas totales de una vez
cuenta=$(wc -l < "$log_iptables")
if [ "$cuenta" -gt 15 ]; then
for f in $ips_sospechosas; do
# Saltamos la excepción y verificamos si la IP analisada ya existe en la cadena
if [[ "$f" != "$excepcion" ]] && ! iptables -C SOSPECHOSOS -s "$f" &>/dev/null; then
# Reglas de bloqueo
iptables -I SOSPECHOSOS 1 -s "$f" -p tcp --tcp-flags SYN,ACK,FIN,RST SYN --match multiport ! --dports 22,20,21,2256 -j DROP
iptables -I SOSPECHOSOS 1 -s "$f" -p icmp -j DROP
# Registro de tiempo (ajuste de -5 horas incluido)
fecha_epoch=$(date +%s)
fecha_ajustada=$((fecha_epoch - 18000))
echo "$f $fecha_ajustada" >> "$detectados"
echo "$f $fecha_ajustada" >> /home/sistemas/detectados2.txt
fi
done
# Vaciado rápido y log
> "$log_iptables"
echo "LOG VACIADO" >> "$detectados"
if [ $contador -eq 0 ]; then
/home/sistemas/contadorban.sh &
contador=1
fi
fi
done
El script contadorban.sh:
#!/bin/bash # Configuración bantime=240 baneados_minimo=1200 archivo_detectados="/home/sistemas/detectados.txt" archivo_desbloqueados="/home/sistemas/desbloqueados.txt" # Función de limpieza de reglas duplicadas/antiguas desbloquea() { # Obtener lista de IPs actualmente en la cadena SOSPECHOSOS de forma eficiente # Extraemos solo la IP (columna 4 en iptables -S) mapfile -t ips_actuales < <(iptables -S SOSPECHOSOS | awk '/-s / {print $4}' | cut -d/ -f1 | uniq) if [ "${#ips_actuales[@]}" -gt "$baneados_minimo" ]; then ahora=$(date +%s) for f in "${ips_actuales[@]}"; do # Buscamos el timestamp en el archivo de una sola vez timestamp=$(grep -m 1 "^$f " "$archivo_detectados" | awk '{print $2}') # Validar si es un número y si el tiempo expiró if [[ "$timestamp" =~ ^[0-9]+$ ]]; then if [ $((ahora - timestamp)) -gt "$bantime" ]; then echo "Desbloqueando $f (Expiró bantime)" # Eliminamos TODAS las ocurrencias de esa IP en la cadena de una vez # Usamos un bucle mientras el check de existencia sea exitoso while iptables -C SOSPECHOSOS -s "$f" -p tcp --tcp-flags SYN,ACK,FIN,RST SYN --match multiport ! --dports 22,20,21,2256 -j DROP 2>/dev/null; do iptables -D SOSPECHOSOS -s "$f" -p tcp --tcp-flags SYN,ACK,FIN,RST SYN --match multiport ! --dports 22,20,21,2256 -j DROP done iptables -D SOSPECHOSOS -s "$f" -p icmp -j DROP 2>/dev/null # Limpieza de archivos (sed -i es lento, pero aceptable si no se hace mil veces) echo "$f DESBLOQUEADA $(date)" >> "$archivo_desbloqueados" sed -i "/^$f /d" "$archivo_detectados" fi fi done fi } # Bucle principal while true; do sleep "$bantime" desbloquea done
Notaras que algunas líneas terminan con el caracter &. Eso es para que el proceso de esa línea se ejecute en segundo plano y permita la ejecución de la siguiente acción. Cada vez que enviamos a segundo plano Linux le asigna un core de CPU diferente a esa acción, así logramos mayor rendimiento en estos ambientes hostiles de la seguridad informática.
¿cómo le hacemos pruebas preliminares a esta solución?
Desde otra máquina con el mismo nmap, o con nping, podemos validar la eficacia de la solución. Con el segundo le lanzamos un ping a cualquiera de los puertos de la máquina protegida con scan_break.
Lanzamos a nping desde otra máquina
sudo nping --tcp -p 10000 192.168.0.202
Esperamos a que scan_break haga lo suyo. Luego, validamos nuevamente con nping
sudo nping --tcp -p 10000 192.168.0.202

Revisando la trampa
Como habrás visto en el algoritmo, el scan_brake.sh genera dos archivos de texto con el propósito de visualizar las direcciones IP bloqueadas y desbloqueadas. Si necesitamos visualizar las IP que han caído en la trampa, miramos la tabla SOSPECHOSOS:
sudo iptables -S SOSPECHOSOS
¿Necesitamos un conteo de las IP registradas en la trampa? Ningún problema, el siguiente comando nos los muestra:
sudo iptables -S SOSPECHOSOS | awk '/DROP/ {print $4}' | uniq | wc -l
Por otro lado, contador_ban.sh ejecuta la función de desbloquear las direcciones IP según el periodo definido en la variable bantime, y si los bloqueados superan el umbral de la variable baneados_minimo.
Con el paso de las horas observarémos cientos de direcciones IP sospechosas. Algunas provienen de verdaderas intenciones oscuras. Otras son de motores de búsqueda de vulnerabilidades de Internet, tales como los del Project Sonar, los de Censys, y de Internet Census Group. Con los sondeos realizados por estos proyectos, se elaboran estadísticas acerca de los servicios de red que usamos en Internet, y su seguridad (o vulnerabilidad) relacionada.
No más imagina que tengas un router cuya vulnerabilidad sea detectada y explotada por SSH desde Internet, con privilegios de Administrador. Prácticamente una puerta trasera. Semejante situación pudiera sucederle a alguien con un router marca Zyxel funcionando con el firmware 4.60 Zyxel USG. Eso fue lo que le encontraron a esos artefactos. Más o menos el 10 % de esos dispositivos estuvieron trabajando con ese firmware por más de tres meses.
También es frecuente encontrar en la trampa las IP de botnets, tales como los crawlers de Google y del buscador de Microsoft.
Precaución!
Debemos tener precaución con la cantidad de direcciones IP registradas en la tabla SOSPECHOSOS, porque es memoria ocupada en la RAM, y la carga del sistema se puede ver comprometida (lo que se conoce como Load average). En mi caso, con 8GB de RAM y un procesador Intel Xeon E5410 Quad Core, el sistema se comienza a poner “pesado” cuando los sospechosos registrados pasan de las 7 mil direcciones IP.
Para equilibrar este asunto, debemos ajustar el bantime y baneados_minimo del script contadorban.sh, según nuestra experiencia con esta solución.
El proceso de desbloqueo de direcciones IP que ejecuta contadorban.sh, puede elevar la temperatura del procesador. Eso depende de la cantidad registros en la tabla SOSPECHOSOS.

El gráfico muestra que en mi caso, cada 2.5 horas se presenta picos de temperatura. Esos picos son el reflejo de contadorban.sh. Gráfico generado con Monitorix.
Obtención de los registros
Cada fila de las direcciones IP dentro del archivo detectados.txt, tiene asociado el tiempo de su registro en formato unix time.
Para analisarlo fácilmente, podemos usar el siguiente script en Python para generar un archivo CSV desde el archivo detectados.txt con el tiempo unix time convertido en formato «Año-mes-dia«, así podremos hacerle análisis en otro programa tipo LibreOffice:
#!/usr/bin/env python3.8
import os
from datetime import datetime
os.system('echo > reporte.csv')#Limpiamos el archivo
reporte = open('reporte.csv', 'a')
detectados = open('detectados.txt', 'r')
for i in detectados:
if len(i) >= 2:
campos = i.split()
ip = campos[0]
fecha_unix = campos[1]
if fecha_unix != 'VACIADO':
fecha = datetime.utcfromtimestamp(int(fecha_unix)).strftime('%Y-%m-%d %H:%M:%S')
reporte.write(ip+','+fecha+'\n')
print(ip+','+fecha)
detectados.close()
reporte.close()
Otro tipo de estadística que podemos visualizar de IPTABLES, incluyendo la tabla SOSPECHOSOS, es el contador de paquetes que cada regla registra. Con el siguiente comando podemos visualizar dicha información:
sudo iptables -L -n -v -x
- -L listar todas las reglas.
- -n no resuleva nombres de direcciones ip.
- -v listas contador de paquetes y bytes.
- -x muestre el conteo de bytes (de lo contrario abrevie como 200K, 3M, etc).
Y eso es todo.
Puedes ver más comandos iptables, aquí
Puede que te interese leer sobre Web Scraping y cómo protegerse)
Valora este artículo:






Pingback: 🥇 Comandos Linux para la Gestión de Redes LAN - Networking
Pingback: 🥇 Prevención de ataques informáticos y ransomware
Pingback: 🥇 Expulsar Intrusos por SSH en Linux - Seguridad Informática
Pingback: 🥇 Web Scraping - Definición, medidas de protección y ejemplos
Pingback: 🥇 Comandos iptables para configurar firewall en Linux - Ejemplos
Pingback: 🥇 Dividir el Tráfico de Red entre Dos ISP en Linux