Escaner de puertos escrito en Python
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.