Construcción de un Escáner de Puertos con Python

PythonBeginner
Practicar Ahora

Introducción

En este proyecto, desarrollaremos un escáner de puertos de servidor utilizando Python para detectar puertos abiertos en un servidor de destino. Esta herramienta es fundamental tanto para administradores de sistemas, que la utilizan para verificar políticas de seguridad, como para posibles atacantes, que la emplean para identificar servicios de red operativos en un host. Nuestro recorrido cubrirá los aspectos esenciales del escaneo de puertos, incluyendo su metodología e implicaciones. Profundizaremos en la creación de un escáner de puertos sencillo pero efectivo aprovechando las capacidades de Python, enfocándonos en un enfoque multihilo para mejorar la eficiencia y el rendimiento.

El escaneo de puertos es el proceso de enviar solicitudes a un rango de puertos de un servidor para determinar cuáles están abiertos. Este paso es crucial tanto para mantener la seguridad como para que los atacantes encuentren servicios vulnerables. Comenzaremos explorando los conceptos básicos del escaneo de puertos y sus implicaciones.

Conceptos Clave:

  • Los escáneres de puertos ayudan a detectar puertos abiertos en un servidor o host.
  • Se utilizan para evaluaciones de seguridad y por atacantes para identificar servicios vulnerables.
  • La forma más simple de escaneo de puertos implica intentar conexiones TCP a un rango de puertos.

👀 Vista Previa

Aquí tienes un adelanto de la herramienta de escaneo de puertos que construiremos:

python port_scanner.py 127.0.0.1 5000-9000

Salida:

Opened Port: 8081
Scanning completed.

🎯 Tareas

En este proyecto, aprenderás:

  • Cómo utilizar las capacidades de programación de sockets de Python para interactuar con puertos de red.
  • Cómo implementar un enfoque multihilo en Python para mejorar la eficiencia de las tareas de escaneo de red.
  • Cómo desarrollar una herramienta de línea de comandos en Python que acepte entradas del usuario para un escaneo de puertos flexible.

🏆 Logros

Después de completar este proyecto, serás capaz de:

  • Usar la librería socket de Python para crear conexiones de red, probar la disponibilidad de puertos y manejar excepciones de red.
  • Comprender y aplicar el multihilo (multi-threading) en Python para realizar tareas concurrentes, mejorando significativamente el rendimiento de las operaciones intensivas de red.
  • Construir una herramienta práctica de escaneo de puertos por línea de comandos, mejorando tus habilidades de scripting en Python y tu comprensión del análisis de argumentos de comandos.

Establecimiento de Pruebas de Conexión TCP

Nuestro primer paso consiste en escribir una función para probar si un puerto TCP está abierto en la IP de destino. Esta función intentará conectarse a un puerto específico y determinará su estado.

Abre el archivo llamado port_scanner.py en el directorio /home/labex/project y añade la siguiente función de Python:

import argparse
from queue import Queue
from socket import AF_INET, gethostbyname, socket, SOCK_STREAM
import threading

def tcp_test(port: int, target_ip: str) -> None:
    with socket(AF_INET, SOCK_STREAM) as sock:
        sock.settimeout(1)
        result = sock.connect_ex((target_ip, port))
        if result == 0:
            print(f"Opened Port: {port}")

Este código define una función, tcp_test, que intenta establecer una conexión TCP a un puerto específico en la dirección IP de destino. Configura un tiempo de espera (timeout) para evitar esperas prolongadas en cada puerto y utiliza connect_ex para intentar la conexión.

Si connect_ex devuelve 0, indica que la conexión fue exitosa, lo que significa que el puerto está abierto, y la función imprime un mensaje indicando el puerto abierto.

✨ Revisar Solución y Practicar

Configuración de Verificaciones de Puertos Concurrentes

A continuación, crearemos una función worker que será utilizada por cada hilo para procesar los puertos de una cola y utilizar la función tcp_test para verificar el estado de cada puerto.

Añade el siguiente código a tu archivo port_scanner.py:

def worker(target_ip: str, queue: Queue) -> None:
    while not queue.empty():
        port = queue.get()
        tcp_test(port, target_ip)
        queue.task_done()

La función worker está diseñada para ser ejecutada por múltiples hilos. Recupera continuamente números de puerto de una cola y utiliza la función tcp_test definida anteriormente para comprobar si esos puertos están abiertos en la IP de destino.

Después de probar un puerto, marca la tarea como completada en la cola. Esta función permite el escaneo concurrente de puertos, acelerando significativamente el proceso.

✨ Revisar Solución y Practicar

Orquestación del Proceso de Escaneo

La función main orquestará el proceso de escaneo configurando la cola, inicializando los hilos de trabajo y gestionando la operación de escaneo sobre el rango de puertos especificado.

Continúa añadiendo la función main a tu archivo port_scanner.py:

def main(host: str, start_port: int, end_port: int) -> None:
    target_ip = gethostbyname(host)
    queue = Queue()
    for port in range(start_port, end_port + 1):
        queue.put(port)
    for _ in range(100):  ## Adjust the number of threads if necessary
        t = threading.Thread(target=worker, args=(target_ip, queue,))
        t.daemon = True
        t.start()
    queue.join()
    print("Scanning completed.")

La función main coordina todo el proceso de escaneo de puertos.

Primero resuelve el nombre de host de destino a una dirección IP y luego crea una cola para los números de puerto que se van a escanear. Llena la cola con todos los puertos en el rango especificado y genera múltiples hilos de trabajo (100 en este ejemplo) que procesan la cola de forma concurrente verificando cada puerto. La función espera a que se procesen todos los puertos (queue.join()) y luego imprime un mensaje indicando que el escaneo ha finalizado.

✨ Revisar Solución y Practicar

Ejecución del Escáner de Puertos

Finalmente, es hora de ejecutar nuestro escáner de puertos. Escanearemos un rango de puertos en el localhost para probar nuestra herramienta.

Añade las siguientes líneas al final de tu archivo port_scanner.py para que el script sea ejecutable:

if __name__ == '__main__':
    parser = argparse.ArgumentParser(description='TCP Port Scanner')
    parser.add_argument('host', help='Host to scan')
    parser.add_argument('ports', help='Port range to scan, formatted as start-end')
    args = parser.parse_args()

    start_port, end_port = map(int, args.ports.split('-'))
    main(args.host, start_port, end_port)

Este código utiliza el análisis de argumentos de línea de comandos para el script del escáner de puertos, haciéndolo más versátil y fácil de usar.

Al usar la librería argparse, define dos argumentos obligatorios: host y ports.

  • El argumento host sirve para especificar la dirección o el nombre de host del objetivo que deseas escanear.
  • El argumento ports espera una cadena que defina el rango de puertos a escanear, con el formato "inicio-fin" (por ejemplo, "5000-8000"), que el script divide en start_port y end_port utilizando el delimitador - y luego los convierte en enteros. Esto permite a los usuarios especificar fácilmente el objetivo y el rango de puertos al ejecutar el script desde la terminal.

Ahora, ejecuta el escáner ejecutando el siguiente comando en la terminal:

python port_scanner.py 127.0.0.1 5000-9000

Salida:

Opened Port: 8081
Scanning completed.

Ahora que has identificado un puerto abierto (8081) utilizando el escáner de puertos, y dado que hay un servidor web ejecutándose en él, puedes explorar qué se está alojando allí.

Para hacer esto, primero haz clic en el botón de añadir y selecciona la opción "Web Service".

Seleccionar opción Web Service

Luego introduce el puerto 8081 y haz clic en el botón "Access".

Introducir puerto y acceder al servidor web

Encontrarás el servidor web ejecutándose en tu sistema, escuchando en el puerto 8081.

Servidor web ejecutándose en el puerto 8081
✨ Revisar Solución y Practicar

Resumen

En este proyecto, hemos construido un escáner de puertos básico con Python, comenzando desde la función fundamental tcp_test hasta llegar a un enfoque de escaneo multihilo. Dividimos el proceso en pasos manejables, culminando en una herramienta funcional capaz de identificar puertos abiertos en un servidor de destino. A través de esta experiencia práctica, no solo profundizamos nuestra comprensión de la seguridad de redes, sino que también perfeccionamos nuestras habilidades en la programación con Python y la gestión de concurrencia.