nmap y python. Un ejemplo

por | enero 28, 2014

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:

  1. 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)
  2. 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)
  3. Se muestra el resultado para todos los puertos indicados (opción -p), del protocolo definido (-P) y del host deseado (-H).