Overview

This is a stealer that has been in operation since atleast May 2022. Recent versions have added a DGA!

References

Sample

  • 6cf8bfba1b221effcb1eccec0c91fb0906d0b8996932167f654680cb3ac53aacUnpacMe

Analysis

This sample has many strings related to the build process that have not been stripped. We can use these for our yara rule.

"powershell -inputformat none -outputformat none -NonInteractive -Command Add-MpPreference -ExclusionExtension \"exe\"",
C:\Workspace\Projects\rat\client\stealer\third_party

C:\Workspace\Projects\rat\client\stealer\out\build\x86-Releaseird_party\cryptopp\_deps\cryptopp\rijndael_simd.cpp

stealertest.dll

IBrowserBase@stealer
ChromeBrowser@stealer
EdgeBrowser@stealer
FirefoxBrowser@stealer

This sample looks similar maybe an earlier version 5e5cc4f42c7d5481db280b28d1227568c17ed8cc4208970b7a963a4f30c7cc83

C:\3001_1\notbotnet\client\stealer\out\third_party\cryptopp
C:\3001_1\notbotnet\client\stealer\third_party
stealertest.dll

Yara Rule

rule metastealer_dga {
strings:
    $libs = "rat\\client\\stealer" ascii wide
    $rtti_1 = "IBrowserBase@stealer" 
    $rtti_2 = "ChromeBrowser@stealer"
    $rtti_3 = "EdgeBrowser@stealer"
    $rtti_4 = "FirefoxBrowser@stealer"
    $name = "stealertest.dll"
condition:
    $name or
    all of ($rtti_*) or
    $libs

}

String Decryption

This is a modified version of Jason Reeves' script from his blog

'''
rule meta_s
{
    meta:
        author = "sysopfb"
    strings:
        $snippet1 = {66 0? ef}
        $snippet2 = {c5 ?? ef}
    condition:
        ($snippet1 or $snippet2)
}
'''


import re


file_data = open('/tmp/metastealer.bin', 'rb').read()

offsets = []
string_egg = rb'\x66[\x00-\x0f]\xef' 


for m in re.finditer(string_egg, file_data, re.DOTALL):
    offsets.append(m.start())

ret = []
out = None

length_off = False

prev_offset = 0
for offset in offsets:
    test = file_data[prev_offset:offset]
    vals = re.findall(b'''\xc7\x85..\xff\xff....''',test)
    if vals != []:
        if len(vals) > 8:
            temp = vals[-8:]
    else:
        temp = vals
    try:
        xdata = temp[0][-4:]
        xdata += temp[1][-4:]
        xdata += temp[2][-4:]
        xdata += temp[3][-4:]
        xkey = temp[4][-4:]
        xkey += temp[5][-4:]
        xkey += temp[6][-4:]
        xkey += temp[7][-4:]
        xdata = bytearray(xdata)
        xkey = bytearray(xkey)
        for i in range(len(xdata)):
            xdata[i] ^= xkey[i]
        tmp_str = b''.join(xdata.split(b'\x00'))
        if tmp_str.isascii():
            print(tmp_str)
    except:
        pass
    prev_offset = offset
    
b'sys'
b'chrome'
b'chrome'
b'chrome'
b'chrome'
b'chrome'
b'chrome'
b'chrome'
b'chrome'
b'chrome'
b'ip'
b'ip'
b'ip'
b'cmd'
b'cmd'
b'FG Started'
b'chrome'
b'chrome'
b'chrome'
b'chrome'
b'chrome'
b'ok'
b'ok'
b'ok'
b'ok'
b'ok'
b':1775'
b':1775'
b'ok'
b'ok'
b'/api/client/new'
b'ok'
b'ffox'
b'dir=in '
b'version'
b'.xyz'
b'ok'
b'os_crypt'
b'os_crypt'
b'RtlGetVersion'
b'RtlGetVersion'
b'Windows 10'
b'Windows 10'
b'Windows 10'
b'Windows 10'
b'Windows 10'
b'Windows 10'
b'Pro'
b'action'
b'action'
b'action'
b'action'
b'action'
b'uuid'
b'uuid'
b'ok'
b'files'
b'files'
b'files'
b'files'
b'files'
b'files'
b'files'
b'files'
b'files'
b'files'
b'files'
b'files'
b'/tasks/collect'
b'status'
b'uuid'
b'uuid'
b'uuid'
b'/tasks/collect'
b'status'
b'status'
b'status'
b'status'
b'/tasks/collect'
b'ROOT'
b'name'
b'name'
b'name'
b'value'
b'host_key'
b'host_key'
b'host_key'
b'host_key'
b'host_key'
b'host_key'
b'host_key'
b'host_key'
b'host_key'
b'domain'
b'domain'
b'path'
b'path'
b'path'
b'path'
b'path'
b'host'
b'isHttpOnly'
b'path'
b'isSecure'
b'expiry'
b'name'
b'value'
b'domain'
b'domain'
b'path'
b'path'
b'path'
b'name'
b'value'
b'origin_url'
b'origin_url'
b'origin_url'
b'url'
b'url'
b'url'
b'ntdll'
b'Uuid'
b'File-Type'
b'files'
b'cmd.exe'
b'cmd.exe'
b'cmd.exe'
b'H\x07\x01\x01'
b'\x02\x01'
b'\x01\x02\x01\x02\x02\x01'
b'\x02\x08\x02\x03\x03'
b''
b'\x0f'
b'a\x06\x01\x10\x18'

DGA

We want to statically extract the DGA seed.

68 EF 06 00 00                          push    1775
89 85 84 FD FF FF                       mov     [ebp-27Ch], eax
8D 85 F0 FD FF FF                       lea     eax, [ebp-210h]
68 34 12 00 00                          push    1234h
import re
import struct

seed_egg = rb'\x68\xEF\x06\x00\x00.{6,16}\x68(..\x00\x00)'
for m in re.finditer(seed_egg, file_data, re.DOTALL):
    tmp_seed = struct.unpack('<I', m.group(1))[0]
    print(hex(tmp_seed))
0x1234