Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 12 additions & 1 deletion app/emu_svc.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@

if not os.path.exists(self.repo_dir) or not os.listdir(self.repo_dir):
self.log.debug('cloning repo %s' % repo_url)
check_call(['git', 'clone', '--depth', '1', repo_url, self.repo_dir], stdout=DEVNULL, stderr=STDOUT)

Check failure on line 74 in app/emu_svc.py

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Use an async subprocess call in this async function instead of a synchronous one.

See more on https://sonarcloud.io/project/issues?id=mitre_emu&issues=AZz22UCIOsOpMRwZ30_1&open=AZz22UCIOsOpMRwZ30_1&pullRequest=52
self.log.debug('clone complete')

async def populate_data_directory(self, library_path=None):
Expand Down Expand Up @@ -309,14 +309,25 @@
[payload for payload in payloads if payload not in self._dynamicically_compiled_payloads]
)

@staticmethod
def _is_resource_path(path):
"""Check if the given path is under a Resources/ directory (not Archive/)."""
path_str = str(path)
return 'Resources' + os.sep in path_str or '/Resources/' in path_str
Comment on lines +315 to +316

def _store_required_payloads(self):
self.log.debug('Searching for and storing required payloads.')
for payload in self.required_payloads:
copied = False
found = False
if os.path.exists(os.path.join(self.payloads_dir, payload)):
continue
for path in Path(self.repo_dir).rglob(payload):
matches = list(Path(self.repo_dir).rglob(payload))
# Prioritize payloads from Resources/ directories over other locations
# (e.g. Archive/CALDERA_DIY/evals/payloads) to avoid loading wrong files.
resource_matches = [p for p in matches if self._is_resource_path(p)]
ordered_matches = resource_matches if resource_matches else matches
for path in ordered_matches:
Comment on lines +325 to +330
found = True
target_path = os.path.join(self.payloads_dir, path.name)
try:
Expand Down
37 changes: 37 additions & 0 deletions tests/test_emu_svc.py
Original file line number Diff line number Diff line change
Expand Up @@ -237,6 +237,43 @@ def _rglob(_, target):
call(PosixPath('/path/to/payload3'), 'plugins/emu/payloads/payload3'),
], any_order=True)

def test_store_required_payloads_prefers_resources_path(self, emu_svc):
"""Payloads under Resources/ should be preferred over Archive/ duplicates (#32)."""
def _rglob(_, target):
return [
PosixPath('/repo/apt29/Archive/CALDERA_DIY/evals/payloads/' + target),
PosixPath('/repo/apt29/Resources/payloads/' + target),
]

emu_svc.required_payloads = {'evil.exe'}
with patch.object(Path, 'rglob', new=_rglob):
with patch.object(shutil, 'copyfile', return_value=None) as new_copyfile:
emu_svc._store_required_payloads()
# Should copy from Resources/ path, not Archive/ path
new_copyfile.assert_called_once_with(
PosixPath('/repo/apt29/Resources/payloads/evil.exe'),
'plugins/emu/payloads/evil.exe',
)

def test_store_required_payloads_falls_back_when_no_resources(self, emu_svc):
"""If no Resources/ match exists, fall back to any available match."""
def _rglob(_, target):
return [PosixPath('/repo/other_location/' + target)]

emu_svc.required_payloads = {'tool.dll'}
with patch.object(Path, 'rglob', new=_rglob):
with patch.object(shutil, 'copyfile', return_value=None) as new_copyfile:
emu_svc._store_required_payloads()
new_copyfile.assert_called_once_with(
PosixPath('/repo/other_location/tool.dll'),
'plugins/emu/payloads/tool.dll',
)

def test_is_resource_path(self):
assert EmuService._is_resource_path(PosixPath('/repo/apt29/Resources/payloads/evil.exe')) is True
assert EmuService._is_resource_path(PosixPath('/repo/apt29/Archive/CALDERA_DIY/evals/payloads/evil.exe')) is False
assert EmuService._is_resource_path(PosixPath('/repo/other/evil.exe')) is False

def test_register_required_payloads(self, emu_svc):
payloads = ['payload1', 'payload2', 'payload3', 'sandcat.go-darwin', 'sandcat.go-linux', 'sandcat.go-windows']
want = {'payload1', 'payload2', 'payload3'}
Expand Down
Loading