CobaltStrike UUID stager By Jason Reaves Published: 2022-03-28 · Archived: 2026-04-05 19:09:45 UTC By: Jason Reaves Press enter or click to view image in full size Lots of interesting stagers and loaders exist for CobaltStrike, we’ve talked about a few them written in various languages[1,2,3]. We have been tracking one for over a year now which hexlifies the stager code on board, recently we saw a hit for a YARA rule we use to track this stager: rule cs_hexlified_stager_sc { strings: $a1 = "d2648b52308b" nocase condition: all of them } https://medium.com/walmartglobaltech/cobaltstrike-uuid-stager-ca7e82f7bb64 Page 1 of 7 Sample: 488d4cbe017c493c66a769ccb203b40cbca3396c654bfff72c1aebeb41f23297 However our system for decoding out the shellcode failed so we took a closer look and noticed a few interesting things, one the hexlified shellcode was stored as UUIDs[4] and two the sample was signed by NVIDIAs cert which was recently leaked by LAPSUS$[5]. Ref: virustotal.com Normally to decode the shellcode our decoder would simply rip out the hexlified data and rebuild it: def decoder(data): ret = [] c2s = [] blob = re.findall(b'''fce8[0-9a-f]+''', data) for val in blob: https://medium.com/walmartglobaltech/cobaltstrike-uuid-stager-ca7e82f7bb64 Page 2 of 7 if len(val)%2 >0: val = val[:-1] temp = binascii.unhexlify(val) s = re.findall(b'[ -~]{4,}', temp) t = [x.decode('cp1250') for x in s] c2s.append(t[-1]) ret += t return((ret,c2s)) Since it is UUIDs then the process is still pretty simple but we just need to account for whitespace and then abuse try/catch to end the collection: idx = data.find(b'0089e8fc') t = data[idx:].split(b'\x00') tt = [x for x in t if x != b''] sc = b'' c2s = [] ret = [] for val in tt: try: u = uuid.UUID(val.decode('cp1250')) sc += u.bytes_le except: break if sc != '': s = re.findall(b'[ -~]{4,}', sc) t = [x.decode('cp1250') for x in s] c2s.append(t[-1]) ret += t The debug artifacts in the sample refers to itself as ‘shellcode_uuids’ which can also be used to find another sample in VirusTotal: a59ab6c4e69fafc927f666cf1afb31a2851a392cc74336d8a31e15daf1f343ff The stagers appear to be the same however, so instead of we can search for something different to pivot on: content:"0089e8fc-" This leads to many more samples and since we already have a decoder we can enumerate the IOCs fairly easily for the samples we have accounted for but there are some others mixed in. For example GoLang samples that have out of order UUIDs compared to the other samples: 07122c83922c50b386a060d4fbf21433042118fccf5bc678f78acd377d5dccfe https://medium.com/walmartglobaltech/cobaltstrike-uuid-stager-ca7e82f7bb64 Page 3 of 7 To decode we just find the offset reference to the first UUID in the code and then walk the table to rebuild: if b'Go build ID' not in data: t = data[idx:].split(b'\x00') tt = [x for x in t if x != b''] else: pe = pefile.PE(data=data) memdata = pe.get_memory_mapped_image() idx = memdata.find(b'0089e8fc') imgbase = pe.OPTIONAL_HEADER.ImageBase val_to_find = struct.pack('