Nmap es una utilidad gratuita para descubrimiento de redes y auditar la seguridad de las mismas. Se puede utilizar para monitorizar los dispositivos de red, comprobar su estado y el estado de sus servicios en funcionamiento, inventariarlos, etc.
Esta utilidad se puede descargar desde su web en https://nmap.org y se puede usar desde la terminal o símbolo del sistema usando los parámetros y valores adecuados, y a través de una interfaz GUI gracias a Zenmap, utilidad que ayuda en la construcción del comando, parámetros y valores y que muestra el resultado en un entorno gráfico.
Comentaros que lo que voy a explicar es para que podáis usarlo en vuestras redes o con permiso escrito del propietario de la misma ya que en algunos países, el uso de estas herramientas en una red que no es vuestra, podría considerarse un delito. Daros por tanto por advertidos.
Si no disponéis de una red de dispositivos, siempre es posible utilizar algún software como VirtualBox o VMware Player para montar un laboratorio donde configurar una switch virtual e instalar máquinas virtuales conectadas a este switch.
En cualquier caso, lo que a nosotros nos interesa es poder usarlo con Python, por lo que tras su instalación en nuestro sistema operativo usando la información disponible en su web, haremos uso de un módulo que nos permitirá hacerlo y que se llama Python-nmap. Para comenzar a usarlo debemos instalarlo y esto lo conseguiremos escribiendo el siguiente comando en la terminal o símbolo del sistema de nuestro sistema operativo:
# pip3 install python-nmap
Para su funcionamiento básico primero hemos de importar el módulo, cosa que hacemos escribiendo:
import nmap
Vamos a mostrar que versión del módulo estamos usando haciendo uso del método __version__.
>>> nmap.__version__
'0.6.1'
Si queremos ver los métodos que existen en el módulo nmap usamos dir() de la siguiente forma:
>>> 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']
Para mostrar el funcionamiento del módulo nos interesa el método PortScanner ya que es el que usaremos para realizar un escaneo de puertos en los dispositivos de nuestra red.
Vamos a ver los métodos disponibles usando dir() de nuevo.
>>> dir(nmap.PortScanner)
['__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__', 'all_hosts', 'analyse_nmap_xml_scan', 'command_line', 'csv', 'get_nmap_last_output', 'has_host', 'listscan', 'nmap_version', 'scan', 'scaninfo', 'scanstats']
Entre otros los métodos que nos interesan ahora son nmap_version que nos mostrará la versión de nmap que estamos usando, scan, que realizará el escaneo de puertos, command_line que nos mostrará el comando y parámetros usados en nuestro escaneo y scaninfo para ver la información producida por el escaneo.
Escanearemos los puertos 80 (http), 443 (https) buscando servidores web disponibles, el puerto 21 (ftp), 22 (ssh) y 3389 (rdp) para ver que equipos tienen configurado y accesible el escritorio remoto.
Vamos a ver que versión de nmap tenemos instalada.
>>> nmap.PortScanner().nmap_version()
(7, 91)
Y comenzamos creando un objeto del tipo nmap.PortScanner con el siguiente comando:
ep = nmap.PortScanner()
Y vamos a lanzar el escaneo usando el método scan de nmap.PortScanner()
ep.scan(‘192.168.0.1’,’21,22,80,443,3389’,’-v’)
Hemos usado la opción verbose (-v) para mostrar el resultado en la consola, sin esta opción no se habría mostrado ningún resultado. Así podemos ver que lo que el resultado del escaneo es un diccionario con claves como command_line, scaninfo, services, etc.
Vamos a observar el comando utilizado para realizar el escaneado con command_line() el cual nos muestra lo que se ha pasado a nmap para obtener ese resultado.
>>> ep.command_line()
'nmap -oX - -p 21,22,80,443,3389 -v 192.168.0.1'
Para ver una lista de todos los hosts envueltos en el escaneo usamos el método all_hosts(), en este caso concreto solo hay uno.
>>> ep.all_hosts()
['192.168.0.1']
Pero esta lista sería diferente si nuestro escaneo hubiera sido otro, por ejemplo:
ep.scan(‘192.168.0.1-10’,’21,22,80,443,3389’)
En ese caso habríamos obtenido una lista con los siguientes resultados
['192.168.0.1', '192.168.0.10', '192.168.0.2', '192.168.0.3', '192.168.0.4', '192.168.0.5', '192.168.0.6', '192.168.0.7', '192.168.0.8', '192.168.0.9'].
Dejando de lado este último dato y volviendo al anterior, podríamos ver el diccionario obtenido para ese host escribiendo lo siguiente:
>>> ep[‘192.168.0.1’]
El resultado sería algo parecido a esto que contiene información de los puertos, nombres, estados, razones, etc.
{'hostnames': [{'name': '', 'type': ''}], 'addresses': {'ipv4': '192.168.0.1'}, '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': '', 'version': '', 'extrainfo': '', 'conf': '3', 'cpe': ''}, 80: {'state': 'open', 'reason': 'syn-ack', 'name': 'http', 'product': '', 'version': '', 'extrainfo': '', 'conf': '3', 'cpe': ''}, 443: {'state': 'open', 'reason': 'syn-ack', 'name': 'https', 'product': '', 'version': '', 'extrainfo': '', 'conf': '3', 'cpe': ''}, 3389: {'state': 'closed', 'reason': 'conn-refused', 'name': 'ms-wbt-server', 'product': '', 'version': '', 'extrainfo': '', 'conf': '3', 'cpe': ''}}}
Aquí está toda la información para ese host. Por ejemplo podemos saber si el host está vivo revisando el estado del host con state().
>>> ep['192.168.0.1'].state()
'up'
Los protocolos usados:
>>> ep['192.168.0.1'].all_protocols()
['tcp']
Podemos revisar el estado del puerto tcp 80.
>>> ep[‘192.168.0.1’]['tcp'][80]
{'state': 'open', 'reason': 'syn-ack', 'name': 'http', 'product': '', 'version': '', 'extrainfo': '', 'conf': '3', 'cpe': ''}
Leído al revés se entiende mejor, puerto 80, protocolo tcp del host 192.168.0.1.
También vamos a mirar el estado del puerto 80.
>>> ep[‘192.168.0.1’]['tcp'][80]['state']
'open'
La razón de su estado, que indica el método usado para averiguar si está abierto.
>>> ep[‘192.168.0.1’]['tcp'][80]['reason']
'syn-ack'
Y el nombre del servicio.
>>> ep[‘192.168.0.1’]['tcp'][80]['name']
'http'
Si quisiéramos obtener todos estos datos en un formato mas legible podríamos usar el método csv de la siguiente forma.
>>> ep.csv()
'host;hostname;hostname_type;protocol;port;name;state;product;extrainfo;reason;version;conf;cpe\r\n192.168.0.1;;;tcp;21;ftp;closed;;;conn-refused;;3;\r\n192.168.0.1;;;tcp;22;ssh;open;;;syn-ack;;3;\r\n192.168.0.1;;;tcp;80;http;open;;;syn-ack;;3;\r\n192.168.0.1;;;tcp;443;https;open;;;syn-ack;;3;\r\n192.168.0.1;;;tcp;3389;ms-wbt-server;closed;;;conn-refused;;3;\r\n'
El resultado podríamos importarlo en cualquier hoja de cálculo.
host | hostname | hostname_type | protocol | port | name | state | product | extrainfo | reason | version | conf | cpe |
192.168.0.1 | tcp | 21 | ftp | closed | conn-refused | 3 | ||||||
192.168.0.1 | tcp | 22 | ssh | open | syn-ack | 3 | ||||||
192.168.0.1 | tcp | 80 | http | open | syn-ack | 3 | ||||||
192.168.0.1 | tcp | 443 | https | open | syn-ack | 3 | ||||||
192.168.0.1 | tcp | 3389 | ms-wbt-server | closed | conn-refused | 3 |
Si te ha sido de utilidad, por favor, deja un comentario.
Gracias 😉
Hi Mr.
I would like to add parameters.
How could I do it?
Thank you, great web.
Hi Henry.
Thank you for your comment.
You can add parameters by writing then inside the parentheses in the third position.
For example, if you want to do a standard tcp scan, you can add something like this:
ep.PortScanner(‘192.168.0.1’ , ‘21,22,80,443,3306,3389,8080’ , ‘ -sT -v’)
In this scenario -sT will do it.
Or for a full and «fast» scan you can use -A and -T4 like this.
ep.PortScanner(‘192.168.0.1’ , ‘21,22,80,443,3306,3389,8080’ , ‘-v -A -T4’)
And you will get even more data like OS name, service names and versions, etc.
Hola amigo sabes como pedir que muestre solo los puertos que esten abiertos y para hacer el escaneo mas rapido a lo que tengo entendido la velocidad del escaneo es depende de los que le ponga en el arguments
graciass, buena web
Hola Jose.
Nmap tiene una opción
--open
que sirve para mostrar los puertos abiertos, por lo que puedes incluirlo de la siguiente manera:ep.scan(‘192.168.0.1’,’21,22,80,443,3389’,’-v --open’)
Con relación a la velocidad de escaneo, depende de varios factores, como la cantidad de puertos, el tiempo que dedique Nmap en escanear. Puedes probar con la opción
-T
y un valor de 0 a 5. El modo por defecto es-T3
, en los valores más bajos Nmap dedica más tiempo al escaneo y en los valores más altos, asume que la red es rápida y escanea rápido, pero en este último caso puede no obtener toda la información que necesitemos.Puedes encontrar mas información aquí: https://nmap.org/book/man-performance.html
Saludos
Manuel
Hola buenas,
¿Sabes cómo usar la opcion root_require() que tiene la clase Nmap para ser usada como root?
Ahora mismo estoy lanzándolo con subprocess y no sé cómo implementarlo.
Un saludo
Hola Jack.
No conozco la opción que indicas, pero si usas subprocess puedes probar a incluir sudo.
Saludos
Hola Manuel
se podrá pasar los host desde un archivo:
ns.scan(«host=/carpeta/equipos.txt -p22 -sC –version-intensity 9 -sS -sV –open -PE -T5»)
sin usar la opción -iL de Nmap.
Gracias…!!!
Saludos.-
Hola Daniel.
Puedes leer los datos de hosts de un fichero línea a línea y escanear cada uno de los hosts.
with open('/carpeta/equipos.txt') as file:
for host in file:
ns.scan(host,'...')
Saludos
Manuel
Hola Manuel.
de esta manera ns.scan(host,’…’), la función ns.scan toma la palabra host como HOSTNAME y quiere escanear un equipo con ese nombre, si en la red ubiese un equipo con HOSTNAME=host lo escanearia, no pasa como una variable.Adjunto el breve código:
import nmap
with open(‘/carpeta/equipos.txt’) as file:
for host in file:
ns = nmap.PortScanner()
ns.scan(host, ‘ -p22 -sC –version-intensity 9 -sS -sV –open -PE -T5’)
supongamos que utilizo el string server01 en lugar de host, el código escanea el equipo server01 que si existe en la red.Desde ya muchas gracias.
Saludos.
Daniel.-
Hola Daniel.
Te paso un breve ejemplo de como lo haría yo:
import nmap
ns = nmap.PortScanner()
with open('./equipos.txt','r') as file:
hosts = file.readlines()
for host in hosts:
print(f'Escaneando: {host}')
print(ns.scan(host, '80'))
Toma nota de que el código en el post no muestra las tabulaciones correctamente
En el ejemplo, primero importamos nmap (previamente hemos instalado el paquete con
pip install python-nmap
).Segundo creamos un objeto de la clase nmap
Luego abrimos la conexión al fichero y con
readlines()
pasamos el contenido a una lista que se llama hosts.Seguidamente recorremos la lista
for host in hosts:
y escaneamos el puerto 80.Da igual que el nombre de host sea un hostname o una ip, siempre que tu servidor de DNSs pueda resolver esos hostnames.
Saludos
Manuel
Excelente Manuel Muchas pero Muchas gracias, yo escaneo y guardo los resultados en un base mysql y quería agregar una barra de progreso con «tqdm»y no sabia como incluir el escaneo dentro de un FOR .
Nuevamente muchas gracias …!!!
Saludos.-
De nada, gracias a ti por leer el blog y por los comentarios.
Saludos
Manuel