├── README.md ├── nmap-query-xml.py └── screenshot1.png /README.md: -------------------------------------------------------------------------------- 1 | # nmap-query-xml 2 | A simple program to query nmap xml files in the terminal. 3 | ![screenshot 1](https://raw.githubusercontent.com/honze-net/nmap-query-xml/master/screenshot1.png) 4 | 5 | ## Prerequisites 6 | You will need Python3 and python-libnmap: https://github.com/savon-noir/python-libnmap#install 7 | 8 | ## Installation 9 | ``` 10 | # Clone or download to your desired installation folder. 11 | git clone https://github.com/honze-net/nmap-query-xml 12 | # Make nmap-query-xml.py executable 13 | chmod +x nmap-query-xml/nmap-query-xml.py 14 | # Create a symbolic link in one of your $PATH folders. E. g. 15 | sudo ln -s $(pwd)/nmap-query-xml/nmap-query-xml.py /usr/bin/nmap-query-xml 16 | ``` 17 | 18 | ## Introduction 19 | The easiest usage is to call `nmap-query-xml` just with a xml file as argument. This will list all services and hosts of all open ports like this: 20 | ``` 21 | $ nmap-query-xml scanme.xml 22 | ssh://scanme.nmap.org:22 23 | http://scanme.nmap.org:80 24 | tcpwrapped://scanme.nmap.org:31337 25 | ssh://scanme2.nmap.org:22 26 | smtp://scanme2.nmap.org:25 27 | http://scanme2.nmap.org:80 28 | ssls://scanme2.nmap.org:443 29 | ``` 30 | (The scanme.xml was generated with: `nmap -sS -T4 -A -sC -oA scanme scanme.nmap.org scanme2.nmap.org`) 31 | 32 | In the beginning of an assessment (bug bounty, pentest, etc.) you might want to create your project folder structure. 33 | ``` 34 | $ nmap-query-xml scanme.xml --pattern "mkdir -p ~/project/{ip}/{protocol}/{port}" 35 | mkdir -p ~/project/45.33.32.156/tcp/22 36 | mkdir -p ~/project/45.33.32.156/tcp/80 37 | mkdir -p ~/project/45.33.32.156/tcp/31337 38 | mkdir -p ~/project/45.33.49.119/tcp/22 39 | mkdir -p ~/project/45.33.49.119/tcp/25 40 | mkdir -p ~/project/45.33.49.119/tcp/80 41 | mkdir -p ~/project/45.33.49.119/tcp/443 42 | ``` 43 | As you can see, every line is a valid `sh` command. So you can pipe this output to `sh` and it will create the project folder like this: 44 | ``` 45 | $ tree project 46 | project 47 | ├── 45.33.32.156 48 | │   └── tcp 49 | │   ├── 22 50 | │   ├── 31337 51 | │   └── 80 52 | └── 45.33.49.119 53 | └── tcp 54 | ├── 22 55 | ├── 25 56 | ├── 443 57 | └── 80 58 | ``` 59 | The `--pattern` option is a string, which is printed for every (default: open) port discovered. The key is to use variables, which are listed below. 60 | 61 | ### List of variables used in --pattern 62 | 63 | - {hostname} - The first hostname of a list of hostnames (nmap could have detected more via rDNS). If no hostname was detected, the IP will be used. 64 | - {hostnames} - Comma separated list of all hostnames. If no hostname was detected, the IP will be used. 65 | - {ip} - The IP address of the host. 66 | - {port} - The port number. 67 | - {protocol} - The protocol used (mostly tcp or udp). 68 | - {s} - This a flag for SSL/TLS tunnel usage. It is "s" if SSL/TLS is used and "" otherwise. 69 | - {service} - The service name discovered by nmap. Sometimes https is discovered as http with the SSL/TLS flag. Use {service}{s} then. 70 | - {state} - The port state nmap discovered (open, closed, filtered, etc.). 71 | - {xmlfile} - The file name (supplied as argument). Great for searching a lot of xml files for a specific host for example. 72 | 73 | 74 | If you want to add hostnames to your project, you could create symbolic links like this: 75 | 76 | In your project folder run: 77 | ``` 78 | $ nmap-query-xml scanme.xml --pattern "ln -s {ip} {hostname}" | sort -u 79 | ln -s 45.33.32.156 scanme.nmap.org 80 | ln -s 45.33.49.119 scanme2.nmap.org 81 | ``` 82 | Note that I copied the scanme.xml in the project folder before. But you can store them anywhere, so you might have to adjust the path to the scanme.xml. The `sort -u` will remove duplicates from the query. 83 | 84 | Now it will look like this: 85 | ``` 86 | $ tree 87 | . 88 | ├── 45.33.32.156 89 | │   └── tcp 90 | │   ├── 22 91 | │   ├── 31337 92 | │   └── 80 93 | ├── 45.33.49.119 94 | │   └── tcp 95 | │   ├── 22 96 | │   ├── 25 97 | │   ├── 443 98 | │   └── 80 99 | ├── scanme2.nmap.org -> 45.33.49.119 100 | ├── scanme.nmap.org -> 45.33.32.156 101 | └── scanme.xml 102 | ``` 103 | 104 | Now you start your assessment! Want to curl each http host for a `robots.txt` and save it in your project folder? 105 | ``` 106 | $ nmap-query-xml scanme.xml --service http --pattern "curl -L -o ./{ip}/{protocol}/{port}/robots.txt {service}://{hostname}:{port}/robots.txt " 107 | curl -L -o ./45.33.32.156/tcp/80/robots.txt http://scanme.nmap.org:80/robots.txt 108 | curl -L -o ./45.33.49.119/tcp/80/robots.txt http://scanme2.nmap.org:80/robots.txt 109 | ``` 110 | This will lead to: 111 | ``` 112 | $ tree 113 | . 114 | ├── 45.33.32.156 115 | │   └── tcp 116 | │   ├── 22 117 | │   ├── 31337 118 | │   └── 80 119 | │   └── robots.txt 120 | ├── 45.33.49.119 121 | │   └── tcp 122 | │   ├── 22 123 | │   ├── 25 124 | │   ├── 443 125 | │   └── 80 126 | │   └── robots.txt 127 | ├── scanme2.nmap.org -> 45.33.49.119 128 | ├── scanme.nmap.org -> 45.33.32.156 129 | └── scanme.xml 130 | ``` 131 | You could now examine both robots.txt by hand or use: 132 | ``` 133 | $ nmap-query-xml scanme.xml --service http --pattern "file ./{ip}/{protocol}/{port}/robots.txt" | sh 134 | ./45.33.32.156/tcp/80/robots.txt: HTML document, ASCII text 135 | ./45.33.49.119/tcp/80/robots.txt: ASCII text 136 | ``` 137 | As you can see, the first file contains HTML. Probably a 404 or something. The second seems fine. 138 | 139 | This concludes this rather simple introduction. I think, you got the point. Feel free to get creative! 140 | 141 | ## Tips & Tricks 142 | ### Multipe xml files 143 | If you want to use several xml files in your directory, because of multiple scans, you can call `nmap-query-xml` with each and everyone using `xargs`: 144 | ``` 145 | $ ls *.xml 146 | 1.xml 2.xml 3.xml 147 | 148 | $ ls *.xml | xargs -n1 nmap-query-xml 149 | ``` 150 | This is the equivalent of 151 | ``` 152 | $ nmap-query-xml 1.xml; nmap-query-xml 2.xml; nmap-query-xml 3.xml 153 | ``` 154 | ### Multi-threading 155 | If your scope contains a large amount of hosts and you want make your approach faster, then use GNU parallel instead of sh as a pipe: `nmap-query-xml your.xml --service http --pattern "yourtool {service}://{hostname}:{port}" | parallel --bar -j8` This will use 8 threads and show a progress bar. 156 | 157 | 158 | 159 | 160 | 161 | -------------------------------------------------------------------------------- /nmap-query-xml.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | # This software must not be used by military or secret service organisations. 4 | # License: TODO 5 | # TODO: PEP 8 6 | 7 | import sys, argparse 8 | from libnmap.parser import NmapParser 9 | 10 | greeter = ''' 11 | ▐ ▄ • ▌ ▄ ·. ▄▄▄· ▄▄▄· .▄▄▄ ▄• ▄▌▄▄▄ .▄▄▄ ▄· ▄▌ ▐▄• ▄ • ▌ ▄ ·. ▄▄▌ 12 | •█▌▐█·██ ▐███▪▐█ ▀█ ▐█ ▄█ ▐▀•▀█ █▪██▌▀▄.▀·▀▄ █·▐█▪██▌ █▌█▌▪·██ ▐███▪██• 13 | ▐█▐▐▌▐█ ▌▐▌▐█·▄█▀▀█ ██▀· █▌·.█▌█▌▐█▌▐▀▀▪▄▐▀▀▄ ▐█▌▐█▪ ·██· ▐█ ▌▐▌▐█·██▪ 14 | ██▐█▌██ ██▌▐█▌▐█ ▪▐▌▐█▪·• ▐█▪▄█·▐█▄█▌▐█▄▄▌▐█•█▌ ▐█▀·. ▪▐█·█▌██ ██▌▐█▌▐█▌▐▌ 15 | ▀▀ █▪▀▀ █▪▀▀▀ ▀ ▀ .▀ ·▀▀█. ▀▀▀ ▀▀▀ .▀ ▀ ▀ • •▀▀ ▀▀▀▀ █▪▀▀▀.▀▀▀ ''' 16 | for c in "█▐▌▄▀": 17 | greeter = greeter.replace(c, "\u001b[32m" + c + "\u001b[0m") 18 | version = "0.0.3#beta" 19 | url = "https://github.com/honze-net/nmap-query-xml" 20 | 21 | parser = argparse.ArgumentParser(description="", epilog="Full documentation: %s\nThis software must not be used by military or secret service organisations." % url, formatter_class=argparse.RawDescriptionHelpFormatter) 22 | parser.add_argument("xml", help="path to Nmap XML file") 23 | parser.add_argument("--service", help="Nmap service name to filter for. Default: Empty", default="", dest="service") 24 | parser.add_argument("--pattern", help="Pattern for output. Default: %(default)s", default="{service}{s}://{hostname}:{port}", dest="pattern") 25 | parser.add_argument("--state", help="Select a port state. Use \"all\" for all. Default: %(default)s", default="open", dest="state") 26 | 27 | if len(sys.argv) == 1: # If no arguments are specified, print greeter, help and exit. 28 | print(greeter) 29 | print(("version %s %s\n" % (version, url)).center(80)) 30 | parser.print_help() 31 | sys.exit(0) 32 | args = parser.parse_args() 33 | 34 | try: 35 | report = NmapParser.parse_fromfile(args.xml) 36 | except IOError: 37 | print("Error: File %s not found." % args.xml) 38 | sys.exit(1) 39 | 40 | for host in report.hosts: 41 | for service in host.services: 42 | if (service.state == args.state or args.state == "all") and (args.service == "" or service.service in args.service.split(",")): # TODO: Test if this is precise enough 43 | line = args.pattern 44 | line = line.replace("{xmlfile}", args.xml) 45 | line = line.replace("{hostname}", host.address if not host.hostnames else host.hostnames[0]) # TODO: Fix naive code. 46 | line = line.replace("{hostnames}", host.address if not host.hostnames else ", ".join(list(set(host.hostnames)))) # TODO: Fix naive code. 47 | line = line.replace("{ip}", host.address) 48 | line = line.replace("{service}", service.service) 49 | line = line.replace("{s}", "s" if service.tunnel == "ssl" else "") 50 | line = line.replace("{protocol}", service.protocol) 51 | line = line.replace("{port}", str(service.port)) 52 | line = line.replace("{state}", str(service.state)) 53 | print("%s" % line) 54 | -------------------------------------------------------------------------------- /screenshot1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/honze-net/nmap-query-xml/fcb53686f9f437ad54e112da80adab777f1abb2a/screenshot1.png --------------------------------------------------------------------------------