Permalink
Find file
Fetching contributors…
Cannot retrieve contributors at this time
executable file 418 lines (346 sloc) 13.1 KB
#!/usr/bin/python
# dotnessus_v2.py
# Python module to deal with Nessus .nessus (v2) files
# http://code.google.com/p/pynessus/
#
# Copyright (C) 2010 Dustin Seibel
#
# GNU General Public Licence (GPL)
#
# This program is free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free Software
# Foundation; either version 2 of the License, or (at your option) any later
# version.
# 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. See the GNU General Public License for more
# details.
# You should have received a copy of the GNU General Public License along with
# this program; if not, write to the Free Software Foundation, Inc., 59 Temple
# Place, Suite 330, Boston, MA 02111-1307 USA
#
# 2011-03-12: 0.1.1: Initial version
# 2011-03-16: 0.1.3: Added more regex parsers
# 2011-11-15: 0.2.0: Added better handling of parsing ReportItem attributes and
# get_scanned_ip Target method.
import sys
import re
import xml.etree.ElementTree as ET
from datetime import datetime
from StringIO import StringIO
# List all nodes in a ReportItem object that can have multiple values
MULTI_VALUED_ATTS = [
'cve',
'bid',
'xref',
]
# HOST_(START|END) date format
HOST_DATE_FORMAT = '%a %b %d %H:%M:%S %Y'
# Regex defs
re_ip = re.compile('\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}')
re_wmi_ip = re.compile('IPAddress/IPSubnet.*?(?P<value>\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})', re.I)
re_wmi_man = re.compile('Computer Manufacturer : (?P<manufacturer>.*?)\n.*?Computer Model : (?P<model>.*?)\n.*?Computer Memory : (?P<memory>\d+)\s', re.I|re.M|re.S)
re_shares = re.compile('- (?P<value>.*?)\n', re.I|re.M|re.S)
re_local_admins = re.compile('- (?P<value>.*?)\s\(', re.I|re.M|re.S)
re_wsus = re.compile('following WSUS server :.*?(?P<wsus_server>http.*?)\n.*?Updates last detected : (?P<wsus_lastdetect>.*?)\n.*?Updates last downloaded : (?P<wsus_lastdownload>.*?)\n.*?Updates last installed : (?P<wsus_lastinstall>.*?)\n.*?AUOptions : (?P<wsus_auoption>.*?)\n', re.I|re.M|re.S)
re_unix_memory = re.compile('Total memory: (?P<memory>\d+)\s', re.I)
re_unix_model = re.compile('Serial Number\s+: (?P<serial>.*?)\s.*?\nProduct Name\s+: (?P<model>.*?)(\n|$)', re.I|re.M)
re_unix_cpu = re.compile('Current Speed\s+: (?P<cpu_speed>.*?)\s*\nManufacturer\s+: (?P<cpu_vendor>.*?)\s*\nFamily\s+: (?P<cpu_model>.*?)\s*\nExternal Clock\s+: (?P<cpu_externalclock>.*?)\s*\nVersion\s+: (?P<cpu_version>.*?)\s*\nType\s+: (?P<cpu_type>.*?)($|\s*\n)', re.I|re.M)
re_domain = re.compile('^.*?smb domain name is :\s+(?P<domain>.*?)(\s|$)', re.I|re.M)
re_hostname = re.compile('^Hostname : (?P<hostname>.*?)(\\n|\\r|\s|$)', re.I)
# Plugin to regex map
# Format is plugin_id: (attribute_name, regex_object, attribute_to_parse, multi_valued)
REGEX_MAP = {
'10785': ('', re_domain, 'plugin_output', False),
'24272': ('ips', re_wmi_ip, 'plugin_output', True),
'25203': ('ips', re_ip, 'plugin_output', True),
'24270': ('', re_wmi_man, 'description', False),
'10395': ('shares', re_shares, 'plugin_output', True),
'10902': ('local_admins', re_local_admins, 'plugin_output', True),
'10860': ('local_users', re_local_admins, 'plugin_output', True),
'50859': ('', re_wsus, 'plugin_output', False),
'45433': ('', re_unix_memory, 'plugin_output', False),
'35351': ('', re_unix_model, 'plugin_output', False),
'45432': ('', re_unix_cpu, 'plugin_output', False),
'55472': ('', re_hostname, 'plugin_output', False),
}
# Local IP list
LOCAL_IP_LIST = [
'0.0.0.0',
'127.0.0.1',
]
class Report(object):
def __init__(self):
self.name = None
self.targets = []
self.scan_start = None
self.scan_end = None
def parse(self, xml_file, from_string=False):
"""Import .nessus file"""
# Parse XML file
if from_string:
xml_file = StringIO(xml_file)
# Iterate through each host scanned and create objects for each
for event, elem in ET.iterparse(xml_file):
# Grab the report name from the Report element
if event == 'end' and elem.tag == 'Report':
self.name = elem.attrib.get('name')
continue
# Only process ReportHost elements
elif event == 'end' and elem.tag != 'ReportHost':
continue
rh_obj = ReportHost(elem)
if rh_obj:
self.targets.append(rh_obj)
# Update Report dates
if not self.scan_start and rh_obj.get('host_start'):
self.scan_start = rh_obj.host_start
if not self.scan_end:
self.scan_end = rh_obj.host_end
if rh_obj.get('host_start'):
if rh_obj.host_start < self.scan_start:
self.scan_start = rh_obj.host_start
if rh_obj.host_end > self.scan_end:
self.scan_end = rh_obj.host_end
def __repr__(self):
return "<Report: %s>" % self.name
def get_target(self, name):
"""Returns a target object given a name"""
for t in self.targets:
if name.lower() == t.name.lower():
return t
class ReportHost(object):
def __init__(self, xml_report_host):
self.name = None
self.auth = False
self.dead = False
self.vulns = []
# Do a check to make sure it's well formed
# ...
# Get ReportHost name
self.name = xml_report_host.attrib.get('name')
# Get HostProperties tags
for n in xml_report_host.findall('HostProperties/tag'):
setattr(self, n.attrib.get('name'), n.text)
# Convert scan dates and check for dead status
if self.get('HOST_START'):
self.host_start = datetime.strptime(self.get('HOST_START'), HOST_DATE_FORMAT)
else:
self.dead = True
self.host_end = datetime.strptime(self.get('HOST_END'), HOST_DATE_FORMAT)
# Get all ReportItems
for ri in xml_report_host.findall('ReportItem'):
ri_obj = ReportItem(ri)
if ri_obj:
self.vulns.append(ri_obj)
xml_report_host.clear()
# Do an additional check for deadness
for v in self.find_vuln(plugin_id='10180'):
if 'dead' in str(v.get('plugin_output')):
self.dead = True
# Check to see if this was an authenticated scan
if self.get('local-checks-proto') and not self.find_vuln(plugin_id='21745'):
self.auth = True
# Parse additional fields into host attributes
for plugin_id in REGEX_MAP:
att, regex, dest_att, multi = REGEX_MAP[plugin_id]
vulns = self.find_vuln(plugin_id=plugin_id)
# If multi flag is set, store results in a dict
if multi:
results = []
# Grab all plugins
for v in vulns:
if multi:
setattr(self, att, regex.findall(v.get(dest_att)))
else:
plugin_output = v.get(dest_att)
if not plugin_output:
continue
res = regex.search(v.get(dest_att))
if not res:
continue
# Check to see if named fields were given
if res.groupdict():
# Store each named field as an attribute
for k, v in res.groupdict().iteritems():
setattr(self, k, v)
# No named fields, just grab whatever matched
else:
setattr(self, att, res.group())
def __repr__(self):
return "<ReportHost: %s>" % self.name
def get(self, attr):
"""Returns attribute value if it exists"""
try:
return getattr(self, attr)
except AttributeError:
return None
def find_vuln(self, **kwargs):
"""Find a ReportItem given the search params"""
results = []
# Iterate through vulns
for r in self.vulns:
match = True
# If one of the search criteria doesn't match, set the flag
for k in kwargs:
if kwargs.get(k) != r.get(k):
match = False
# If it's a match, add it to results
if match:
results.append(r)
return results
def get_ips(self, exclude_local=True):
"""Return a list of IPs for host"""
ip_list = set()
if re_ip.search(self.name):
ip_list.add(self.name)
if self.get('host-ip'):
ip_list.add(self.get('host-ip'))
if self.get('ips'):
ip_list.update(self.ips)
# If exclude_local is set, remove local IPs from list
if exclude_local:
for i in LOCAL_IP_LIST:
if i in ip_list:
ip_list.remove(i)
return list(ip_list)
def get_scanned_ip(self):
"""Return the IP address that was scanned"""
if re_ip.search(self.name):
return self.name
elif self.get('host-ip'):
return self.get('host-ip')
else:
return None
def get_open_ports(self):
"""Returns a dict of open ports found"""
results = {}
# Fetch results
vulns = self.find_vuln(plugin_id='0')
# For each port, put it in a dict
for v in vulns:
proto = v.get('protocol')
port = v.get('port')
if proto not in results:
results[proto] = []
results[proto].append(port)
return results
def get_name(self, fqdn=True):
"""Returns a friendly name for host"""
if re_ip.search(self.name):
if self.get('hostname'):
return self.get('hostname').lower()
elif self.get('netbios-name'):
return self.get('netbios-name').lower()
elif self.get('host-fqdn'):
if fqdn:
return self.get('host-fqdn').lower()
else:
return self.get('host-fqdn').lower().split('.')[0]
else:
return self.name
else:
return self.name
class ReportItem(object):
def __init__(self, xml_report_item):
# Make sure object is well formed
# ...
# Get ReportItem attributes
#self.port = xml_report_item.attrib.get('port')
#self.svc_name = xml_report_item.attrib.get('svc_name')
#self.protocol = xml_report_item.attrib.get('protocol')
#self.severity = xml_report_item.attrib.get('severity')
#self.plugin_id = xml_report_item.attrib.get('pluginID')
#self.plugin_name = xml_report_item.attrib.get('pluginName')
#self.plugin_family = xml_report_item.attrib.get('pluginFamily')
# Get ReportItem attributes
for k in xml_report_item.attrib.keys():
setattr(self, k, xml_report_item.attrib.get(k))
# Set some attributes to maintain backwards compat from old versions of this script
self.plugin_id = xml_report_item.attrib.get('pluginID')
self.plugin_name = xml_report_item.attrib.get('pluginName')
self.plugin_family = xml_report_item.attrib.get('pluginFamily')
# Create multi-valued atts
for m in MULTI_VALUED_ATTS:
setattr(self, m, list())
# Get optional nodes
for n in xml_report_item.getchildren():
# If it's a multi-valued att, append to list
if n.tag in MULTI_VALUED_ATTS:
v = getattr(self, n.tag)
v.append(n.text.strip())
setattr(self, n.tag, v)
continue
# If it's not a multi-valued att, store it as a string
setattr(self, n.tag, n.text.strip())
xml_report_item.clear()
def __repr__(self):
return "<ReportItem: %s/%s %s %s>" % (self.port, self.protocol, self.plugin_id, self.plugin_name)
def get(self, attr):
"""Returns attribute value if it exists"""
try:
return getattr(self, attr)
except AttributeError:
return None
#!/usr/bin/python
# PayGen (www.hackwhackandsmack.com)
# Written by Ben Turner 2016
import io, time, commands, os, subprocess, sys, signal, tempfile, readline, glob
print ""
print "\033[0;32m======================================================================="
print "\033[0;32mNessus Parser for RMI and HTTP/HTTPS services - Written by @benpturner"
print "\033[0;32m======================================================================="
print ""
def complete(text, state):
return (glob.glob(text+'*')+[None])[state]
readline.set_completer_delims(' \t\n;')
readline.parse_and_bind("tab: complete")
readline.set_completer(complete)
nessusreport = raw_input("\033[0;31mFull path to Nessus file: \033[0m")
currentdir = os.getcwd()
rpt = Report()
rpt.parse(nessusreport)
outputdir = raw_input("\033[0;31mOutput directory: [" + currentdir + "]\033[0m") or currentdir
if not (outputdir.endswith("/")):
outputdir = outputdir+"/"
webhosts = open(outputdir+'webhosts.tmp', 'w')
allaffected = open(outputdir+'rmi.txt', 'w')
# rmi object plugin id
for t in rpt.targets:
for v in t.vulns:
if v.get('plugin_id') == '22363':
allaffected.write(t.name +":"+ v.get('port')+"\n")
# rmi registry plugin id
for t in rpt.targets:
for v in t.vulns:
if v.get('plugin_id') == '22227':
allaffected.write(t.name +":"+ v.get('port')+"\n")
# get all web hosts
for t in rpt.targets:
for v in t.vulns:
if v.get('svc_name') == 'www':
webhosts.write(t.name +":"+ v.get('port')+"\n")
webhosts.close()
allaffected.close()
subprocess.call('cat '+outputdir+'webhosts.tmp | sort | uniq > '+outputdir+'webhosts.txt', shell=True)
subprocess.call('rm '+outputdir+'webhosts.tmp', shell=True)
target = open(outputdir+'rmi.rc', 'w')
target.write('use auxiliary/scanner/misc/java_rmi_server\n')
target.write('<ruby>\n')
target.write('##################################################\n')
target.write('# go through list of hosts:port and run a module #\n')
target.write('##################################################\n\n')
target.write('File.open("'+outputdir+'rmi.txt").each do |line|\n')
target.write(' linesplit = line.split(":")\n')
target.write(' self.run_single("set RPORT #{linesplit[1]}")\n')
target.write(' self.run_single("set RHOSTS #{linesplit[0]}")\n')
target.write(' self.run_single("run")\n')
target.write('end\n\n')
target.write('</ruby>\n')
target.close()
print ""
print "\033[0;32m[+] Completed successfully"
print ""
handler = raw_input("\033[0;31mStart Metasploit auxiliary module against targets [Y/n]: \033[0m") or "Y"
if (handler == "Y") or (handler =="y"):
subprocess.call('msfconsole -r '+outputdir+'rmi.rc', shell=True)