Overview

According to Zscaler Mystic is a stealer that has been active since April 2023, sold on underground forum such as XSS. Other than stealing browser credentials and crypto wallets the stealer's main differentiator is the use of a custom obfuscator that is used to protect strings. This obfuscator produces similar output as ADVObfuscator, there as been speculation that it is indeed just ADVObfuscator.

Samples

BF38A3699AB2072DEA806FF2EE3E54FCA4ABFA983BE9CBB207C3AE8E65095364 UnpacMe

References

Analysis

Looking at the C2 encryption routine we can see that there was a progression from the earlier builds.

The earliest build from March 2023 47439044a81b96be0bb34e544da881a393a30f0272616f52f54405b4bf288c7c has a build path that has not been erased G:\Projects\stealer\oGnSUE7arNOZser\tmp_compiler\output.pdb. This build does not use the custom encryption algorithm described below but simply uses an encrypted stack string to hide the c2 164.132.200.171.

Builds from April-May 2023 (example 45D29AFC212F2D0BE4E198759C3C152BB8D0730BA20D46764A08503EAB0B454F) uses the algorithm with a stack string containing the IP address as a DWORD followed by the port as a WORD and a stack string key.

New builds (example BF38A3699AB2072DEA806FF2EE3E54FCA4ABFA983BE9CBB207C3AE8E65095364) have used a modified version of the algorithm which embed the key in the algorithm and used a global encrypted string to hide the full URL of the C2, not just an IP and Port.

Config

The following is a modified version of the open source decryptor for Zscaler decrypt_c2s.py

import struct

def uint32(val):
    return val & 0xffffffff

def decrypt(data):
    out = b''
    block_size = 8  
    num_blocks = len(data) // block_size
    blocks = struct.unpack(f"<{2 * num_blocks}L", data)

    for i in range(0,len(blocks),2):
        out += decrypt_block(blocks[i],blocks[i+1])
    return out

def decrypt_block(v0, v1):
    sum_value = 0xC6EF3720
    delta = 0x61C88647
    
    #key0, key1, key2, key3 = struct.unpack("<4L", key)
    
    key0 = 0x7D935554
    key1 = 0x7A3B0639
    key2 = 0x4C774985
    key3 = 0x8F036C0
    
    for i in range(32):
        v1 = v1 - ((v0 + sum_value) ^ (key2 + (v0 << 4)) ^ (key3 + (v0 >> 5)))
        v1 = uint32(v1)
        # print("v1:", hex(v1))
        v7 = uint32(v1 + sum_value)
        sum_value = uint32(sum_value + delta)
        v8 = v7 ^ uint32(key0 + (v1 << 4)) ^ uint32(key1 + (v1 >> 5))
        v0 =  uint32(v0 - v8)
        
    
    return struct.pack("<2L", v0, v1)

decrypt(bytes.fromhex('6f928688f64c75a5c916e8abe4560c29e279d7b750aba83f'))
b'http://193.233.254.61/\x00\x00'

The encrypted C2 can take two forms, either an IP address in DWORD octet format, followed by a port, or a full URL. The octet format follows.

c21 = 0x91736C72
c22 = 0x17DEE303
c23 = 0x85B9C50
c24 = 0xD8A3FC07

c221 = 0x66EB0028;
c222 = 0x41978887;
c223  = 0x85B9C50;
c224  = 0xD8A3FC07;


def decrypt_block(v0, v1):
    sum_value = 0xC6EF3720
    delta = 0x61C88647
    
    #key0, key1, key2, key3 = struct.unpack("<4L", key)
    
    key0 = 0x51D067C
    key1 = 0x3F113D44
    key2 = 0x6AA301C0
    key3 = 0x72656277
    
    for i in range(32):
        v1 = v1 - ((v0 + sum_value) ^ (key2 + (v0 << 4)) ^ (key3 + (v0 >> 5)))
        v1 = uint32(v1)
        # print("v1:", hex(v1))
        v7 = uint32(v1 + sum_value)
        sum_value = uint32(sum_value + delta)
        v8 = v7 ^ uint32(key0 + (v1 << 4)) ^ uint32(key1 + (v1 >> 5))
        v0 =  uint32(v0 - v8)
        
    
    return struct.pack("<2L", v0, v1)



print(decrypt_block(c221, c222))

print(ord('\x87'))
print(ord('\xb5'))
print(ord('/'))
print(ord('_'))

import struct

print(struct.unpack('>H',b'3\xa3')[0])
b'3\xa3\x00\x00\x00\x00\x00\x00'
135
181
47
95
13219

Sample ID

The new samples share common bytes related to the decryption algorithm c1 e8 05 05 ?? ?? ?? ??33 c8 8b c3 c1 e0 04 05 which can be used to sig them.

C1 E8 05                                shr     eax, 5
05 C0 36 F0 08                          add     eax, key3
33 C8                                   xor     ecx, eax
8B C3                                   mov     eax, ebx
C1 E0 04                                shl     eax, 4
05 85 49 77 4C                          add     eax, key2
33 C8                                   xor     ecx, eax
2B F1                                   sub     esi, ecx
8B C6                                   mov     eax, esi
C1 E0 04                                shl     eax, 4
05 54 55 93 7D                          add     eax, key0
8D 0C 37                                lea     ecx, [edi+esi]
33 C8                                   xor     ecx, eax
8D BF 47 86 C8 61                       lea     edi, [edi+61C88647h]
8B C6                                   mov     eax, esi
C1 E8 05                                shr     eax, 5
05 39 06 3B 7A                          add     eax, key1
33 C8                                   xor     ecx, eax
2B D9                                   sub     ebx, ecx
83 ED 01                                sub     ebp, 1
import re
import struct 

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


egg = rb'\xc1\xe8\x05\x05(....)\x33\xc8\x8b\xc3\xc1\xe0\x04\x05(....)\x33\xc8\x2b\xf1\x8b\xc6\xc1\xe0\x04\x05(....)\x8d\x0c\x37\x33\xc8\x8d\xbf\x47\x86\xc8\x61\x8b\xc6\xc1\xe8\x05\x05(....)\x33\xc8\x2b\xd9\x83\xed\x01'


match = re.search(egg, file_data)


assert match is not None

key0 = struct.unpack('<I', match.group(3))[0]
key1 = struct.unpack('<I', match.group(4))[0]
key2 = struct.unpack('<I', match.group(2))[0]
key3 = struct.unpack('<I', match.group(1))[0]

print(f"{hex(key0)}\n{hex(key1)}\n{hex(key2)}\n{hex(key3)}\n")


def decrypt(data, key0, key1, key2, key3):
    out = b''
    block_size = 8  
    num_blocks = len(data) // block_size
    blocks = struct.unpack(f"<{2 * num_blocks}L", data)

    for i in range(0,len(blocks),2):
        out += decrypt_block(blocks[i],blocks[i+1], key0, key1, key2, key3)
    return out


def decrypt_block(v0, v1, key0, key1, key2, key3):
    sum_value = 0xC6EF3720
    delta = 0x61C88647
    
    #key0, key1, key2, key3 = struct.unpack("<4L", key)
    
    for i in range(32):
        v1 = v1 - ((v0 + sum_value) ^ (key2 + (v0 << 4)) ^ (key3 + (v0 >> 5)))
        v1 = uint32(v1)
        # print("v1:", hex(v1))
        v7 = uint32(v1 + sum_value)
        sum_value = uint32(sum_value + delta)
        v8 = v7 ^ uint32(key0 + (v1 << 4)) ^ uint32(key1 + (v1 >> 5))
        v0 =  uint32(v0 - v8)
        
    
    return struct.pack("<2L", v0, v1)
0x7d935554
0x7a3b0639
0x4c774985
0x8f036c0

import pefile

pe = pefile.PE(data=file_data)

rdata = None 

for s in pe.sections:
    if s.Name[:6] == b'.rdata':
        rdata = s.get_data()
        
assert rdata is not None


candidates = rdata.split(b'\x00\x00\x00')

found = False
for c in candidates:
    if found:
        break
    c = c.strip(b'\x00').rstrip(b'\x00')
    
    if len(c) < 11:
        continue
    
    for i in range(len(c) - 10):
        tmp = c[i:]
        #print(f'\n\nTesting: {tmp}')
        try:
            out = decrypt(tmp, key0, key1, key2, key3)

            if out[:4] == b'http':
                print(out)
                found = True
                break
        except:
            pass

        
b'http://193.233.254.61/\x00\x00'

Config Extraction

import re
import struct
import pefile




def extract(file_path):
    file_data = open(file_path,'rb').read()


    egg = rb'\xc1\xe8\x05\x05(....)\x33\xc8\x8b\xc3\xc1\xe0\x04\x05(....)\x33\xc8\x2b\xf1\x8b\xc6\xc1\xe0\x04\x05(....)\x8d\x0c\x37\x33\xc8\x8d\xbf\x47\x86\xc8\x61\x8b\xc6\xc1\xe8\x05\x05(....)\x33\xc8\x2b\xd9\x83\xed\x01'


    match = re.search(egg, file_data)


    assert match is not None

    key0 = struct.unpack('<I', match.group(3))[0]
    key1 = struct.unpack('<I', match.group(4))[0]
    key2 = struct.unpack('<I', match.group(2))[0]
    key3 = struct.unpack('<I', match.group(1))[0]
    
    
    
    pe = pefile.PE(data=file_data)

    rdata = None 

    for s in pe.sections:
        if s.Name[:6] == b'.rdata':
            rdata = s.get_data()

    assert rdata is not None


    candidates = rdata.split(b'\x00\x00\x00')

    found = False
    for c in candidates:
        if found:
            break
        c = c.strip(b'\x00').rstrip(b'\x00')

        if len(c) < 11:
            continue

        for i in range(len(c) - 10):
            tmp = c[i:]
            #print(f'\n\nTesting: {tmp}')
            try:
                out = decrypt(tmp, key0, key1, key2, key3)

                if out[:4] == b'http':
                    print(out)
                    found = True
                    break
            except:
                pass

            
extract('/tmp/mystic/mys.bin')
b'http://193.233.254.61/\x00\x00'
import os
# assign directory
directory = '/tmp/test/'
 
# iterate over files in
# that directory
for filename in os.listdir(directory):
    f = os.path.join(directory, filename)
    # checking if it is a file
    if os.path.isfile(f):
        try:
            print(f)
            extract(f)
        except:
            pass
/tmp/test/e59b04adba48e393f70f266963502d0874ab4922d82771d34201c5437f13fa1d
b'http://193.233.254.61/\x00\x00'
/tmp/test/57b5d8f8ac46c06e4557306f1695d84ac69abd4fe0c7483c0f629c759a19af84
b'http://193.233.254.61/\x00\x00'
/tmp/test/0f25abd7883b4fde66a267efdc2b81276660e4e086609f1e463eaf148a845412
b'http://193.233.254.61/\x00\x00'
/tmp/test/9dd4e6ebfed11febb02847a221bdcf7067954e2a5c08469fa6cf9a125c13bff1
b'http://193.233.254.61/\x00\x00'
/tmp/test/d834a9551db2db0f36b8e0b38f65a8caa134b1133ba5490359edb04679eb5db6
b'http://193.233.254.61/\x00\x00'
/tmp/test/9960e5aa85b8d62c322ff29ebd9cfbff638975fedffd1c288027e5e9d5743f26
b'http://193.233.254.61/\x00\x00'
/tmp/test/f78d0fc08a32b469f4a12b12ec350e0d9cb6441ad8d3a16eeaf1af9194c16e60
/tmp/test/f59fe5f95fdfa3dbd342360eaf42495e3a51d4dae1ff4a7ee2b38a7a5daaf0fb
b'http://193.233.254.61/\x00\x00'
/tmp/test/0326eb043f804b7930c136027f9e5c0244891d1501ad13a80da06548c2e1a3d9
b'http://193.233.254.61/\x00\x00'
/tmp/test/9c1c2cd81b3e62965190e8656e1b3c45e0d8ae3d355c35d2ae81cf3b77d65152
/tmp/test/67ab1a6010aee0a8a790e69c7f22561d95cc074d165f98a840478d74768f9e4f
b'http://193.233.254.61/\x00\x00'
/tmp/test/395d7d16acadfae3604219aa1fe4ccf7142f8f215b96e2dc1a9880539c400dcb
b'http://193.233.254.61/\x00\x00'
/tmp/test/4584609ff61f249134d234d5629220e7aedcaef2b3f784afe7fb073f6e743a18
/tmp/test/970a982835c5f287c6c237f8e8e10281bbe4f4f37aa7120715f78379193a9bac
b'http://193.233.254.61/\x00\x00'
/tmp/test/bf38a3699ab2072dea806ff2ee3e54fca4abfa983be9cbb207c3ae8e65095364
b'http://193.233.254.61/\x00\x00'
/tmp/test/8878fe5756a50bc6e512c4f5d80ab84e4de1f50f671bfaec1423e39a044072fb
b'http://193.233.254.61/\x00\x00'
/tmp/test/bafcdf275e7887d57a3dcfb4721c8fd29a6cd0e6aa5c2b298834b2aacc7ac188
/tmp/test/b2fa41699e557d86b361dc0de9154f98ce20d3cee15ebb0f85e6af5bd4848024
b'http://193.233.254.61/\x00\x00'
/tmp/test/960808e357ca317bc29f5cbdad780954453c927e776d9408bc214d4618eb7d95
/tmp/test/14df3fd26f153ef688fd76bb28abfc6e422a9fded4d0cd9cf80efa6afcfb468c
b'http://193.233.254.61/\x00\x00'
/tmp/test/e58bfabc14ea859d0478579d96eff008650c26dfa9448a080b599156e5da2ea1
b'http://193.233.254.61/\x00\x00'
/tmp/test/4d24bcc5fea0dc2245bdd3529fb0018df64449e4a338929d4f2903951402cb56
b'http://193.233.254.61/\x00\x00'
/tmp/test/1bf40bde4f9ffd166044947caff6cf31a9b174a440848926a63ef769a1d6ba19
b'http://193.233.254.61/\x00\x00'
/tmp/test/780d565f97a8158bd2b30a2202bf6d5d72c4215ba6962e09a1c3710d83155767
b'http://193.233.254.61/\x00\x00'
/tmp/test/2c787ede99dd50ec562ec9d709e8631bb7d69efedb2d8406b938b9fea6cb8322
/tmp/test/30c3320896bbe590e10c1bfa08b252772e6257e5b9c7db51fadfc015081a2270
b'http://193.233.254.61/\x00\x00'
/tmp/test/520a27517beb6aef092d6330558b23bcf5459e71b99a956b15455ed89084ee76
b'http://193.233.254.61/\x00\x00'
/tmp/test/6e16a21dd020156d991cadb4f3a7906f456c904f4c4e7bf50cfe4e2bccaee06d
/tmp/test/41efcaa0ec4f018aa8acd2aab5d6dc75ecf8229d72b7ded6db95a9717154a57a
b'http://193.233.254.61/\x00\x00'
/tmp/test/4d7ac6351289074abfdc52ed135bd35038bc2fdd932e75b5be2cfa2a7de9a143
/tmp/test/3829f70f494fe49db9aae125a3f3904b88a29e2ddda1c17293422d6464bc5e85
b'http://193.233.254.61/\x00\x00'
/tmp/test/49fa56435473a6b4d8d3172f6a6ee7edd0b887025481d9b8ec93343ae4beb035
b'http://193.233.254.61/\x00\x00'
/tmp/test/e44de48e90d3a3c03917b7731f0c06c2e32a64c730d2abc3b4a7c80546f8c2c9
/tmp/test/aef9049a5c25a11e8273258a06e72d11d8c9c66c6ee5bce29cb1b69a2988e495
/tmp/test/258d77c694fd025a6ac2757f4227b6eb20016bf5a69f55ca020aecc5363db137
/tmp/test/a25b66447a714a14b726513827b39dee31ee96cba2ae7b360bcb96e8592e86f7
b'http://193.233.254.61/\x00\x00'
/tmp/test/54386fee06d9601e2d108569932bccb4d33cb7d06e9b0efb2ff91ca8b8d39e34
b'http://193.233.254.61/\x00\x00'
/tmp/test/db7d70d4f2b2cbc727f3aae9b522ce55a3930e92e3bf248549c00ba494d45489
b'http://193.233.254.61/\x00\x00'
/tmp/test/03d0542982344c31c8f087dd12c77bf2c0745a676031256e7eaed403b900c0c2
/tmp/test/50387b41f0396ac0ac4528632e18d3053471191f3a001b6e75caa1ca5c8f8804
b'http://193.233.254.61/\x00\x00'
/tmp/test/1a0cee71c61609c7a62caff0d7e6551afed262da6c856565af5b54a57ee89adb
b'http://193.233.254.61/\x00\x00'
/tmp/test/9512bddb087072a60cd38ebe7c3214dd200e8aef8ec1495817ad21015a6eb7a2
b'http://193.233.254.61/\x00\x00'
/tmp/test/fb7cdcc007aba2d1e5331cc2fc144226a5d9e8d20f8ed00bd92e640d7d9170ab
b'http://193.233.254.61/\x00\x00'
/tmp/test/9e232e8445c8b9fe662574f27ec9433386d14a92ead091803c70946c10150e03
/tmp/test/fa50ea6db6777895c9b03db397a269bd9e0ef56fda650926456ff358c42f86f5
/tmp/test/828a62c5a9a5cec7da8e5de1c8bbc8d02724570909ab41685b56595e688c5c4b
b'http://193.233.254.61/\x00\x00'
/tmp/test/0faeeafd3519a66f31bf4c453dd25fadc793f2473d58ff654ba88a5b229b2daa
b'http://193.233.254.61/\x00\x00'
/tmp/test/9fa7a7f154d626f3492fb32efcd0506ae7fadaca9c0ad6f58afe6177fc83b5f4
b'http://193.233.254.61/\x00\x00'
/tmp/test/e3363a7dec1fdd09dfb42b07c5a1f07af265f624a57c302ce062a0fed67b7511
b'http://193.233.254.61/\x00\x00'
/tmp/test/e63021e15b521441f3240589301782e32375fa1cf6363cc2a7ba24ff5bc89607
b'http://193.233.254.61/\x00\x00'
/tmp/test/cdfc8656033ef0eda0ae2eb6bc1db5f453aead1a2082cb5e4f6d9b9b89b268d7
/tmp/test/a60686284be7df16f043ac13c17a5795b0cc306d2ca922271e010f5caa2984a7
b'http://193.233.254.61/\x00\x00'
/tmp/test/f7c102956df1607d11fe7fcaf28aa3d9cf115f3f738fa27463bc059524fca16e
b'http://193.233.254.61/\x00\x00'
/tmp/test/ffde38c646f6327ec54f203188fde80f8d71168c602b5a35f6af9529c1283b6c
b'http://193.233.254.61/\x00\x00'
/tmp/test/6102d150c5d41a86781e3c26467ec58fb3a435009cb29ebd84c93b45d7b791c0
/tmp/test/630af90137926658fc94c44f09ab192cf2a8be729a2ebbcaa20aac98a842f5b9
b'http://193.233.254.61/\x00\x00'
/tmp/test/56e5d6c96d788471dfc25d34a587fb838a5e623a2bbe8514462c5e384829f8d0
b'http://193.233.254.61/\x00\x00'
/tmp/test/33d960872f673b5fa6e6340791fa14b6cffbaeca3defedfce09b0bdbd0312cc0
/tmp/test/c60e298ebd733e6bd08895acf69219d9e9e875cf316b45e4db03b3b9df6cefbc
/tmp/test/1468531a2341058beada9425d79af920878ab2a17a26021c0f96d2f8748176ac
b'http://193.233.254.61/\x00\x00'
/tmp/test/4e141ff278908c3dd57eabe17b8a3f34148ba552d8658265591ae32efd2ffe9c
b'http://193.233.254.61/\x00\x00'
/tmp/test/5b58307c0f5084a55024440c46c41df4f7ca66a2588bf54e03d50d2daf48c4ad
b'http://193.233.254.61/\x00\x00'
/tmp/test/feaf5a84933930c075e6cf3d4c79c859981981f867af68505106fd5b27193ccd
b'http://193.233.254.61/\x00\x00'
/tmp/test/46692170437bc326a8fbab2d4c08ca5a9f5efe0ad3e9c7284c3c1baa178aa7d7
/tmp/test/2c66182f2206461ff0f867788f6eac4b47f13dd949e2216907fcec5731a32f26
/tmp/test/f7649570fad801fba226030c6038c63c75fafe6f7cb596859388d5af16d180dc
b'http://193.233.254.61/\x00\x00'
/tmp/test/37ef3eb958c9d0f1a46f5889bdb6d68c0f70d3fe5ec7ca7987a509608309490f
b'http://193.233.254.61/\x00\x00'
/tmp/test/b8c4ac06dc5a056c16774cbecb0a910449c530fae797644f9bfbb8bc4971039b
b'http://193.233.254.61/\x00\x00'
/tmp/test/32a8c291b982f60ccf661ea907545c689358c49b417c025a307dd9438604ff99
b'http://193.233.254.61/\x00\x00'
/tmp/test/5df8202aa5d03ccfdcb444fe44229200cd6c79162985fc069d98b60c0e9db8e9
b'http://193.233.254.61/\x00\x00'
/tmp/test/6f09bf8357daf529f1b0f407aa2e53b97f3fd9af3dbed9b036c1d28b0ccb47fc
/tmp/test/db236789acea7a3be8ad5bf8981d9d13eaf0eb2f7fc155bc791f6486adf3b244
b'http://193.233.254.61/\x00\x00'
/tmp/test/b1086831d8165e73605595b6a29939c9cd40d04f2f216cd82b1011147cd5a443
b'http://193.233.254.61/\x00\x00'
/tmp/test/d6645851464027faab45e87abcfad3ad68614ce169f27e5f7b9be9d75c5a803e
/tmp/test/82955284142ee4ccedb8427999a226e79098fae3ab2bbbe4b1f18c69dac0f35b
b'http://193.233.254.61/\x00\x00'
/tmp/test/b5f3f04a54a5551c8ed092ef5d4c58fbe019f99dbb72823be416f7fcd5b4f6dc
b'http://193.233.254.61/\x00\x00'
/tmp/test/7c66575b0fcd722d1543b95e8234b83556ac6b0166437da68db6e04f2906b191
b'http://193.233.254.61/\x00\x00'
/tmp/test/2acc203829dede0c6116c9a13a11b8d674681d08996231b82ec84f78e4d00645
b'http://193.233.254.61/\x00\x00'
/tmp/test/9285a2d60bfb1e1ac7a92532914ccb88cfb9092e9ea0d28e52ed60bdf3e97b20
/tmp/test/71dc15afbf53b691a5b947fb6ea853cbf8a4d0efb638d38c8818512cd8086df0
b'http://193.233.254.61/\x00\x00'