From b8151b4b7b70dc04b2139654053b84908f32597d Mon Sep 17 00:00:00 2001 From: Vincent Rose Date: Sat, 4 Nov 2023 17:47:06 -0700 Subject: [PATCH] =?UTF-8?q?make=20donut=20install=20conditional=20so=20we?= =?UTF-8?q?=20can=20support=20arm=20machines.=20add=20wa=E2=80=A6=20(#721)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * make donut install conditional so we can support arm machines. add warnings when donut is invoked but not installed * post-merge fix --- CHANGELOG.md | 2 ++ empire/server/common/stagers.py | 26 +++++++++++++++---- .../csharp/ProcessInjection.Covenant.py | 16 ++++++++++-- .../modules/powershell/management/shinject.py | 5 +++- empire/server/stagers/windows/shellcode.py | 22 +++++++++++++--- poetry.lock | 2 +- pyproject.toml | 2 +- 7 files changed, 62 insertions(+), 13 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7fd685956..a8d001b10 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -34,6 +34,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Added automatic tasking for sysinfo for stageless agents (@Cx01N) - Updated listeners to consistently use port 80 and 443 for HTTP traffic by default (@Cx01N) - Remove unneeded condition statement from all listeners (@Vinnybod) +- Make the installation of donut conditional on architecture since it doesn't work on ARM (@Vinnybod) + - When donut is invoked but not installed, give a useful warning (@Vinnybod) ## [5.7.3] - 2023-10-17 diff --git a/empire/server/common/stagers.py b/empire/server/common/stagers.py index 50240afbf..6d5cb2bb2 100755 --- a/empire/server/common/stagers.py +++ b/empire/server/common/stagers.py @@ -21,8 +21,13 @@ import subprocess import zipfile from itertools import cycle +from typing import Optional, Tuple + +try: + import donut +except ModuleNotFoundError: + donut = None -import donut import macholib.MachO from empire.server.core.db import models @@ -179,7 +184,7 @@ def generate_powershell_exe( def generate_powershell_shellcode( self, posh_code, arch="both", dot_net_version="net40" - ): + ) -> Tuple[Optional[str], Optional[str]]: """ Generate powershell shellcode using donut python module """ @@ -191,8 +196,14 @@ def generate_powershell_shellcode( arch_type = 3 directory = self.generate_powershell_exe(posh_code, dot_net_version) + + if not donut: + err = "module donut-shellcode not installed. It is only supported on x86." + log.warning(err, exc_info=True) + return None, err + shellcode = donut.create(file=directory, arch=arch_type) - return shellcode + return shellcode, None def generate_exe_oneliner( self, language, obfuscate, obfuscation_command, encode, listener_name @@ -270,7 +281,7 @@ def generate_python_exe( def generate_python_shellcode( self, posh_code, arch="both", dot_net_version="net40" - ): + ) -> Tuple[Optional[str], Optional[str]]: """ Generate ironpython shellcode using donut python module """ @@ -281,9 +292,14 @@ def generate_python_shellcode( elif arch == "both": arch_type = 3 + if not donut: + err = "module donut-shellcode not installed. It is only supported on x86." + log.warning(err, exc_info=True) + return None, err + directory = self.generate_python_exe(posh_code, dot_net_version) shellcode = donut.create(file=directory, arch=arch_type) - return shellcode + return shellcode, None def generate_macho(self, launcherCode): """ diff --git a/empire/server/modules/csharp/ProcessInjection.Covenant.py b/empire/server/modules/csharp/ProcessInjection.Covenant.py index 6482d3111..0f01a5dd3 100644 --- a/empire/server/modules/csharp/ProcessInjection.Covenant.py +++ b/empire/server/modules/csharp/ProcessInjection.Covenant.py @@ -1,6 +1,10 @@ from typing import Dict -import donut +try: + import donut +except ModuleNotFoundError: + donut = None + import yaml from empire.server.common import helpers @@ -48,9 +52,11 @@ def generate( return handle_error_message("[!] Invalid listener: " + listener_name) if language.lower() == "powershell": - shellcode = main_menu.stagers.generate_powershell_shellcode( + shellcode, err = main_menu.stagers.generate_powershell_shellcode( launcher, arch=arch, dot_net_version=dot_net_version ) + if err: + return handle_error_message(err) elif language.lower() == "csharp": if arch == "x86": @@ -60,6 +66,12 @@ def generate( elif arch == "both": arch_type = 3 directory = f"{main_menu.installPath}/csharp/Covenant/Data/Tasks/CSharp/Compiled/{dot_net_version}/{launcher}.exe" + + if not donut: + return handle_error_message( + "module donut-shellcode not installed. It is only supported on x86." + ) + shellcode = donut.create(file=directory, arch=arch_type) elif language.lower() == "ironpython": diff --git a/empire/server/modules/powershell/management/shinject.py b/empire/server/modules/powershell/management/shinject.py index ac608b85f..d7391ef7e 100644 --- a/empire/server/modules/powershell/management/shinject.py +++ b/empire/server/modules/powershell/management/shinject.py @@ -50,9 +50,12 @@ def generate( return handle_error_message("[!] Error in launcher generation.") else: launcher_code = launcher.split(" ")[-1] - sc = main_menu.stagers.generate_powershell_shellcode( + sc, err = main_menu.stagers.generate_powershell_shellcode( launcher_code, arch ) + if err: + return handle_error_message(err) + encoded_sc = helpers.encode_base64(sc) script_end = '\nInvoke-Shellcode -ProcessID {} -Shellcode $([Convert]::FromBase64String("{}")) -Force'.format( diff --git a/empire/server/stagers/windows/shellcode.py b/empire/server/stagers/windows/shellcode.py index d94edeb0d..80a9c4ff8 100644 --- a/empire/server/stagers/windows/shellcode.py +++ b/empire/server/stagers/windows/shellcode.py @@ -1,6 +1,11 @@ import logging -import donut +try: + import donut +except ModuleNotFoundError: + donut = None + +from empire.server.utils.module_util import handle_error_message log = logging.getLogger(__name__) @@ -142,9 +147,12 @@ def generate(self): return "[!] Error in launcher command generation." if language.lower() == "powershell": - shellcode = self.mainMenu.stagers.generate_powershell_shellcode( + shellcode, err = self.mainMenu.stagers.generate_powershell_shellcode( launcher, arch=arch, dot_net_version=dot_net_version ) + if err: + return handle_error_message(err) + return shellcode elif language.lower() == "csharp": @@ -155,14 +163,22 @@ def generate(self): elif arch == "both": arch_type = 3 + if not donut: + return handle_error_message( + "module donut-shellcode not installed. It is only supported on x86." + ) + directory = f"{self.mainMenu.installPath}/csharp/Covenant/Data/Tasks/CSharp/Compiled/{dot_net_version}/{launcher}.exe" shellcode = donut.create(file=directory, arch=arch_type) return shellcode elif language.lower() == "python": - shellcode = self.mainMenu.stagers.generate_python_shellcode( + shellcode, err = self.mainMenu.stagers.generate_python_shellcode( launcher, arch=arch, dot_net_version=dot_net_version ) + if err: + return handle_error_message(err) + return shellcode else: diff --git a/poetry.lock b/poetry.lock index 41e6da469..48a560de1 100644 --- a/poetry.lock +++ b/poetry.lock @@ -3208,4 +3208,4 @@ test = ["pytest"] [metadata] lock-version = "2.0" python-versions = ">=3.10,<3.13" -content-hash = "b597e531825a427d976d0c37bb7e1b3d0cdf57472cff83f7bff45cb6adb50c6e" +content-hash = "ef22acd364fda0e317c57695f40c980379f5c531f664a5a2bcbaf81630506dc2" diff --git a/pyproject.toml b/pyproject.toml index d2f61c1c6..bc6169559 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -48,7 +48,7 @@ pyvnc = {git = "https://github.com/BC-SECURITY/pyVNC.git"} python-socketio = {extras = ["client"], version = "^5.10.0"} Flask = "^2.3.3" pysecretsocks = {git = "https://github.com/BC-SECURITY/PySecretSOCKS.git", rev = "da5be0e"} -donut-shellcode = "^1.0.2" +donut-shellcode = { version = "^1.0.2", markers = "platform_machine == 'x86_64' or platform_machine == 'amd64'" } python-obfuscator = "^0.0.2" pyinstaller = "^5.13.2" md2pdf = {git = "https://github.com/bc-security/md2pdf", rev = "48d5a46"}