4

I'm working on a project with Python 2.6 on linux and I am using nmap from the terminal to get those scan result, I get this inside a string:

Starting Nmap 6.47 ( http://nmap.org ) at 2015-06-28 23:15 IDT
Nmap scan report for 10.0.0.5
Host is up (0.010s latency).
Not shown: 999 closed ports
PORT      STATE SERVICE
62078/tcp open  iphone-sync
MAC Address: 70:3E:AC:22:A0:B0 (Unknown)
Device type: general purpose|media device|phone
Running: Apple Mac OS X 10.7.X|10.9.X|10.8.X, Apple iOS 4.X|5.X|6.X
OS CPE: cpe:/o:apple:mac_os_x:10.7 cpe:/o:apple:mac_os_x:10.9 cpe:/o:apple:mac_os_x:10.8 cpe:/o:apple:iphone_os:4 cpe:/a:apple:apple_tv:4 cpe:/o:apple:iphone_os:5 cpe:/o:apple:iphone_os:6
OS details: Apple Mac OS X 10.7.0 (Lion) - 10.9.2 (Mavericks) or iOS 4.1 - 7.1 (Darwin 10.0.0 - 14.0.0)
Network Distance: 1 hop

Nmap scan report for 10.0.0.16
Host is up (0.011s latency).
Not shown: 998 closed ports
PORT     STATE SERVICE
443/tcp  open  https
1111/tcp open  lmsocialserver
MAC Address: 94:EB:CD:2E:39:DC (Research In Motion Limited)
Device type: phone
Running: RIM BlackBerry 10.X
OS CPE: cpe:/o:blackberry:blackberry_os:10.0
OS details: BlackBerry 10
Network Distance: 1 hop

Nmap scan report for 10.0.0.30
Host is up (0.11s latency).
Not shown: 984 closed ports
PORT     STATE SERVICE
80/tcp   open  http
139/tcp  open  netbios-ssn
443/tcp  open  https
445/tcp  open  microsoft-ds
515/tcp  open  printer
631/tcp  open  ipp
6839/tcp open  unknown
7435/tcp open  unknown
8080/tcp open  http-proxy
9100/tcp open  jetdirect
9101/tcp open  jetdirect
9102/tcp open  jetdirect
9110/tcp open  unknown
9111/tcp open  DragonIDSConsole
9220/tcp open  unknown
9290/tcp open  unknown
MAC Address: EC:9A:74:98:3C:9D (Hewlett Packard)
Device type: printer|VoIP adapter
Running: HP VxWorks, Vocality embedded
OS CPE: cpe:/o:hp:vxworks
OS details: VxWorks: HP printer or Vocality BASICS Four Wire VoIP gateway
Network Distance: 1 hop

Nmap scan report for 10.0.0.4
Host is up (0.000040s latency).
All 1000 scanned ports on 10.0.0.4 are closed
Too many fingerprints match this host to give specific OS details
Network Distance: 0 hops

OS detection performed. Please report any incorrect results at http://nmap.org/submit/ .
Nmap done: 50 IP addresses (4 hosts up) scanned in 56.74 second

I want to parse it into something like this: (example of the first block)

['10.0.0.5', [['62078/tcp', 'open', 'iphone-sync']], '70:3E:AC:22:A0:B0', 'Apple Mac OS X 10.7.0 (Lion) - 10.9.2 (Mavericks) or iOS 4.1 - 7.1 (Darwin 10.0.0 - 14.0.0)']

this is the pattern I want [IP, [list of [port, state, service], MAC, OS] Is there an easy way to do that with Python?

dlavila
  • 1,204
  • 11
  • 25
ori
  • 369
  • 2
  • 6
  • 17
  • 2
    Have you done *any* research of your own so far? – jonrsharpe Jun 28 '15 at 21:08
  • I didn't know what to exactly do I search in this situation, I tried using loops over the data but it didn't work well.. – ori Jun 28 '15 at 21:11
  • ...I'd be inclined to start by Googling *"python nmap parser"* and see how far that got me. See http://meta.stackoverflow.com/q/261592/3001761 – jonrsharpe Jun 28 '15 at 21:12
  • Nmap's text output is unsuitable for programmable parsing because it changes from version to version. Use [XML output](http://nmap.org/book/output-formats-xml-output.html) for that. – bonsaiviking Jun 29 '15 at 14:08

2 Answers2

7

Have you looked at the following Python modules?

  • python-nmap: This is a python class to use nmap and access scan results from python3
  • python-libnmap: Python NMAP library enabling you to start async nmap tasks, parse and compare/diff scan results

In particular, python-nmap is exactly what you want to do.

python-nmap usage example:

>>> import nmap
>>> nm = nmap.PortScanner()
>>> nm.scan('127.0.0.1', '22-443')
>>> nm.command_line()
'nmap -oX - -p 22-443 -sV 127.0.0.1'
>>> nm.scaninfo()
{'tcp': {'services': '22-443', 'method': 'connect'}}
>>> nm.all_hosts()
['127.0.0.1']
>>> nm['127.0.0.1'].hostname()
'localhost'
>>> nm['127.0.0.1'].state()
'up'
>>> nm['127.0.0.1'].all_protocols()
['tcp']
>>> nm['127.0.0.1']['tcp'].keys()
[80, 25, 443, 22, 111]
>>> nm['127.0.0.1'].has_tcp(22)
True
>>> nm['127.0.0.1'].has_tcp(23)
False
>>> nm['127.0.0.1']['tcp'][22]
{'state': 'open', 'reason': 'syn-ack', 'name': 'ssh'}
>>> nm['127.0.0.1'].tcp(22)
{'state': 'open', 'reason': 'syn-ack', 'name': 'ssh'}
>>> nm['127.0.0.1']['tcp'][22]['state']
'open'

>>> for host in nm.all_hosts():
>>>     print('----------------------------------------------------')
>>>     print('Host : %s (%s)' % (host, nm[host].hostname()))
>>>     print('State : %s' % nm[host].state())
>>>     for proto in nm[host].all_protocols():
>>>         print('----------')
>>>         print('Protocol : %s' % proto)
>>> 
>>>         lport = nm[host][proto].keys()
>>>         lport.sort()
>>>         for port in lport:
>>>             print ('port : %s\tstate : %s' % (port, nm[host][proto][port]['state']))
----------------------------------------------------
Host : 127.0.0.1 (localhost)
State : up
----------
Protocol : tcp
port : 22   state : open
port : 25   state : open
port : 80   state : open
port : 111  state : open
port : 443  state : open


>>> print(nm.csv())
host;protocol;port;name;state;product;extrainfo;reason;version;conf
127.0.0.1;tcp;22;ssh;open;OpenSSH;protocol 2.0;syn-ack;5.9p1 Debian 5ubuntu1;10
127.0.0.1;tcp;25;smtp;open;Exim smtpd;;syn-ack;4.76;10
127.0.0.1;tcp;53;domain;open;dnsmasq;;syn-ack;2.59;10
127.0.0.1;tcp;80;http;open;Apache httpd;(Ubuntu);syn-ack;2.2.22;10
127.0.0.1;tcp;111;rpcbind;open;;;syn-ack;;10
127.0.0.1;tcp;139;netbios-ssn;open;Samba smbd;workgroup: WORKGROUP;syn-ack;3.X;10
127.0.0.1;tcp;443;;open;;;syn-ack;;


>>> nm.scan(hosts='192.168.1.0/24', arguments='-n -sP -PE -PA21,23,80,3389')
>>> hosts_list = [(x, nm[x]['status']['state']) for x in nm.all_hosts()]
>>> for host, status in hosts_list:
>>>     print('{0}:{1}'.host)
192.168.1.0:down
192.168.1.1:up
192.168.1.10:down
192.168.1.100:down
192.168.1.101:down
192.168.1.102:down
192.168.1.103:down
192.168.1.104:down
192.168.1.105:down
[...]



>>> nma = nmap.PortScannerAsync()
>>> def callback_result(host, scan_result):
>>>     print '------------------'
>>>     print host, scan_result
>>> 
>>> nma.scan(hosts='192.168.1.0/30', arguments='-sP', callback=callback_result)
>>> while nma.still_scanning():
>>>     print("Waiting >>>")
>>>     nma.wait(2)   # you can do whatever you want but I choose to wait after the end of the scan
>>> 
192.168.1.1 {'nmap': {'scanstats': {'uphosts': '1', 'timestr': 'Mon Jun  7 11:31:11 2010', 'downhosts': '0', 'totalhosts': '1', 'elapsed': '0.43'}, 'scaninfo': {}, 'command_line': 'nmap -oX - -sP 192.168.1.1'}, 'scan': {'192.168.1.1': {'status': {'state': 'up', 'reason': 'arp-response'}, 'hostname': 'neufbox'}}}
------------------
192.168.1.2 {'nmap': {'scanstats': {'uphosts': '0', 'timestr': 'Mon Jun  7 11:31:11 2010', 'downhosts': '1', 'totalhosts': '1', 'elapsed': '0.29'}, 'scaninfo': {}, 'command_line': 'nmap -oX - -sP 192.168.1.2'}, 'scan': {'192.168.1.2': {'status': {'state': 'down', 'reason': 'no-response'}, 'hostname': ''}}}
------------------
192.168.1.3 {'nmap': {'scanstats': {'uphosts': '0', 'timestr': 'Mon Jun  7 11:31:11 2010', 'downhosts': '1', 'totalhosts': '1', 'elapsed': '0.29'}, 'scaninfo': {}, 'command_line': 'nmap -oX - -sP 192.168.1.3'}, 'scan': {'192.168.1.3': {'status': {'state': 'down', 'reason': 'no-response'}, 'hostname': ''}}}

>>> nm = nmap.PortScannerYield()
>>> for progressive_result in nm.scan('127.0.0.1/24', '22-25'): print(progressive_result)
fferri
  • 18,285
  • 5
  • 46
  • 95
0

If you just want to parse the xml results, you can use xmltodict:

import xmltodict
import json

with open("nmap_results.xml") as f:
    xml = f.read()
d = json.loads(json.dumps(xmltodict.parse(xml)))
# d = xmltodict.parse(xml) # you may also try using xmltodict directly without using json, but I'd some issues doing so, give it a try.

To write the nmap results to xml, use the -oX arg, i.e.:

nmap -oX nmap_results.xml x.x.x.x

Pedro Lobito
  • 94,083
  • 31
  • 258
  • 268