Quick and dirty Python: masscan
Those who know me are aware that I am a recovering shell programmer. I have 35+ years of various shell scripts involving complicated code pipelines with grep, cut, sort, uniq, awk, input files, output files, redirects, pipes etc...cobbled together to get jobs done. None of it is elegant and little of it could be called pretty. The last couple of years I have been trying to ramp up on Python and am increasingly finding that these complicated shell code scripts can be elegantly implemented in Python. The resulting code is way easier to read and way more supportable.
A simple example of this is the various scripts I have around as simple port scanners used to scan large swaths of IP address ranges for vulnerabilities. Since nmap is too slow for large numbers of IPs, my tool of choice for initial scanning of swaths of IPs and ports is the very speedy masscan. masscan will find the open ports and then typically I will write the results to a file, manipulate the masscan output file to create an input file that nmap will read and then launch nmap to do the detailed scanning on the smaller set of IPs sending that output to even more files which then need to be manipulated and analyzed to extract the information I need.
Just recently I discovered there is a Python module for both masscan and nmap. So far I have only spent time on the masscan module.
Suppose you needed a script which will find all the web servers (port 80, 443) in an address range. It took me about 5 minutes to code up scan_web.py.
#!/usr/local/bin/python3
import sys,getopt,argparse
import masscan
import pprint
def main():
# read in the IP parameter
parser = argparse.ArgumentParser()
parser.add_argument('IP', help="IP address or range")
args=parser.parse_args()
ip=args.IP
#scan address(es) using Masscan
try:
mas = masscan.PortScanner()
mas.scan(ip, ports='80,443')
except:
print("Error:", sys.exc_info()[0])
sys.exit(1)
# output result
pprint.pprint(mas.scan_result)
if __name__ == "__main__":
main()
The script takes IP address(es) as an input and then scans those IPs using masscan to check if port 80 or 443 are open.
Running the script results in:
# ./scan_web.py 45.60.103.0,45.60.31.34
[2021-05-04 20:05:28,652] [DEBUG] [masscan.py 10 line] Scan parameters: "masscan -oX - 45.60.103.0,45.60.31.34 -p 80,443"
{'masscan': {'command_line': 'masscan -oX - 45.60.103.0,45.60.31.34 -p 80,443',
'scanstats': {'downhosts': '0',
'elapsed': '12',
'timestr': '2021-05-04 20:05:41',
'totalhosts': '4',
'uphosts': '4'}},
'scan': {'45.60.103.0': {'tcp': {80: {'endtime': '1620158730',
'reason': 'syn-ack',
'reason_ttl': '53',
'services': [],
'state': 'open'},
443: {'endtime': '1620158730',
'reason': 'syn-ack',
'reason_ttl': '53',
'services': [],
'state': 'open'}}},
'45.60.31.34': {'tcp': {80: {'endtime': '1620158730',
'reason': 'syn-ack',
'reason_ttl': '61',
'services': [],
'state': 'open'},
443: {'endtime': '1620158730',
'reason': 'syn-ack',
'reason_ttl': '61',
'services': [],
'state': 'open'}}}}}
The result is a Python dictionary that can be easily be parsed and fed into python-nmap (an exercise for another day).
Caveat1: Never scan an IP range you don't have permission to scan. While port scanning is not illegal in most jurisdictions it is questionable ethically to scan things you don't own or have permission to scan.
Caveat2: I am not a professional Python programmer. My scripting gets the job done that I need it to do. I know there are many smart people out there who can write way better code than I can.
-- Rick Wanner MSISE - rwanner at isc dot sans dot edu - Twitter:namedeplume (Protected)
Comments
I tried 3 different nmap frameworks for Python, including the one you mentioned. The one i ended up going with for Netdelta.io was python-libnmap (https://github.com/savon-noir/python-libnmap) .. mostly because it gave me the flexibility i need and was well-documented.
Anonymous
May 4th 2021
3 years ago