from dissect.cobaltstrike.beacon import BeaconConfig
from dissect.cobaltstrike import beacon
from minidump.minidumpfile import MinidumpFile
import yara
from pprint import pprint
= "../../malware/minidumps/beacon.mdmp" SAMPLE
Extracting Beacon Configuration from Minidump
Overview
Extract configuration from a process memory dump containing a running beacon. Initial extraction on the full minidump file fails (TODO: figure out exactly why it fails). Using yara to find the segment where beacon is residing passing that segment to dissect.cobaltstrike allows the proper configuration extraction.
Libraries
Samples
Sample was chosen somewhat randomly from the results of this virustotal search.
VT Search: crowdsourced_yara_rule:000aa75fc2|CobaltStrike_Sleep_Decoder_Indicator trid:"Windows Minidump"
Attempt Extraction on full dump
Using the normal method and the iter_beacon_config_blocks method recommened by the documentation.
Both of these methods fail to extract a valid configuration.
= BeaconConfig.from_path(SAMPLE, all_xor_keys=True)
bconfig bconfig.version
<BeaconVersion 'Unknown', tuple=None, date=None>
= False
found with open(SAMPLE, 'rb') as f:
for config_block, extra_data in beacon.iter_beacon_config_blocks(f):
try:
= beacon.BeaconConfig(config_block)
bconfig if not len(bconfig.domains):
continue
= True
found except ValueError:
continue
print(bconfig, bconfig.domain_uri_pairs)
if not found:
print("No beacons found.")
No beacons found.
Find Beacon Segment
Yara Setup
= yara.compile(source=r'''
rule rule CobaltStrike_Sleep_Decoder_Indicator {
meta:
description = "Detects CobaltStrike sleep_mask decoder"
author = "yara@s3c.za.net"
date = "2021-07-19"
strings:
$sleep_decoder = { 48 89 5C 24 08 48 89 6C 24 10 48 89 74 24 18 57 48 83 EC 20 4C 8B 51 08 41 8B F0 48 8B EA 48 8B D9 45 8B 0A 45 8B 5A 04 4D 8D 52 08 45 85 C9 }
condition:
$sleep_decoder
}
''')
= rule.match(SAMPLE)
matches for match in matches:
for s in match.strings:
print(f'Rule: {match.rule}\t\tOffset: 0x{s[0]:x}\tString: {s[1]}')
Rule: CobaltStrike_Sleep_Decoder_Indicator Offset: 0xafc9c1 String: $sleep_decoder
Rule: CobaltStrike_Sleep_Decoder_Indicator Offset: 0xb9db9c String: $sleep_decoder
Setup Minidump
= MinidumpFile.parse(SAMPLE)
md print(f'OS: {md.sysinfo.OperatingSystem}\tProcess: {md.modules.modules[0].name}')
OS: Windows Server 2012 R2 Process: C:\Windows\System32\svchost.exe
Find the segments
Use minidump to iterate over all segments and run the yara rule on each segments contents.
# segment_addr, offset, buf
= []
decoders
= md.get_reader()
reader for segment in md.memory_segments.memory_segments:
= segment.start_virtual_address
va = segment.size
size = reader.read(va , size)
buf = rule.match(data=buf)
matches for match in matches:
for s in match.strings:
for info in md.memory_info.infos:
if info.BaseAddress == va:
print(f'String: {s[1]}\tSegment Addr: 0x{va:x}\tOffset: 0x{s[0]:x}\tSize {size:x}\tProtect: {info.Protect}\tType: {info.Type}')
0], buf)) decoders.append((va, s[
String: $sleep_decoder Segment Addr: 0x25bda48000 Offset: 0x442cd Size d1000 Protect: AllocationProtect.PAGE_READWRITE Type: MemoryType.MEM_PRIVATE
String: $sleep_decoder Segment Addr: 0x25bdb50000 Offset: 0x0 Size 2000 Protect: AllocationProtect.PAGE_READWRITE Type: MemoryType.MEM_PRIVATE
Find modules
If the matches happened to fall within a loaded module this code would provide the module names and could be extracted and written to disk.
# find in modules, don't need since our type is PRIVATE (not MAPPED or IMAGE)
for match in decoders:
for module in md.modules.modules:
if module.inrange(match[0]):
print(module.name)
Extract Configuration
Pass matching segment data to dissect.cobaltstrike and print config!
for match in decoders:
try:
= BeaconConfig.from_bytes(match[2], all_xor_keys=True)
bconfig
pprint(bconfig.settings)except ValueError as exc:
print(f'Segment: 0x{match[0]:x}: Config not found.')
mappingproxy(OrderedDict([('SETTING_PROTOCOL', 8),
('SETTING_PORT', 443),
('SETTING_SLEEPTIME', 45000),
('SETTING_MAXGET', 1403644),
('SETTING_JITTER', 37),
('SETTING_PUBKEY',
'c1b23fce5720138b846a78d3193d3ef1d2da0672d4c01b706ba0c29f3aab301f'),
('SETTING_DOMAINS',
'cp.ikmcontact.com,/jquery-3.3.1.min.js'),
('SETTING_DOMAIN_STRATEGY', 0),
('SETTING_DOMAIN_STRATEGY_SECONDS', 4294967295),
('SETTING_DOMAIN_STRATEGY_FAIL_X', 4294967295),
('SETTING_DOMAIN_STRATEGY_FAIL_SECONDS', 4294967295),
('SETTING_SPAWNTO',
'00000000000000000000000000000000'),
('SETTING_SPAWNTO_X86',
'%windir%\\syswow64\\dllhost.exe'),
('SETTING_SPAWNTO_X64',
'%windir%\\sysnative\\dllhost.exe'),
('SETTING_CRYPTO_SCHEME', 0),
('SETTING_C2_VERB_GET', 'GET'),
('SETTING_C2_VERB_POST', 'POST'),
('SETTING_C2_CHUNK_POST', 0),
('SETTING_WATERMARK', 23333),
('SETTING_WATERMARKHASH',
b'elf5JE2Us6WaAP9xbnKyFw=='),
('SETTING_CLEANUP', 1),
('SETTING_CFG_CAUTION', 0),
('SETTING_MAX_RETRY_STRATEGY_ATTEMPTS', 0),
('SETTING_MAX_RETRY_STRATEGY_INCREASE', 0),
('SETTING_MAX_RETRY_STRATEGY_DURATION', 0),
('SETTING_USERAGENT',
'Mozilla/5.0 (Windows NT 6.3; Trident/7.0; rv:11.0) '
'like Gecko'),
('SETTING_SUBMITURI', '/jquery-3.3.2.min.js'),
('SETTING_C2_RECOVER',
[('print', True),
('append', 1522),
('prepend', 84),
('prepend', 3931),
('base64url', True),
('mask', True)]),
('SETTING_C2_REQUEST',
[('_HEADER',
b'Accept: text/html,application/xhtml+xml,applicat'
b'ion/xml;q=0.9,*/*;q=0.8'),
('_HEADER', b'Referer: http://code.jquery.com/'),
('_HEADER', b'Accept-Encoding: gzip, deflate'),
('BUILD', 'metadata'),
('BASE64URL', True),
('MASK', True),
('BASE64URL', True),
('PREPEND', b'__cfduid='),
('HEADER', b'Cookie')]),
('SETTING_C2_POSTREQ',
[('_HEADER',
b'Accept: text/html,application/xhtml+xml,applicat'
b'ion/xml;q=0.9,*/*;q=0.8'),
('_HEADER', b'Referer: http://code.jquery.com/'),
('_HEADER', b'Accept-Encoding: gzip, deflate'),
('BUILD', 'id'),
('MASK', True),
('BASE64URL', True),
('PARAMETER', b'__cfduid'),
('BUILD', 'output'),
('MASK', True),
('BASE64URL', True),
('PRINT', True)]),
('SETTING_HOST_HEADER',
'Host: cp.ikmcontact.com\r\n'),
('SETTING_HTTP_NO_COOKIES', 1),
('SETTING_PROXY_BEHAVIOR', 2),
('SETTING_TCP_FRAME_HEADER', b'\x80'),
('SETTING_SMB_FRAME_HEADER', b'\x80'),
('SETTING_EXIT_FUNK', 0),
('SETTING_KILLDATE', 0),
('SETTING_GARGLE_NOOK', 1),
('SETTING_GARGLE_SECTIONS',
['0x2e000-0x3da32',
'0x3e000-0x4d2a8',
'0x4e000-0x50130',
'0x51000-0x51f90']),
('SETTING_PROCINJ_PERMS_I', 4),
('SETTING_PROCINJ_PERMS', 32),
('SETTING_PROCINJ_MINALLOC', 17500),
('SETTING_PROCINJ_TRANSFORM_X86',
[('append', b'\x90\x90'), ('prepend', b'')]),
('SETTING_PROCINJ_TRANSFORM_X64',
[('append', b'\x90\x90'), ('prepend', b'')]),
('SETTING_PROCINJ_STUB',
'f0de4b5c59430261fd22ac401da76a58'),
('SETTING_PROCINJ_EXECUTE',
['CreateThread "ntdll!RtlUserThreadStart+0x42"',
'CreateThread',
'NtQueueApcThread_s',
'CreateRemoteThread',
'RtlCreateUserThread']),
('SETTING_PROCINJ_ALLOCATOR', 1),
('SETTING_PROCINJ_ALLOWED', 1),
('SETTING_KILLDATE_YEAR', 0),
('SETTING_MASKED_WATERMARK',
'814ec611594302798e17952c28ce1b6995b4116f1612664e8e45da0e7cc05765')]))
Segment: 0x25bdb50000: Config not found.