Extracting Beacon Configuration from Minidump

cobaltstrike, minidump, process memory
Use minidump format to improve chances of extracting a complete beacon configuration.
Published

January 20, 2022

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"

from dissect.cobaltstrike.beacon import BeaconConfig
from dissect.cobaltstrike import beacon
from minidump.minidumpfile import MinidumpFile
import yara
from pprint import pprint

SAMPLE = "../../malware/minidumps/beacon.mdmp"

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.

bconfig = BeaconConfig.from_path(SAMPLE, all_xor_keys=True)
bconfig.version
<BeaconVersion 'Unknown', tuple=None, date=None>
found = False
with open(SAMPLE, 'rb') as f:
    for config_block, extra_data in beacon.iter_beacon_config_blocks(f):
        try:
            bconfig = beacon.BeaconConfig(config_block)
            if not len(bconfig.domains):
                continue
            found = True
        except ValueError:
            continue
        
        print(bconfig, bconfig.domain_uri_pairs)
    if not found:
        print("No beacons found.")
No beacons found.

Find Beacon Segment

Yara Setup

rule = yara.compile(source=r'''
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
}
''')

matches = rule.match(SAMPLE)
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

md = MinidumpFile.parse(SAMPLE)
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 = []

reader = md.get_reader()
for segment in md.memory_segments.memory_segments:
    va = segment.start_virtual_address
    size = segment.size
    buf = reader.read(va , size)
    matches = rule.match(data=buf)
    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}')
                    decoders.append((va, s[0], buf))
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:
        bconfig = BeaconConfig.from_bytes(match[2], all_xor_keys=True)
        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.