During
the reconnaissance phase of network penetration testing engagements
or black box web application testing, there will come a point when
scanning all ports on a target network simply needs to be done. In
the past I have relied on nmap for this task and indeed still do for
aggressive scanning and to interrogate discovered services. More
recently I have been testing and using ZMap to aid with
service discovery, more specifically to determine if a port is open.
ZMap has some distinct advantages over other networking scanning
tools, mainly its speed; ZMap's claim to fame is that it can be used
to scan the entire IPv4 address space in under 45 minutes... one port
of the IPv4 space anyway. Doing so requires significant bandwidth
resources such as gigabit Ethernet otherwise it may take a bit
longer. At any rate (no pun intended) it does it's job quickly and
quite efficiently.
While
ZMap can come in handy during penetration testing it can also be
quite useful from a blue team perspective. As the old adage goes,
“the best defense is a good offense” using ZMap to scan your own
corporate or private network can keep you apprised of what services
are available externally. For the home user this could mean
discovering services that your new router has available and turned on
by default. For the corporate security professional this is your
check and balance for all of the firewall rule requests that you see
being submitted or are directly responsible for approving.
Given
the scenarios above I found it useful to write a python script that
would help with the task of network discovery. From an offensive
perspective this script can be used to quickly identify open ports on
a target network or host. From a defensive perspective this script
can be used for the same thing; schedule it and validate the results
with what you expect to see then remediate as necessary in terms of
disabling firewall rules.
A
couple of things to note as this is a beta release, you will need to
identify your primary network interface and manually provide the
interface name to the script. There are several enhancements in the
pipeline, but for now it should work as long as:
- You can run it as root
- The correct interface is specified
- And the python environment is adequate
The
advantage of this script over simply using ZMap outright is the port
range option and some useful features in coming releases. Below is the
script (full lines and proper formatting are preserved even though it looks like it wraps around) and remember that this is in beta and I am not claiming to be a great python writer.
#!/bin/python ## SB 6-2014 ##Use ZMap to scan range of ports on provided network ##65535 seconds = 18.204 hours ##You may need to change the python path above and make sure the script is executable ##Expect the script to create a new directory each time it's invoked ##On line 53, change "em1" to the name of your network interface (ifconfig -a) ##This program is distributed in the hope that it will be useful, ##but WITHOUT ANY WARRANTY; without even the implied warranty of ##MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. ##import modules import subprocess import os import datetime import zipfile import argparse parser = argparse.ArgumentParser( prog="zmapScan", description="Scan ports on specified network using ZMap", formatter_class=argparse.RawDescriptionHelpFormatter, epilog=('''\ Example: ./zmapScan.py -sp 80 -ep 80 -r example.com - scan only port 80 on example.com Example: ./zmapScan.py -sp 0 -ep 65535 -r example.com - scan all ports on example.com Example: ./zmapScan.py -sp 1 -ep 1024 -r 1.2.3.4/29 - scan ports 1-1024 on a /29 tis but a scratch... ''')) parser.add_argument("-sp", help="starting port *required", dest="sp", metavar="starting port", type=int, required=True, choices=range(0, 65535)) parser.add_argument("-ep", help="ending port *required", dest="ep", metavar="ending port", type=int, required=True, choices=range(0, 65535)) parser.add_argument("-r", help="ip/hostname/range *required", dest="r", metavar="ip/hostname/range", type=str, required=True) parser.add_argument("-v", "--version", action="version", version="zmapScan version .63 (July 2014)") results=parser.parse_args() ##get the date/time of right now d = datetime.datetime.now() ##set a as the datetime like this: 06-26-2016_142003_941871 dirname = d.strftime('%m-%d-%Y_%H%M%S_%f') port = 0 ##Create a new directory everytime program is run os.mkdir(dirname) ## cd into new directory os.chdir(dirname) ##Begin ZMap work ##Have ZMap loop through ports and output one file for each port. Each file will include IPs that have that port open for port in range (results.sp, results.ep+1): cmd = "sudo zmap -p{0} -o zmapoutput_{1} -B 100k -c 2 -v 0 -q -i em1 {2}".format(port, port, results.r) subprocess.call(cmd.split(), shell=False) print "Port %s complete" % port ##End ZMap work ##Begin clean-up of ZMap output (delete any empty files) ##Get current working directory folder = os.getcwd() ##Loop through every file that ZMap outputs for file in os.listdir(folder): ## Remove empty files. Files that have data will be retained since they are useful try: if os.stat(file)[6]==0: os.remove(file) except Exception, e: print e ##End clean-up of ZMap output ##Begin zip ZMap results ## zip up result files zf = zipfile.ZipFile("ZmapResults.zip", "w") for dirname, subdirs, files in os.walk("./"): zf.write(dirname) for filename in files: zf.write(os.path.join(dirname, filename)) zf.close() ##End zip ZMap results