Escaner de puertos escrito en Python
El objetivo de este articulo es demostrar como es la creación de un escaner de puertos simples en python.
Librerias utilizadas
- socket
- argparse
- sys
- concurrent.futures
- termcolor
Definir Ctrl+C
Objetivo: Permitir que el usuario interrumpa el programa de manera segura
1
2
3
4
5
6
7
8
9
10
11
12
open_sockets = []
def def_handler(sig, frame):
print(colored(f"[!] Saliendo del programa...", 'red'))
for socket in open_sockets:
socket.close()
sys.exit(1)
signal.signal(signal.SIGINT, def_handler) # CTRL+C
Detalles:
- Muestra un mensaje indicando que el programa se está cerrando.
- Cierra todos los sockets que están abiertos en
open_sockets
para liberar recursos. - Termina el programa con
sys.exit(1)
.
Capturar el objetivo (target
) y los puertos (port
) que se quieren escanear
1
2
3
4
5
6
7
def get_arguments(): # Definir argumentos
parser = argparse.ArgumentParser(description='Escaner de puertos TCP')
parser.add_argument("-t", "--target", dest="target", required=True, help="Target a escanear (Ej: -t 192.168.1.1)")
parser.add_argument("-p", "--port", dest="port", required=True, help="Puertos a escanear (Ej: -p 1-1000)")
options = parser.parse_args() # Recopila todos los argumentos y los almacena en options
return options.target, options.port # Retorna los argumentos
Detalles:
- Define los argumentos requeridos:
-t
para el objetivo y-p
para los puertos. - Si alguno de los argumentos no se proporciona, muestra un mensaje de ayuda automáticamente.
- Retorna el objetivo y la cadena de puertos proporcionada por el usuario.
Crear un socket TCP con un tiempo de espera
1
2
3
4
5
6
7
def create_socket():
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.settimeout(1) # Timeout de 1 segundo para determinar si el puerto esta cerrado o no
open_sockets.append(s)
return s
Detalles:
- El socket se configura para usar IPv4 y TCP.
- Se establece un tiempo de espera de 1 segundo, lo que ayuda a determinar si un puerto está cerrado.
- Se agrega el socket a la lista
open_sockets
para poder cerrarlo más tarde si es necesario. - Retorna el socket creado.
Verificar si un puerto específico está abierto o cerrado
1
2
3
4
5
6
7
8
9
10
11
def port_scanner(port, host):
s = create_socket()
try:
s.connect((host, port)) # Se entabla la conexion
print(colored(f"\n[+] El puerto {port} esta abierto", 'green'))
s.close()
except (socket.timeout, ConnectionRefusedError): # Manejo de excepciones
pass
Detalles:
- Crea un nuevo socket llamando a
create_socket()
. - Intenta conectar el socket al puerto y dirección IP proporcionados.
- Si la conexión es exitosa, imprime un mensaje indicando que el puerto está abierto y cierra el socket.
- Si no puede conectar (por timeout o rechazo de conexión), no hace nada y deja que el socket se cierre.
Escanear múltiples puertos simultáneamente para acelerar el proceso
1
2
3
4
def scan_ports(ports, target):
with ThreadPoolExecutor(max_workers=100) as executor:
executor.map(lambda port: port_scanner(port, target), ports)
Detalles:
- Utiliza
ThreadPoolExecutor
para gestionar hasta 100 hilos simultáneamente. - Cada hilo ejecuta
port_scanner
para un puerto en particular. - La función
executor.map()
asigna la tarea de escaneo a cada puerto.
Interpretar la entrada del usuario sobre los puertos y convertirla en un formato usable (rango o lista)
1
2
3
4
5
6
7
8
9
def parse_ports(ports_str):
if '-' in ports_str: # Escanear un rango de puertos
start, end = map(int, ports_str.split('-'))
return range(start, end + 1)
elif ',' in ports_str:
return map(int, ports_str.split(','))
else:
return (int(ports_str),)
Detalles:
- Si la cadena contiene un guion (
-
), se interpreta como un rango de puertos (por ejemplo,1-100
se convierte enrange(1, 101)
). - Si la cadena contiene comas (
,
), se convierte en una lista de puertos específicos (por ejemplo,22,80,443
se convierte en[22, 80, 443]
). - Si se proporciona un solo puerto, lo convierte en una tupla con un solo número.
Orquestar la ejecución de todas las funciones para llevar a cabo el escaneo de puertos
1
2
3
4
5
6
7
8
def main():
target, ports_str = get_arguments() # Recibir los argumentos
ports = parse_ports(ports_str)
scan_ports(ports, target)
if __name__ == '__main__':
main()
Detalles:
- Llama a
get_arguments()
para obtener el objetivo y los puertos a escanear. - Utiliza
parse_ports()
para interpretar los puertos proporcionados. - Llama a
scan_ports()
para comenzar el escaneo concurrente de los puertos.
Flujo General del Programa
- Inicia el programa: Se define el manejador de señales y se capturan los argumentos.
- Interpretación de puertos: Se convierten los puertos introducidos en un rango o lista de puertos.
- Escaneo de puertos: Se escanean los puertos de manera concurrente usando hilos.
- Manejo de interrupciones: Si el usuario presiona
CTRL+C
, el programa se cierra limpiamente, cerrando todos los sockets abiertos.
Este flujo permite escanear puertos de manera eficiente y manejar la interrupción del programa de forma segura.
Screenshot
This post is licensed under CC BY 4.0 by the author.