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

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 😉

bloquear escaneo de puertos en Linux

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

En la imagen podemos ver que la máquina protegida ya no responde a los paquetes SYN de nping.

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í

Comandos iptables

Puede que te interese leer sobre Web Scraping y cómo protegerse)

Qué es el web scraping y cómo protegerse


Valora este artículo:

Bloquear escaneo de puertos en Linux
5,0 rating based on 12.345 ratings
Overall rating: 5 out of 5 based on 1 reviews.
Nombres
Email
Titulo
Calificanos
Contenido

Muy bueno

★★★★★
“ Súper interesante!”
- Rodolfo Saavedra
Comparte esto en
Publicado en Seguridad Informática.