Para realizar pruebas sobre una red o equipo -casi- todos pensamos, en seguida, en nmap. Muchas veces queremos automatizar ciertas tareas y cuando interviene nmap, con el módulo python-nmap, podemos programarlas con este maravilloso lenguaje. Si, por ejemplo, necesitamos saber si un equipo tiene abierto un puerto determinado, si está «vivo»,… podemos fácilmente usar la funcionalidad de nmap e incluirla en nuestro programa sin necesidad de hacer invocaciones de comandos del sistema.
Esta librería nos proporciona varias clases que implementan la funcionalidad, síncrona y asíncrona, de nmap. PortScanner es una de ellas y es la que se utiliza en el sencillo guión que se muestra como ejemplo y que permite comprobar el estado de uno o varios puertos indicados por parámetro. Antes de que se me olvide, en esta página podéis encontrar información.
#!/usr/bin/python try: import optparse, sys, re, socket except: print("Error executing 'import optparse, sys, re, socket' command") exit(1) try: import nmap except: print "Error importing nmap module. It is possible you need running 'apt-get install python-nmap'" exit(1) def checkIp(ip): if (re.match("^((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?).){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$",ip)): host = ip elif re.match(r'^[a-zA-Z0-9_]{,63}(.[a-zA-Z0-9_]{,63}){3}$',ip): #("^W+(.W)$", ip): host = socket.gethostbyname(ip) else: host="" print "Name/IP host wrong. You have to write a FQDN name or a IP direction" exit(3) return host def main(): parser = optparse.OptionParser("usage%prog " + "-H <target host> -p <target port> + -N <target_network>") parser.add_option('-H', dest = 'host', type = 'string', help = 'Please, specify the target host') parser.add_option('-P', dest = 'protocol', type = 'string', help = 'Please, specify the transport protocol: tcp or udp') parser.add_option('-p', dest = 'ports', type = 'string', help = 'Please, specify the target port(s) separated by comma') (options, args) = parser.parse_args() if (options.host == None): print '[-] You must specify a target host or a target network.' exit(0) if (options.ports == None): print '[-] You must specify a target port(s).' exit(0) if ((options.protocol == None) or (options.protocol != "tcp" and options.protocol != "udp")): print '[-] You must specify the transport protocol: tcp or udp.' exit(0) protocol = options.protocol host=checkIp(options.host) ports = options.ports.split(',') scanner=nmap.PortScanner() for port in ports: try: print('Analizing %s, %s/%d' % (host, protocol,(int(port)))) if (protocol != "tcp"): scanner.scan(host, port, '-sU') else: scanner.scan(host, port) print " [+] "+ host + "(" + scanner[host].state() + ") " + protocol +"/" + port + "->" + scanner[host][protocol][int(port)]['state'] except nmap.PortScannerError: print('Nmap ERROR: %s ' % sys.exc_info()[0]) exit(2) except: print("Unexpected ERROR: %s" % sys.exc_info()[0]) exit(3) if __name__ == "__main__": main()
Lo que hace el guión es, tras el formateo de los parámetros de entrada:
- Se comprueba si con el parámetro -H se ha especificado una dirección IP válida (regexp: «^((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?).){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$»), o un nombre de dominio completamente cualificado (regexp: r’^[a-zA-Z0-9_]{,63}(.[a-zA-Z0-9_]{,63}){3}$’,ip): #(«^W+(.W)$») y, en ese caso, se resuelve por su ip (socket.gethostbyname(options.host)
- Declaramos la clase y se lanza la comprobación, en función del protocolo de transporte. Si es UDP, los permisos deben ser de root (no lo comprueba el guión –> mejora)
- Se muestra el resultado para todos los puertos indicados (opción -p), del protocolo definido (-P) y del host deseado (-H).