Herramientas de usuario

Herramientas del sitio


informatica:programacion:cursos:python_avanzado_proyectos_seguridad:nmap_herramienta_escaner_puertos

¡Esta es una revisión vieja del documento!


Nmap como herramienta de escáner de puertos

Módulo perteneciente al curso Python avanzado para proyectos de seguridad

Nmap es una herramienta para la exploración de la red y la auditoría de seguridad. Permite realizar escaneados con ping (determinando que máquinas están activas), utilizando diferentes técnicas de escaneado de puertos, detección de versiones (determinando los protocolos de los servicios y las versiones de las aplicaciones que están escuchando en los puertos), e identificación mediante TCP/IP (identificando el sistema operativo de la máquina o el dispositivo).

La herramienta nmap se utiliza principalmente para reconocimiento y escaneo de puertos en un determinado segmento de red.

En la página oficial nos podemos descargar la última versión disponible dependiendo del sistema operativo sobre el cual queramos instalarlo.

Tipos de escaneo con nmap

Si ejecutamos la herramienta nmap desde la consola, vemos que tenemos los siguientes tipos de escaneo:

IMAAAAAAAAAAAAAAAAAAAAGEN

Entre las principales opciones que podemos utilizar destacan:

  • sT (TCP Connect Scan): Es la opción que se suele utilizar para detectar si un puerto está abierto o cerrado, pero también suele ser el mecanismo más auditado y vigilado por sistemas de detección de intrusos. Con esta opción, un puerto se encuentra abierto si el servidor responde con un paquete que contenga el flag ACK al enviar un paquete con el flag SYN.
  • sS (TCP Stealth Scan): Tipo de escaneo basado en el TCP Connect Scan con la diferencia de que la conexión en el puerto indicado no se realiza de forma completa. Consiste en comprobar el paquete de respuesta del objetivo ante un paquete con el flag SYN habilitado. Si el objetivo responde con un paquete que tiene el flag RST, entonces se puede comprobar si el puerto está abierto o cerrado.
  • sU (UDP Scan): Tipo de escaneo basado en el protocolo UDP donde no se lleva a cabo un proceso de conexión, sino que simplemente se envía un paquete UDP para determinar si el puerto está abierto. Si la respuesta es otro paquete UDP, significa que el puerto está abierto. En el caso de que el puerto no esté abierto se recibirá un paquete ICMP del tipo 3(destino inalcanzable).
  • sA (TCP ACK Scan): Tipo de escaneo que permite saber si nuestra máquina objetivo tiene algún tipo de firewall en ejecución. Lo que hace este escaneo es enviar un paquete con el flag ACK activado y se envía a la máquina objetivo. En el caso de que la máquina remota responda con un paquete que tenga el flag RST activado, se puede determinar que el puerto no se encuentra filtrado por ningún firewall. En el caso de que el no responda o lo haga con un paquete ICMP del tipo se puede determinar que hay un firewall filtrando los paquetes enviados en el puerto indicado.
  • sN (TCP NULL Scan): Tipo de escaneo que envía un paquete TCP a la máquina objetivo sin ningún flag. Si la máquina remota no emita ninguna respuesta, se puede determinar que el puerto se encuentra abierto. Si la máquina remota devuelve un flag RST, podemos decir que el puerto se encuentra cerrado.
  • sF (TCP FIN Scan): Tipo de escaneo que envía un paquete TCP a la máquina objetivo con el flag FIN. Si la máquina remota no emita ninguna respuesta, se puede determinar que el puerto se encuentra abierto. Si la máquina remota devuelve un flag RST, podemos decir que el puerto se encuentra cerrado.
  • sX (TCP XMAS Scan): Tipo de escaneo que envía un paquete TCP a la máquina objetivo con los flags PSH, FIN, URG. Si la máquina remota no emita ninguna respuesta, se puede determinar que el puerto se encuentra abierto. Si la máquina remota devuelve un flag RST, podemos decir que el puerto se encuentra cerrado. Si en el paquete de respuesta obtenemos uno del tipo ICMP del tipo 3, entonces el puerto se encuentra filtrado.

El tipo de scan por defecto puede variar en función del usuario que lo esté ejecutando, por aquello de los permisos de enviar paquetes durante el scan. La diferencia entre unos y otros scan radica en el “ruido” generado, y en su capacidad de evitar ser detectados por sistemas de seguridad como pueden ser los firewalls o los sistemas detección de intrusos.

Si queremos crear un escáner de puertos, tendríamos que crear un hilo por cada socket que abriese una conexión en un puerto y gestionar el uso compartido de la pantalla mediante un semáforo. Con esto tendríamos un código largo y además solo haríamos un scanning simple TCP, pero no ACK, SYN-ACK, RST o FIN proporcionados por el toolkit Nmap.

Actividad práctica: Utilizar nmap para realizar un escaneo en una máquina y lista de puertos específica con el objetivo de detectar puertos y servicios abiertos

Determinar los puertos abiertos y filtrados para los siguientes dominios:

Solución

Nmap scan report for adrformacion.com (34.255.218.111)
Host is up (0.042s latency).
Other addresses for adrformacion.com (not scanned): 63.33.142.19 34.249.78.102
rDNS record for 34.255.218.111: ec2-34-255-218-111.eu-west-1.compute.amazonaws.com
Not shown: 998 filtered ports
PORT    STATE SERVICE
80/tcp  open  http
443/tcp open  https


Nmap scan report for python.org (45.55.99.72)
Host is up (0.10s latency).
Not shown: 997 closed ports
PORT    STATE    SERVICE
80/tcp  open     http
443/tcp open     https
646/tcp filtered ldp


Nmap scan report for ftp.be.debian.org (195.234.45.114)
Host is up (0.039s latency).
Other addresses for ftp.be.debian.org (not scanned): 2a05:7300:0:100::2
rDNS record for 195.234.45.114: mirror.as35701.net
Not shown: 993 closed ports
PORT     STATE    SERVICE
21/tcp   open     ftp
22/tcp   open     ssh
80/tcp   open     http
443/tcp  open     https
646/tcp  filtered ldp
873/tcp  open     rsync
9999/tcp open     abyss

Escaneo de puertos con Python-nmap

En python podemos hacer uso de nmap a través de la librería python-nmap la cual nos permite manipular fácilmente los resultados de un escaneo, además, puede ser una herramienta perfecta para administradores de sistemas o consultores de seguridad informática a la hora de automatizar procesos de penetration testing.

Python-nmap es una herramienta que se utiliza dentro del ámbito de las auditorías de seguridad o pruebas de intrusión y su principal funcionalidad es descubrir qué puertos o servicios tiene en escucha un determinado host.

Para proceder con la instalación lo podemos hacer con alguno de los siguientes comandos:

sudo apt-get install python-pip nmap
sudo pip install python-nmap

Además, para instalar el módulo es necesario ejecutar el comando con permisos de administrador/superusuario del sistema (sudo).

Una vez instalado podríamos invocar el módulo desde nuestros scripts o desde la terminal interactiva, por ejemplo:

>>> import nmap
>>> nmap.__version__
'0.6.1'
>>> dir(nmap)
['ET', 'PortScanner', 'PortScannerAsync', 'PortScannerError', 'PortScannerHostDict', 'PortScannerYield', 'Process', '__author__', '__builtins__', '__cached__', '__doc__', '__file__', '__last_modification__', '__loader__', '__name__', '__package__', '__path__', '__spec__', '__version__', 'convert_nmap_output_to_encoding', 'csv', 'io', 'nmap', 'os', 're', 'shlex', 'subprocess', 'sys']

Una vez hayamos comprobado que la instalación ha sido correcta podemos empezar a realizar escaneos sobre un determinado host, para ello tenemos que instanciar un objeto de la clase PortScanner(), así podremos acceder al método más importante: scan. Una buena práctica para entender cómo trabaja una función, método u objeto es usar la función help() o dir() para saber las funciones/métodos disponibles en una clase u objeto.

>>> import nmap
>>> portScanner = nmap.PortScanner()
>>> dir(portScanner)
['_PortScanner__process', '__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', '_nmap_last_output', '_nmap_path', '_nmap_subversion_number', '_nmap_version_number', '_scan_result', 'all_hosts', 'analyse_nmap_xml_scan', 'command_line', 'csv', 'get_nmap_last_output', 'has_host', 'listscan', 'nmap_version', 'scan', 'scaninfo', 'scanstats']

Método de escaneo

Si ejecutamos el comando help(portScanner.scan) vemos que el método scan() de la clase PortScanner recibe tres argumentos, el host a analizar, los puertos y los argumentos, además al final añade como deben ser enviados los parámetros (todos deben ser en formato string):

>>> help(portScanner.scan)

IMAAAAAAAAAAAAAAAAAAAAAAAAGEN

Lo primero que tenemos que hacer es importar la librería de nmap y crearnos nuestro objeto para empezar a interactuar con PortScanner().

Lanzamos nuestro primer escaneo con el método scan("ip/rango","puertos",'argumentos'), donde solo el primer parámetro es obligatorio y el segundo y tercer parámetros son opcionales, de forma que si no lo definimos realiza un escaneo estándar de Nmap.

El siguiente código lo podemos encontrar en el fichero portScanner_inicial.py:

import nmap
portScanner = nmap.PortScanner()
resultados = portScanner.scan('scanme.nmap.org', '21,22,23,80','-sV')
print(resultados)
print(portScanner.command_line())

En el script anterior está realizando un escaneo sobre el dominio scanme.nmap.org en los puertos 21, 22, 23 y 80.

Lo primero que hacemos es instanciar un objeto de la clase PortScanner() y a través de los métodos scan() y command_line() para ver el comando que nmap está ejecutando por debajo.

Con el argumento -sV le estamos indicando que detecte las versiones cuando se realiza el escaneo. El resultado del escaneo es un diccionario que contiene la misma información que devolvería un escaneo hecho con Nmap directamente.

La ejecución del script anterior podría ser:

$ python3 portScanner_inicial.py      
{'nmap': {'command_line': 'nmap -oX - -p 21,22,23,80 -sV scanme.nmap.org', 'scaninfo': {'tcp': {'method': 'connect', 'services': '21-23,80'}}, 'scanstats': {'timestr': 'Tue Jul 28 18:08:15 2020', 'elapsed': '8.32', 'uphosts': '1', 'downhosts': '0', 'totalhosts': '1'}}, 'scan': {'45.33.32.156': {'hostnames': [{'name': 'scanme.nmap.org', 'type': 'user'}, {'name': 'scanme.nmap.org', 'type': 'PTR'}], 'addresses': {'ipv4': '45.33.32.156'}, 'vendor': {}, 'status': {'state': 'up', 'reason': 'conn-refused'}, 'tcp': {21: {'state': 'closed', 'reason': 'conn-refused', 'name': 'ftp', 'product': '', 'version': '', 'extrainfo': '', 'conf': '3', 'cpe': ''}, 22: {'state': 'open', 'reason': 'syn-ack', 'name': 'ssh', 'product': 'OpenSSH', 'version': '6.6.1p1 Ubuntu 2ubuntu2.13', 'extrainfo': 'Ubuntu Linux; protocol 2.0', 'conf': '10', 'cpe': 'cpe:/o:linux:linux_kernel'}, 23: {'state': 'closed', 'reason': 'conn-refused', 'name': 'telnet', 'product': '', 'version': '', 'extrainfo': '', 'conf': '3', 'cpe': ''}, 80: {'state': 'open', 'reason': 'syn-ack', 'name': 'http', 'product': 'Apache httpd', 'version': '2.4.7', 'extrainfo': '(Ubuntu)', 'conf': '10', 'cpe': 'cpe:/a:apache:http_server:2.4.7'}}}}}   
nmap -oX - -p 21,22,23,80 -sV scanme.nmap.org

En la salida del script anterior vemos como los puertos 22 y 80 son los que están abiertos, además muestra información sobre la versión del servicio que se está ejecutando en ese puerto.

El método all_hosts() nos devuelve información acerca de los hosts o direcciones ip que están activos.

>>>portScanner.all_hosts()
['45.33.32.156']

Con el método scaninfo() podemos ver los servicios que han dado algún tipo de respuesta en el proceso de escaneo, así como el método de escaneo.

>>>portScanner.scaninfo()
{'tcp': {'method': 'connect', 'services': '21-23,80'}}

Obtener resultados del escaneo en formato CSV

Si queremos visualizar de una manera fácil el resultado del escaneo, disponemos de la función csv(), el cual nos devolverá la información en formato csv que lo separa por punto y coma.

>>> portScanner.csv()

Ejemplo de salida:

host;hostname;hostname_type;protocol;port;name;state;product;extrainfo;reason;version;conf;cpe
45.33.32.156;scanme.nmap.org;user;tcp;21;ftp;closed;;;conn-refused;;3;
45.33.32.156;scanme.nmap.org;PTR;tcp;21;ftp;closed;;;conn-refused;;3;
45.33.32.156;scanme.nmap.org;user;tcp;22;ssh;open;OpenSSH;"Ubuntu Linux; protocol 2.0";syn-ack;6.6.1p1 Ubuntu 2ubuntu2.13;10;cpe:/o:linux:linux_kernel
45.33.32.156;scanme.nmap.org;PTR;tcp;22;ssh;open;OpenSSH;"Ubuntu Linux; protocol 2.0";syn-ack;6.6.1p1 Ubuntu 2ubuntu2.13;10;cpe:/o:linux:linux_kernel
45.33.32.156;scanme.nmap.org;user;tcp;23;telnet;closed;;;conn-refused;;3;
45.33.32.156;scanme.nmap.org;PTR;tcp;23;telnet;closed;;;conn-refused;;3;
45.33.32.156;scanme.nmap.org;user;tcp;80;http;open;Apache httpd;(Ubuntu);syn-ack;2.4.7;10;cpe:/a:apache:http_server:2.4.7
45.33.32.156;scanme.nmap.org;PTR;tcp;80;http;open;Apache httpd;(Ubuntu);syn-ack;2.4.7;10;cpe:/a:apache:http_server:2.4.7</p>

Escaneo síncrono

En el siguiente ejemplo vamos a implementar una clase llamada NmapScanner que permite realizar un escaneo de una dirección ip y un puerto específico que se pasan por parámetro a la función nmapScan(ip,port) de la clase NmapScanner.

El siguiente código lo podemos encontrar en el fichero NmapScanner.py:

#!/usr/bin/env python3
 
import nmap
 
class NmapScanner:
 
    def __init__(self):
        self.portScanner = nmap.PortScanner()
 
    def nmapScan(self, host, port):
        try:
            print("Comprobando puerto "+ port +" en la máquina "+host)
            self.portScanner.scan(host, port)
 
            # Command info
            print("[*] Ejecutando el comando: %s" % self.portScanner.command_line())     
            self.state = self.portScanner[host]['tcp'][int(port)]['state']
            print(" [+] "+ host + " tcp/" + port + " " + self.state)
            print(self.portScanner[host].tcp(int(port)))
            self.server = self.portScanner[host].tcp(int(port))['product']
            self.version = self.portScanner[host].tcp(int(port))['version']
            print(" [+] "+ self.server + " " + self.version + " tcp/" + port)
 
        except Exception as exception:
            print("Error al conectar con " + host + " para escaner de puertos"+str(exception))
 
 
NmapScanner = NmapScanner()
NmapScanner.nmapScan("45.33.32.156","80")

Ejecución escáner síncrono

En la ejecución del script anterior vemos como ha detectado que el puerto 80 está abierto en la dirección ip 45.33.32.156, mostrando información adicional sobre el comando ejecutado y la versión del sistema operativo.

La siguiente sería una ejecución del script anterior:

$ python3 NmapScanner.py
Comprobando el puerto 80 en la máquina 45.33.32.156
[*] Ejecutando el comando: nmap -oX - -p 80 -sV 45.33.32.156
 [+] 45.33.32.156 tcp/80 open
{'state': 'open', 'reason': 'syn-ack', 'name': 'http', 'product': 'Apache httpd', 'version': '2.4.7', 'extrainfo': '(Ubuntu)', 'conf': '10', 'cpe': 'cpe:/a:apache:http_server:2.4.7'}
 [+] Apache httpd 2.4.7 tcp/80
  

Guardar resultado del escaneo en un fichero JSON

Además de realizar el escaneo de puertos y devolver el resultado por consola, podríamos generar un documento JSON donde almacenar el resultado con los puertos abiertos para un determinado host o dirección ip. En este caso utilizamos la función csv() que devuelve el resultado del escaneo en un formato fácil de tratar para recoger la información que necesitemos.

El siguiente código lo podemos encontrar en el fichero NmapScannerJSON.py:

 #!/usr/bin/env python3
 
import nmap
import json
 
class NmapScannerJSON:
 
    def __init__(self):
        self.portScanner = nmap.PortScanner()
 
    def nmapScanJSON(self, host, ports):
        try:
            print("Comprobando puertos "+ str(ports) +" en la máquina "+host)
            self.portScanner.scan(host, ports)
 
            # Info del comando ejecutado
            print("[*] Ejecutando el comando: %s" % self.portScanner.command_line())
 
            print(self.portScanner.csv())
            results = {}     
 
            for x in self.portScanner.csv().split("\n")[1:-1]:
                splited_line = x.split(";")
                host = splited_line[0]
                dns = splited_line[1]
                protocolo = splited_line[3]
                puerto = splited_line[4]
                estado = splited_line[6]
 
                results.update({'host': host,'dns': dns,'protocolo': protocolo,'puerto': puerto,'estado': estado})
 
            # Almacenar info en json
            file_info =  "scan_%s.json" % host
            with open(file_info, "w") as file_json:
                json.dump(results, file_json)
 
            print("[*] El fichero '%s' ha sido generado con los resultados del escaner" % file_info)            
 
        except Exception as exception:
            print("Error al conectar con " + host + " para escaner de puertos"+str(exception))
 
NmapScannerJSON = NmapScannerJSON()
NmapScannerJSON.nmapScanJSON("45.33.32.156","21,22,23,80")

Al final de script vemos cómo se instancia un objeto de la clase NmapScannerJSON y se realiza la llamada al método nmapScanJSON() pasando por parámetros la ip y la lista de puertos que queremos analizar.

Ejecución escáner formato json

En la ejecución del script anterior vemos como ha detectado como abiertos los puertos 22 y 80 en la dirección ip 45.33.32.156, mostrando información adicional sobre el comando ejecutado y la versión del sistema operativo.

$ python3 NmapSEcannerJSON.py 
Comprobando puertos 21,22,23,80 en la máquina 45.33.32.156
[*] Ejecutando el comando: nmap -oX - -p 21,22,23,80 -sV 45.33.32.156
host;hostname;hostname_type;protocol;port;name;state;product;extrainfo;reason;version;conf;cpe
45.33.32.156;scanme.nmap.org;PTR;tcp;21;ftp;closed;;;conn-refused;;3;
45.33.32.156;scanme.nmap.org;PTR;tcp;22;ssh;open;OpenSSH;"Ubuntu Linux; protocol 2.0";syn-ack;6.6.1p1 Ubuntu 2ubuntu2.13;10;cpe:/o:linux:linux_kernel
45.33.32.156;scanme.nmap.org;PTR;tcp;23;telnet;closed;;;conn-refused;;3;
45.33.32.156;scanme.nmap.org;PTR;tcp;80;http;open;Apache httpd;(Ubuntu);syn-ack;2.4.7;10;cpe:/a:apache:http_server:2.4.7

[*] El fichero 'scan_45.33.32.156.json' ha sido generado con los resultados del escáner

Como resultado del anterior script, además de mostrar la información por consola, lo que hace es generar un fichero json de nombre scan_45.33.32.156.json.

El contenido de este fichero es el siguiente:

{"host": "45.33.32.156", "dns": "scanme.nmap.org", "protocolo": "tcp", "puerto": "80", "estado": "open"}

Usando PortScannerYield

Si necesitamos mostrar los resultados conforme los vayamos obteniendo podemos utilizar su clase PortScannerYield, con el cual podemos ir obteniendo el progreso de nuestro escaneo. Esta clase útil cuando tenemos que escanear un segmento de red y necesitamos hacer operaciones con cada host que vayamos descubriendo.

El siguiente código lo podemos encontrar en el fichero PortScannerYield.py:

import nmap
 
portScannerYield = nmap.PortScannerYield()
 
for scan_yield in portScannerYield.scan('scanme.nmap.org', '21,22,23,25,80'):
    print(scan_yield[1]['nmap']['scanstats']['uphosts'])
    if scan_yield[1]['nmap']['scanstats']['uphosts'] == "1":
        print("Host ==> " + scan_yield[0])
        print(scan_yield[1]['scan'])

La salida del script anterior sería la siguiente:

Host ==> 45.33.32.156
{'45.33.32.156': {'hostnames': [{'name': 'scanme.nmap.org', 'type': 'PTR'}], 'addresses': {'ipv4': '45.33.32.156'}, 'vendor': {}, 'status': {'state': 'up', 'reason': 'syn-ack'}, 'tcp': {21: {'state': 'closed', 'reason': 'conn-refused', 'name': 'ftp', 'product': '', 'version': '', 'extrainfo': '', 'conf': '3', 'cpe': ''}, 22: {'state': 'open', 'reason': 'syn-ack', 'name': 'ssh', 'product': 'OpenSSH', 'version': '6.6.1p1 Ubuntu 2ubuntu2.13', 'extrainfo': 'Ubuntu Linux; protocol 2.0', 'conf': '10', 'cpe': 'cpe:/o:linux:linux_kernel'}, 23: {'state': 'closed', 'reason': 'conn-refused', 'name': 'telnet', 'product': '', 'version': '', 'extrainfo': '', 'conf': '3', 'cpe': ''}, 25: {'state': 'closed', 'reason': 'conn-refused', 'name': 'smtp', 'product': '', 'version': '', 'extrainfo': '', 'conf': '3', 'cpe': ''}, 80: {'state': 'open', 'reason': 'syn-ack', 'name': 'http', 'product': 'Apache httpd', 'version': '2.4.7', 'extrainfo': '(Ubuntu)', 'conf': '10', 'cpe': 'cpe:/a:apache:http_server:2.4.7'}}}}

A continuación, vamos a realizar un escaneo utilizando otros módulos. Para realizar el escaneo con nmap desde python, también tendríamos la opción de realizarlo utilizando los módulos os y subprocess.

informatica/programacion/cursos/python_avanzado_proyectos_seguridad/nmap_herramienta_escaner_puertos.1731666564.txt.gz · Última modificación: por tempwin