Skip to content

Commit

Permalink
Revert "Function name randomization (#109)" (#112)
Browse files Browse the repository at this point in the history
This reverts commit d4dab8e.
  • Loading branch information
Cx01N committed Mar 2, 2020
1 parent d4dab8e commit 953db5c
Show file tree
Hide file tree
Showing 20 changed files with 24 additions and 514 deletions.
11 changes: 5 additions & 6 deletions README.md
Expand Up @@ -24,10 +24,9 @@
Keep up-to-date on our blog at [https://www.bc-security.org/blog][7]

# Empire
## The beta release of [Empire 3.0](https://github.com/BC-SECURITY/Empire/tree/dev) is available on the dev branch ##
Empire 3.0 is a post-exploitation framework that includes a pure-PowerShell 2.0 Windows agent, and compatibility with Python 2.x/3.x Linux/OS X agents. It is the merger of the previous PowerShell Empire and Python EmPyre projects. The framework offers cryptologically-secure communications and a flexible architecture. On the PowerShell side, Empire implements the ability to run PowerShell agents without needing powershell.exe, rapidly deployable post-exploitation modules ranging from key loggers to Mimikatz, and adaptable communications to evade network detection, all wrapped up in a usability-focused framework. PowerShell Empire premiered at [BSidesLV in 2015](https://www.youtube.com/watch?v=Pq9t59w0mUI) and Python EmPyre premeiered at HackMiami 2016. BC-Security presented updates to further evade Microsoft Antimalware Scan Interface (AMSI) and JA3/S signatures at [DEF CON 27](https://github.com/BC-SECURITY/DEFCON27).

Empire relies heavily on the work from several other projects for its underlying functionality. We have tried to call out a few of those people we've interacted with [heavily here](http://www.powershellempire.com/?page_id=2) and have included author/reference link information in the source of each Empire module as appropriate. If we have failed to improperly cite existing or prior work, please let us know.
Empire relies heavily on the work from several other projects for its underlying functionality. We have tried to call out a few of those people we've interacted with [heavily here](http://www.powershellempire.com/?page_id=2) and have included author/reference link information in the source of each Empire module as appropriate. If we have failed to properly cite existing or prior work, please let us know at Empire@BC-Security.org.

Empire is developed by [@harmj0y](https://twitter.com/harmj0y), [@sixdub](https://twitter.com/sixdub), [@enigma0x3](https://twitter.com/enigma0x3), [@rvrsh3ll](https://twitter.com/424f424f), [@killswitch_gui](https://twitter.com/killswitch_gui), [@xorrior](https://twitter.com/xorrior), and [@bcsecurity1](https://twitter.com/BCSecurity1). While the main fork for Empire is no longer maintained, this fork is maintained by [BC-Security](https://www.bc-security.org) and will continue to receive periodic updates.

Expand Down Expand Up @@ -76,13 +75,13 @@ Check out the [Empire wiki](https://github.com/EmpireProject/Empire/wiki/Quickst

## To Do List

* Port code to work with Python 3
* ~~Port code to work with Python 3~~
* [Invoke-SocksProxy](https://github.com/p3nt4/Invoke-SocksProxy)
* Function name randomization
* JA3/S signature randomization
* Multi-menu function calls
* ~~JA3/S signature randomization~~
* ~~Multi-menu function calls~~
* Function name aliasing
* Update to [Mimikatz 2.2.0](https://github.com/gentilkiwi/mimikatz)
* ~~Update to [Mimikatz 2.2.0](https://github.com/gentilkiwi/mimikatz)~~

## Contribution Rules

Expand Down
42 changes: 3 additions & 39 deletions lib/listeners/http.py
Expand Up @@ -12,7 +12,6 @@
import copy
import json
import sys
import threading
from pydispatch import dispatcher
from flask import Flask, request, make_response, send_from_directory
# Empire imports
Expand Down Expand Up @@ -167,27 +166,13 @@ def __init__(self, mainMenu, params=[]):

# randomize the length of the default_response and index_page headers to evade signature based scans
self.header_offset = random.randint(0, 64)

# used to protect self.http and self.mainMenu.conn during threaded listener access
self.lock = threading.Lock()


self.session_cookie = ''

# check if the current session cookie not empty and then generate random cookie
if self.session_cookie == '':
self.options['Cookie']['Value'] = self.generate_cookie()

# this might not be necessary. Could probably be achieved by just callingg mainmenu.get_db but all the other files have
# implemented it in place. Might be worthwhile to just make a database handling file
def get_db_connection(self):
"""
Returns the cursor for SQLlite DB
"""
self.lock.acquire()
self.mainMenu.conn.row_factory = None
self.lock.release()
return self.mainMenu.conn


def default_response(self):
"""
Returns an IIS 7.5 404 not found page.
Expand Down Expand Up @@ -573,17 +558,7 @@ def generate_stager(self, listenerOptions, encode=False, encrypt=True, obfuscate
f = open("%s/data/agent/stagers/http.ps1" % (self.mainMenu.installPath))
stager = f.read()
f.close()

# Get the random function name generated at install and patch the stager with the proper function name
conn = self.get_db_connection()
self.lock.acquire()
cur = conn.cursor()
cur.execute("SELECT Invoke_Empire FROM functions")
replacement = cur.fetchone()
cur.close()
self.lock.release()
stager = stager.replace("Invoke-Empire", replacement[0])


# make sure the server ends with "/"
if not host.endswith("/"):
host += "/"
Expand Down Expand Up @@ -703,17 +678,6 @@ def generate_agent(self, listenerOptions, language=None, obfuscate=False, obfusc
f = open(self.mainMenu.installPath + "./data/agent/agent.ps1")
code = f.read()
f.close()

# Get the random function name generated at install and patch the stager with the proper function name
conn = self.get_db_connection()
self.lock.acquire()
cur = conn.cursor()
cur.execute("SELECT Invoke_Empire FROM functions")
replacement = cur.fetchone()
cur.close()
self.lock.release()

code = code.replace("Invoke-Empire", replacement[0])

# patch in the comms methods
commsCode = self.generate_comms(listenerOptions=listenerOptions, language=language)
Expand Down
45 changes: 4 additions & 41 deletions lib/listeners/http_com.py
Expand Up @@ -10,7 +10,6 @@
import copy
import json
import sys
import threading
from pydispatch import dispatcher
from flask import Flask, request, make_response, send_from_directory

Expand Down Expand Up @@ -144,23 +143,9 @@ def __init__(self, mainMenu, params=[]):
# set the default staging key to the controller db default
self.options['StagingKey']['Value'] = str(helpers.get_config('staging_key')[0])

# used to protect self.http and self.mainMenu.conn during threaded listener access
self.lock = threading.Lock()

# randomize the length of the default_response and index_page headers to evade signature based scans
self.header_offset = random.randint(0,64)

# this might not be necessary. Could probably be achieved by just callingg mainmenu.get_db but all the other files have
# implemented it in place. Might be worthwhile to just make a database handling file
def get_db_connection(self):
"""
Returns the cursor for SQLlite DB
"""
self.lock.acquire()
self.mainMenu.conn.row_factory = None
self.lock.release()
return self.mainMenu.conn

def default_response(self):
"""
Returns an IIS 7.5 404 not found page.
Expand Down Expand Up @@ -396,16 +381,6 @@ def generate_stager(self, listenerOptions, encode=False, encrypt=True, obfuscate
stager = f.read()
f.close()

# Get the random function name generated at install and patch the stager with the proper function name
conn = self.get_db_connection()
self.lock.acquire()
cur = conn.cursor()
cur.execute("SELECT Invoke_Empire FROM functions")
replacement = cur.fetchone()
cur.close()
self.lock.release()
stager = stager.replace("Invoke-Empire", replacement[0])

# make sure the server ends with "/"
if not host.endswith("/"):
host += "/"
Expand Down Expand Up @@ -488,17 +463,6 @@ def generate_agent(self, listenerOptions, language=None, obfuscate=False, obfusc
code = f.read()
f.close()


conn = self.get_db_connection()
self.lock.acquire()
cur = conn.cursor()
cur.execute("SELECT Invoke_Empire FROM functions")
replacement = cur.fetchone()
cur.close()
self.lock.release()

code = code.replace("Invoke-Empire", replacement[0])

# patch in the comms methods
commsCode = self.generate_comms(listenerOptions=listenerOptions, language=language)
code = code.replace('REPLACE_COMMS', commsCode)
Expand Down Expand Up @@ -782,7 +746,7 @@ def handle_get(request_uri):
listenerName = self.options['Name']['Value']
message = "[*] Agent from {} retrieved taskings".format(clientIP)
signal = json.dumps({
'print': True,
'print': False,
'message': message
})
dispatcher.send(signal, sender="listeners/http_com/{}".format(listenerName))
Expand Down Expand Up @@ -824,7 +788,6 @@ def handle_post(request_uri):
if dataResults and len(dataResults) > 0:
for (language, results) in dataResults:
if isinstance(results, str):

results = results.encode('UTF-8')
if results:
if results.startswith(b'STAGE2'):
Expand Down Expand Up @@ -856,11 +819,11 @@ def handle_post(request_uri):
})
dispatcher.send(signal, sender="listeners/http_com/{}".format(listenerName))
return make_response(self.default_response(), 200)
elif results == 'VALID':
elif results == b'VALID':
listenerName = self.options['Name']['Value']
message = "[*] Valid results return by {}".format(clientIP)
signal = json.dumps({
'print': True,
'print': False,
'message': message
})
dispatcher.send(signal, sender="listeners/http_com/{}".format(listenerName))
Expand Down Expand Up @@ -891,7 +854,7 @@ def handle_post(request_uri):
context.load_cert_chain("%s/empire-chain.pem" % (certPath), "%s/empire-priv.key" % (certPath))
#setting the cipher list allows for modification of the JA3 signature. Select a random cipher to change
#it every time the listener is launched
ipherlist = ["ECDHE-RSA-AES256-GCM-SHA384", "ECDHE-RSA-AES128-GCM-SHA256", "ECDHE-RSA-AES256-SHA384",
cipherlist = ["ECDHE-RSA-AES256-GCM-SHA384", "ECDHE-RSA-AES128-GCM-SHA256", "ECDHE-RSA-AES256-SHA384",
"ECDHE-RSA-AES256-SHA", "AES256-SHA256", "AES128-SHA256"]
selectciph = random.choice(cipherlist)
context.set_ciphers(selectciph)
Expand Down
28 changes: 1 addition & 27 deletions lib/modules/powershell/credentials/mimikatz/cache.py
Expand Up @@ -2,7 +2,6 @@
from builtins import str
from builtins import object
from lib.common import helpers
import threading

class Module(object):

Expand Down Expand Up @@ -49,27 +48,13 @@ def __init__(self, mainMenu, params=[]):
# save off a copy of the mainMenu object to access external functionality
# like listeners/agent handlers/etc.
self.mainMenu = mainMenu

# used to protect self.http and self.mainMenu.conn during threaded listener access
self.lock = threading.Lock()


for param in params:
# parameter format is [Name, Value]
option, value = param
if option in self.options:
self.options[option]['Value'] = value

# this might not be necessary. Could probably be achieved by just callingg mainmenu.get_db but all the other files have
# implemented it in place. Might be worthwhile to just make a database handling file -Hubbl3
def get_db_connection(self):
"""
Returns the cursor for SQLlite DB
"""
self.lock.acquire()
self.mainMenu.conn.row_factory = None
self.lock.release()
return self.mainMenu.conn


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

Expand All @@ -96,15 +81,4 @@ def generate(self, obfuscate=False, obfuscationCommand=""):
if obfuscate:
scriptEnd = helpers.obfuscate(self.mainMenu.installPath, psScript=scriptEnd, obfuscationCommand=obfuscationCommand)
script += scriptEnd

# Get the random function name generated at install and patch the stager with the proper function name
conn = self.get_db_connection()
self.lock.acquire()
cur = conn.cursor()
cur.execute("SELECT Invoke_Mimikatz FROM functions")
replacement = cur.fetchone()
cur.close()
self.lock.release()
script = script.replace("Invoke-Mimikatz", replacement[0])

return script
26 changes: 1 addition & 25 deletions lib/modules/powershell/credentials/mimikatz/certs.py
Expand Up @@ -2,7 +2,6 @@
from builtins import str
from builtins import object
from lib.common import helpers
import threading

class Module(object):

Expand Down Expand Up @@ -48,25 +47,13 @@ def __init__(self, mainMenu, params=[]):
# save off a copy of the mainMenu object to access external functionality
# like listeners/agent handlers/etc.
self.mainMenu = mainMenu
# used to protect self.http and self.mainMenu.conn during threaded listener access
self.lock = threading.Lock()


for param in params:
# parameter format is [Name, Value]
option, value = param
if option in self.options:
self.options[option]['Value'] = value

# this might not be necessary. Could probably be achieved by just callingg mainmenu.get_db but all the other files have
# implemented it in place. Might be worthwhile to just make a database handling file -Hubbl3
def get_db_connection(self):
"""
Returns the cursor for SQLlite DB
"""
self.lock.acquire()
self.mainMenu.conn.row_factory = None
self.lock.release()
return self.mainMenu.conn

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

Expand All @@ -91,15 +78,4 @@ def generate(self, obfuscate=False, obfuscationCommand=""):
if obfuscate:
scriptEnd = helpers.obfuscate(self.mainMenu.installPath, psScript=scriptEnd, obfuscationCommand=obfuscationCommand)
script += scriptEnd

# Get the random function name generated at install and patch the stager with the proper function name
conn = self.get_db_connection()
self.lock.acquire()
cur = conn.cursor()
cur.execute("SELECT Invoke_Mimikatz FROM functions")
replacement = cur.fetchone()
cur.close()
self.lock.release()
script = script.replace("Invoke-Mimikatz", replacement[0])

return script
25 changes: 0 additions & 25 deletions lib/modules/powershell/credentials/mimikatz/command.py
Expand Up @@ -2,7 +2,6 @@
from builtins import str
from builtins import object
from lib.common import helpers
import threading

class Module(object):

Expand Down Expand Up @@ -53,26 +52,13 @@ def __init__(self, mainMenu, params=[]):
# save off a copy of the mainMenu object to access external functionality
# like listeners/agent handlers/etc.
self.mainMenu = mainMenu

# used to protect self.http and self.mainMenu.conn during threaded listener access
self.lock = threading.Lock()

for param in params:
# parameter format is [Name, Value]
option, value = param
if option in self.options:
self.options[option]['Value'] = value

# this might not be necessary. Could probably be achieved by just callingg mainmenu.get_db but all the other files have
# implemented it in place. Might be worthwhile to just make a database handling file -Hubbl3
def get_db_connection(self):
"""
Returns the cursor for SQLlite DB
"""
self.lock.acquire()
self.mainMenu.conn.row_factory = None
self.lock.release()
return self.mainMenu.conn

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

Expand All @@ -98,15 +84,4 @@ def generate(self, obfuscate=False, obfuscationCommand=""):
if obfuscate:
scriptEnd = helpers.obfuscate(self.mainMenu.installPath, psScript=scriptEnd, obfuscationCommand=obfuscationCommand)
script += scriptEnd

# Get the random function name generated at install and patch the stager with the proper function name
conn = self.get_db_connection()
self.lock.acquire()
cur = conn.cursor()
cur.execute("SELECT Invoke_Mimikatz FROM functions")
replacement = cur.fetchone()
cur.close()
self.lock.release()
script = script.replace("Invoke-Mimikatz", replacement[0])

return script

0 comments on commit 953db5c

Please sign in to comment.