Herramienta para escanear puertos abiertos en python3
Herramienta para escanear puertos con scapy através de Stealth Scan empleando hilos
Introducción
¿Cúantos de vosotros estáis cansados de utilizar nmap en vuestros escaneos?
Hoy os traigo otra manera de escaneo de puertos abiertos efectiva mediante un script en Python
utilizando SYN Scan
, empleando hilos por cada puerto a escanear, capturando la señal Ctrl+c por parte del usuario y empleando colores y barras de progreso para amenizar la espera.
Este tipo de escaneo es rápido y “sigiloso”, ya que no completa la conexión TCP (half-open scanning) aunque seamos realistas, siempre hay trazas. De hecho, nmap
utiliza el parámetro -sS
para realizar el mismo funcionamiento.
Script Completo
Podéis encontrar el script a continuación o en mi github.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
#!/usr/bin/env python3
"""
Descripcion: Script para ecanear puertos abiertos
Autor: k3sero
"""
from pwn import *
from scapy.all import *
from termcolor import colored
import signal
import sys
import time
import threading
# Solamente mostrar errores criticos
logging.getLogger("scapy.runtime").setLevel(logging.ERROR)
p1 = log.progress("TCP Scan")
p1.status("Escaneando Puertos...")
def def_handler(sig,frame):
p1.failure("Escaneo abortado")
print(colored(f"\n\n[!] Saliendo...\n",'red'))
sys.exit(1)
#Ctrl+c
signal.signal(signal.SIGINT, def_handler)
def scanPort(ip,port):
src_port = RandShort()
try:
response = sr1(IP(dst=ip)/TCP(sport=src_port, dport=port, flags="S"), timeout=2, verbose=0)
if response is None:
return False
elif response.haslayer(TCP) and response.getlayer(TCP).flags == 0x12:
send(IP(dst=ip)/TCP(sport=src_port, dport=port, flags="R"), verbose = 0)
return True
else:
return False
except Exception as e:
log.failure(f"Error escaneando {ip} en puerto {port}: {e}")
sys.exit(1)
def thread_function(ip,port):
response = scanPort(ip,port)
if response:
print(f"Puerto {port} - Abierto")
def main(ip, ports, end_port):
threads = []
time.sleep(2)
for port in ports:
p1.status(f"Progreso del escaneo: [{port}/{end_port}]")
thread = threading.Thread(target=thread_function, args=(ip,port))
thread.start()
threads.append(thread)
for thread in threads:
thread.join()
p1.success("Escaneo finalizado")
if __name__ == '__main__':
if len(sys.argv) != 3:
print(colored(f"\n\n[!] Uso: {colored("python3",'blue')} {colored(sys.argv[0],'green')} {colored("<ip> <ports-range>\n",'yellow')}",'red'))
sys.exit(1)
target_ip = sys.argv[1]
portRange = sys.argv[2].split("-")
start_port = int(portRange[0])
end_port = int(portRange[1])
ports = range(start_port, end_port + 1)
main(target_ip, ports, end_port)
Funcionamiento
Bibliotecas
El script utiliza las siguientes bibliotecas:
scapy
: Para la manipulación de paquetes de red.termcolor
: Para colorear la salida en consola.threading
: Para ejecutar escaneos en paralelo.pwn
: Para mostrar una barra de progreso.
1
2
3
4
from pwn import *
from scapy.all import *
from termcolor import colored
import signal, sys, time, threading
Manejo de la señal Ctrl + c
Gestión de la funcionalidad Ctrl + c para finalizar la ejecución, para ello se define el siguiente manejador.
1
2
3
4
5
6
def def_handler(sig,frame):
p1.failure("Escaneo abortado")
print(colored(f"\n\n[!] Saliendo...\n",'red'))
sys.exit(1)
signal.signal(signal.SIGINT, def_handler)
Lógica del Escaneo.
Las peticiones se realizan mediante paquetes SynScan, de modo en que nosotros como atacantes enviaremos a la máquina victima una paquete SYN
por un puerto en específico para iniciar la conexión. La máquina víctima recibe dicho paquete y si el puerto asociado se encuentra abierto, la máquina nos enviará una trama SYN-ACK
dando paso a la conexión. Por último si todo se ha realizado correcatamente, el atacante enviara en último lugar un paquete RST
para cerrar la conexión sin completarla.
SYN
: Se envía un paquete TCP con flag SYN.
SYN-ACK
: Si el puerto está abierto, se recibe un SYN-ACK.
RST
: Se envía un RST para cerrar la conexión sin completarla.
1
2
3
4
5
6
def scanPort(ip, port):
response = sr1(IP(dst=ip)/TCP(flags="S", dport=port), timeout=2, verbose=0)
if response and response.haslayer(TCP) and response.getlayer(TCP).flags == 0x12:
send(IP(dst=ip)/TCP(flags="R"), verbose=0) # Envía RST
return True
return False
Hilos para realizar paralelismo
Cada puerto se escanea en un hilo independiente para acelerar el proceso.
1
2
3
4
5
6
7
8
9
10
11
12
def thread_function(ip, port):
if scanPort(ip, port):
print(f"Puerto {port} - Abierto")
def main(ip, ports, end_port):
threads = []
for port in ports:
thread = threading.Thread(target=thread_function, args=(ip, port))
thread.start()
threads.append(thread)
for thread in threads:
thread.join()
Barra de Progreso
Se emplea una barra de progreso interactiva para amenizar la espera utilizando log.progress()
1
2
3
4
p1 = log.progress("TCP Scan")
p1.status("Escaneando Puertos...")
# ...
p1.status(f"Progreso: [{port}/{end_port}]")
Ejemplos de Uso
1
sudo python3 scanner.py <IP> <puerto-inicial>-<puerto-final>
Un ejemplo básico podría ser el siguiente.
1
sudo python3 scanner.py 192.168.1.1 1-100
Conclusión
En cuanto a eficiencia, no tiene nada que envidiarle a escaneadores como nmap
ya que gracias a el empleo de hilos, obtenemos tiempos de ejecución bastante similares, pero puede llegar a saturar la red si esta no cuenta con buenos recursos si el rango a escanear es muy amplio.
Al no completar la conexión el escaneo como hemos dicho anteriormente, es menos detectable pero igualmente dejamos traza a la hora de realizar las conexiones. Además hay que tener en cuenta que algunos sistemas/firewalls más sofisticados pueden bloquear este tipo de escaneos SYN
.