Overview

Sample: 0A0C225F0E5EE941A79F2B7701F1285E4975A2859EB4D025D96D9E366E81ABB9

References:

Helper Functions

def unhex(hex_string):
    import binascii
    if type(hex_string) == str:
        return binascii.unhexlify(hex_string.encode('utf-8'))
    else:
        return binascii.unhexlify(hex_string)

def tohex(data):
    import binascii
    if type(data) == str:
        return binascii.hexlify(data.encode('utf-8'))
    else:
        return binascii.hexlify(data)

API Hashing

A simple API hashing algorithm is used to resolve LoadLibraryA and GetProcAddress.

The rest of the dynamically resolved APIs are resolved via GetProcAddress once their function names have been decrypted.

kernel32_hash = 999818334

string = b'k\x00e\x00r\x00n\x00e\x00l\x003\x002\x00.\x00d\x00l\x00l\x00'
hash_high = 0xffff
hash_low = 0xffff

for ptr in range(len(string)):
    hash_low = (hash_low + string[ptr])
    hash_high = (hash_high + hash_low) 
hash_high %= 0xFFF1
hash_low %= 0xFFF1

hash = (hash_high << 16) + hash_low

print(hex(hash))
print('===')
print(hex(kernel32_hash))
0x3b98045e
===
0x3b98045e

API String Decryption

A unique decryption method is used to both decrypt the API strings as well as other data stored in the ransomware binary. The decryption is split into two main functions, one used to generate a decryption key buffer, and another used to perform the actual decryption.

import struct 

def gen_key_buffer(buf1, buf2):
    key_buffer = [0]*256

    v3 = 240
    v4 = buf1[:4]
    v5 = buf1[4:8]
    v6 = buf1[8:12]
    result = buf1[12:]
    v3 = 240
    while v3 >= 0:
        for i in range(4):
            key_buffer[v3 + i + 12 ] = v4[i]
            key_buffer[v3 + i + 8 ] = result[i]
            key_buffer[v3 + i + 4 ] = v5[i]
            key_buffer[v3 + i] = v6[i]
        v4 = struct.pack('<I', (struct.unpack('<I',v4)[0] - 0x10101010) & 0xffffffff);
        result = struct.pack('<I', (struct.unpack('<I',result)[0] - 0x10101010) & 0xffffffff);
        v5 = struct.pack('<I', (struct.unpack('<I',v5)[0] - 0x10101010) & 0xffffffff);
        v6 = struct.pack('<I', (struct.unpack('<I',v6)[0] - 0x10101010) & 0xffffffff);
        v3 -= 16


    lo_v8 = 0
    v9 = 0
    v10 = 0
    flag_return = False
    while True:
        if flag_return:
            break
        while True:
            lo_result = key_buffer[v9] & 0xff
            lo_v8 = (lo_result + ((buf2[v10] + lo_v8) & 0xff)) & 0xff
            hi_result = key_buffer[lo_v8]
            v10 += 1
            key_buffer[lo_v8] = lo_result
            key_buffer[v9] = hi_result
            if v10 >= 16:
                break
            v9 += 1
            v9 &= 0xff
            if v9 == 0:
                flag_return = True
                break
        v10 = 0
        v9 += 1
        v9 &= 0xff
        if v9 == 0:
            break
    return key_buffer


def decrypt_data(data, key_buffer):
    data = list(data)
    data_len = len(data)
    key = key_buffer.copy()
    edx = 0
    cl = 0
    curr_index = 0
    eax = 0
    while data_len != 0:
        cl = (key[(1 + edx) & 0xff] + cl) & 0xFF  
        eax = key[(1 + edx) & 0xff] & 0xFF 
        ch = key[cl] & 0xFF             
        key[cl] = eax
        key[(1 + edx) & 0xff] = ch
        eax = (ch + eax) & 0xFF
        edx = (edx + 1) & 0xff
        data[curr_index] ^= key[eax]
        curr_index += 1
        data_len -= 1
    return bytes(data)

Setup Decryption Key Buffer

KEY_BUFFER can be used in the decryption algorithms as a global

KEY_BUFFER = gen_key_buffer(unhex('edf9e5ed8640fd53ab185838646bd9df'),unhex('92b2801a9c19867db6a5002936c1084a'))
decrypt_data(unhex('7b0d2ddb284b'),KEY_BUFFER)
b'ntdll\x00'

Config Decryption Functions

Because the config file is so large it needs it's own custom decryption wrapper to decrypt 256 bytes at a time. The decryption routine also needs to handle 256 blocks of data. The config is also compressed using aplib and the values are base64 encoded.

The following functions will aid in the config decryption.

Large Data Decrypt

def decrypt_large_data(data, key_buffer):
    out = b''
    for ptr in range(0,len(data),255):
        out += decrypt_data(data[ptr:ptr+255],key_buffer)
    return out

APLib

Credit: Sandor Nemes (snemes)

import struct
from binascii import crc32
from io import BytesIO

__all__ = ['APLib', 'decompress']
__version__ = '0.6'
__author__ = 'Sandor Nemes'


class APLib(object):

    __slots__ = 'source', 'destination', 'tag', 'bitcount', 'strict'

    def __init__(self, source, strict=True):
        self.source = BytesIO(source)
        self.destination = bytearray()
        self.tag = 0
        self.bitcount = 0
        self.strict = bool(strict)

    def getbit(self):
        # check if tag is empty
        self.bitcount -= 1
        if self.bitcount < 0:
            # load next tag
            self.tag = ord(self.source.read(1))
            self.bitcount = 7

        # shift bit out of tag
        bit = self.tag >> 7 & 1
        self.tag <<= 1

        return bit

    def getgamma(self):
        result = 1

        # input gamma2-encoded bits
        while True:
            result = (result << 1) + self.getbit()
            if not self.getbit():
                break

        return result

    def depack(self):
        r0 = -1
        lwm = 0
        done = False

        try:

            # first byte verbatim
            self.destination += self.source.read(1)

            # main decompression loop
            while not done:
                if self.getbit():
                    if self.getbit():
                        if self.getbit():
                            offs = 0
                            for _ in range(4):
                                offs = (offs << 1) + self.getbit()

                            if offs:
                                self.destination.append(self.destination[-offs])
                            else:
                                self.destination.append(0)

                            lwm = 0
                        else:
                            offs = ord(self.source.read(1))
                            length = 2 + (offs & 1)
                            offs >>= 1

                            if offs:
                                for _ in range(length):
                                    self.destination.append(self.destination[-offs])
                            else:
                                done = True

                            r0 = offs
                            lwm = 1
                    else:
                        offs = self.getgamma()

                        if lwm == 0 and offs == 2:
                            offs = r0
                            length = self.getgamma()

                            for _ in range(length):
                                self.destination.append(self.destination[-offs])
                        else:
                            if lwm == 0:
                                offs -= 3
                            else:
                                offs -= 2

                            offs <<= 8
                            offs += ord(self.source.read(1))
                            length = self.getgamma()

                            if offs >= 32000:
                                length += 1
                            if offs >= 1280:
                                length += 1
                            if offs < 128:
                                length += 2

                            for _ in range(length):
                                self.destination.append(self.destination[-offs])

                            r0 = offs

                        lwm = 1
                else:
                    self.destination += self.source.read(1)
                    lwm = 0

        except (TypeError, IndexError):
            if self.strict:
                raise RuntimeError('aPLib decompression error')

        return bytes(self.destination)

    def pack(self):
        raise NotImplementedError


def aplib_decompress(data, strict=False):
    packed_size = None
    packed_crc = None
    orig_size = None
    orig_crc = None
    if data.startswith(b'AP32') and len(data) >= 24:
        # data has an aPLib header
        header_size, packed_size, packed_crc, orig_size, orig_crc = struct.unpack_from('=IIIII', data, 4)
        data = data[header_size : header_size + packed_size]
    if strict:
        if packed_size is not None and packed_size != len(data):
            raise RuntimeError('Packed data size is incorrect')
        if packed_crc is not None and packed_crc != crc32(data):
            raise RuntimeError('Packed data checksum is incorrect')
    result = APLib(data, strict=strict).depack()
    if strict:
        if orig_size is not None and orig_size != len(result):
            raise RuntimeError('Unpacked data size is incorrect')
        if orig_crc is not None and orig_crc != crc32(result):
            raise RuntimeError('Unpacked data checksum is incorrect')
    return result

Config Decryption

The config is stored in the data or ndata section of the PE file. The first two 16-byte blocks are the components of the key buffer. Following the key material is a DWORD that indicates the size of the encrypted config. This is followed by the encrypted config itself.

The actual config decryption process is as follows.

  • Find the data section
  • Locate the size of the config at offset 0x20
  • Extract the encrypted config
  • Decrypt config using the custom decryption algorithm
  • Decompress the resulting data using aplib
import pefile
import struct

RANSOMWARE_FILE = r'/tmp/darkside.bin'

data = open(RANSOMWARE_FILE, 'rb').read()
pe = pefile.PE(data=data)

# Get section with config
section_data = None
for s in pe.sections:
    if b'ndata' in s.Name:
        section_data = s.get_data()
        break

# Extract config
config_length = struct.unpack('<I',section_data[0x20:0x24])[0]
enc_data = section_data[0x24:0x24+config_length]

# Decrypt config
ap_data = decrypt_large_data(enc_data, KEY_BUFFER)

# Decompress config data with aplib
ptxt_data = aplib_decompress(ap_data)
ptxt_data
b"\x01\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xef&u>\x87\x15\xd8(\xb1\xf3A\xef\xb1\xc9\xd3\xdbw\xd2\x08\xad\x1c/\xaa\xd0,\xf4\xc7\xbc<s\x89k\xd9\x88!s\xe31\xbe\xd4\xcb}W\x9d;\xf5\xacnt\xe5O\x07gBe\xed\xc5\xc8\x1f\xe5\x90\x8e\xa4\xdeb *\xe9\xac\x90\x8d\x03\xb3\x13\xc1\x9d*\xb2\xb1]W\x19\x08W\x0fa\x0e L\xd8\xe2\xd2\t\x11\x14Oo\xf2\xd8a\xca\xc4\xa1\x81`\xdb\x15\x916\n\xf5W\xbc\xc2\xe8\xb9D\x13_j}Q\xda\x802\x90:u0607b8382472634\x00\xd0\x90\xe4\x95o\xe6,'\x19VG\x14wXCy\x02\x01\x00\x01\x01\x01\x01\x01\x00\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x02\x010\x00\x00\x00\xdd\x02\x00\x00.\x04\x00\x00\xb7\x06\x00\x00\xd0\x06\x00\x00\xf1\x06\x00\x00\xb6\x07\x00\x00\x8b\n\x00\x00d\x0b\x00\x00\x00\x00\x00\x00\xb5\x0b\x00\x00\xbe\x0c\x00\x00JAByAGUAYwB5AGMAbABlAC4AYgBpAG4AAABjAG8AbgBmAGkAZwAuAG0AcwBpAAAAJAB3AGkAbgBkAG8AdwBzAC4AfgBiAHQAAAAkAHcAaQBuAGQAbwB3AHMALgB+AHcAcwAAAHcAaQBuAGQAbwB3AHMAAABhAHAAcABkAGEAdABhAAAAYQBwAHAAbABpAGMAYQB0AGkAbwBuACAAZABhAHQAYQAAAGIAbwBvAHQAAABnAG8AbwBnAGwAZQAAAG0AbwB6AGkAbABsAGEAAABwAHIAbwBnAHIAYQBtACAAZgBpAGwAZQBzAAAAcAByAG8AZwByAGEAbQAgAGYAaQBsAGUAcwAgACgAeAA4ADYAKQAAAHAAcgBvAGcAcgBhAG0AZABhAHQAYQAAAHMAeQBzAHQAZQBtACAAdgBvAGwAdQBtAGUAIABpAG4AZgBvAHIAbQBhAHQAaQBvAG4AAAB0AG8AcgAgAGIAcgBvAHcAcwBlAHIAAAB3AGkAbgBkAG8AdwBzAC4AbwBsAGQAAABpAG4AdABlAGwAAABtAHMAbwBjAGEAYwBoAGUAAABwAGUAcgBmAGwAbwBnAHMAAAB4ADYANABkAGIAZwAAAHAAdQBiAGwAaQBjAAAAYQBsAGwAIAB1AHMAZQByAHMAAABkAGUAZgBhAHUAbAB0AAAAAAB=\x00YQB1AHQAbwByAHUAbgAuAGkAbgBmAAAAYgBvAG8AdAAuAGkAbgBpAAAAYgBvAG8AdABmAG8AbgB0AC4AYgBpAG4AAABiAG8AbwB0AHMAZQBjAHQALgBiAGEAawAAAGQAZQBzAGsAdABvAHAALgBpAG4AaQAAAGkAYwBvAG4AYwBhAGMAaABlAC4AZABiAAAAbgB0AGwAZAByAAAAbgB0AHUAcwBlAHIALgBkAGEAdAAAAG4AdAB1AHMAZQByAC4AZABhAHQALgBsAG8AZwAAAG4AdAB1AHMAZQByAC4AaQBuAGkAAAB0AGgAdQBtAGIAcwAuAGQAYgAAAAAA\x00MwA4ADYAAABhAGQAdgAAAGEAbgBpAAAAYgBhAHQAAABiAGkAbgAAAGMAYQBiAAAAYwBtAGQAAABjAG8AbQAAAGMAcABsAAAAYwB1AHIAAABkAGUAcwBrAHQAaABlAG0AZQBwAGEAYwBrAAAAZABpAGEAZwBjAGEAYgAAAGQAaQBhAGcAYwBmAGcAAABkAGkAYQBnAHAAawBnAAAAZABsAGwAAABkAHIAdgAAAGUAeABlAAAAaABsAHAAAABpAGMAbAAAAGkAYwBuAHMAAABpAGMAbwAAAGkAYwBzAAAAaQBkAHgAAABsAGQAZgAAAGwAbgBrAAAAbQBvAGQAAABtAHAAYQAAAG0AcwBjAAAAbQBzAHAAAABtAHMAcwB0AHkAbABlAHMAAABtAHMAdQAAAG4AbABzAAAAbgBvAG0AZQBkAGkAYQAAAG8AYwB4AAAAcAByAGYAAABwAHMAMQAAAHIAbwBtAAAAcgB0AHAAAABzAGMAcgAAAHMAaABzAAAAcwBwAGwAAABzAHkAcwAAAHQAaABlAG0AZQAAAHQAaABlAG0AZQBwAGEAYwBrAAAAdwBwAHgAAABsAG8AYwBrAAAAawBlAHkAAABoAHQAYQAAAG0AcwBpAAAAcABkAGIAAAAAAH==\x00YgBhAGMAawB1AHAAAAAAAC==\x00cwBxAGwAAABzAHEAbABpAHQAZQAAAAAA\x00dgBtAGMAbwBtAHAAdQB0AGUALgBlAHgAZQAAAHYAbQBtAHMALgBlAHgAZQAAAHYAbQB3AHAALgBlAHgAZQAAAHMAdgBjAGgAbwBzAHQALgBlAHgAZQAAAFQAZQBhAG0AVgBpAGUAdwBlAHIALgBlAHgAZQAAAGUAeABwAGwAbwByAGUAcgAuAGUAeABlAAAAAAB=\x00cwBxAGwAAABvAHIAYQBjAGwAZQAAAG8AYwBzAHMAZAAAAGQAYgBzAG4AbQBwAAAAcwB5AG4AYwB0AGkAbQBlAAAAYQBnAG4AdABzAHYAYwAAAGkAcwBxAGwAcABsAHUAcwBzAHYAYwAAAHgAZgBzAHMAdgBjAGMAbwBuAAAAbQB5AGQAZQBzAGsAdABvAHAAcwBlAHIAdgBpAGMAZQAAAG8AYwBhAHUAdABvAHUAcABkAHMAAABlAG4AYwBzAHYAYwAAAGYAaQByAGUAZgBvAHgAAAB0AGIAaQByAGQAYwBvAG4AZgBpAGcAAABtAHkAZABlAHMAawB0AG8AcABxAG8AcwAAAG8AYwBvAG0AbQAAAGQAYgBlAG4AZwA1ADAAAABzAHEAYgBjAG8AcgBlAHMAZQByAHYAaQBjAGUAAABlAHgAYwBlAGwAAABpAG4AZgBvAHAAYQB0AGgAAABtAHMAYQBjAGMAZQBzAHMAAABtAHMAcAB1AGIAAABvAG4AZQBuAG8AdABlAAAAbwB1AHQAbABvAG8AawAAAHAAbwB3AGUAcgBwAG4AdAAAAHMAdABlAGEAbQAAAHQAaABlAGIAYQB0AAAAdABoAHUAbgBkAGUAcgBiAGkAcgBkAAAAdgBpAHMAaQBvAAAAdwBpAG4AdwBvAHIAZAAAAHcAbwByAGQAcABhAGQAAABuAG8AdABlAHAAYQBkAAAAAAA=\x00dgBzAHMAAABzAHEAbAAAAHMAdgBjACQAAABtAGUAbQB0AGEAcwAAAG0AZQBwAG8AYwBzAAAAcwBvAHAAaABvAHMAAAB2AGUAZQBhAG0AAABiAGEAYwBrAHUAcAAAAEcAeABWAHMAcwAAAEcAeABCAGwAcgAAAEcAeABGAFcARAAAAEcAeABDAFYARAAAAEcAeABDAEkATQBnAHIAAAAAAH==\x00YgBhAHIAbwBxAHUAZQB0AGUAZQBzAC4AYwBvAG0AAAByAHUAbQBhAGgAcwBpAGEALgBjAG8AbQAAAAAA\x00VwBlAGwAYwBvAG0AZQAgAHQAbwAgAEQAYQByAGsAUwBpAGQAZQAhACAADQAKACAAIAANAAoAIABBAGwAbAAgAFkAbwB1AHIAIABGAGkAbABlAHMAIABBAHIAZQAgAEUAbgBjAHIAeQBwAHQAZQBkACEAIAANAAoAIAAgAA0ACgAgAEYAaQBuAGQAIAAlAHMAIABBAG4AZAAgAEYAbwBsAGwAbwB3ACAASQBuAHMAdAByAHUAYwB0AGkAbwBuAHMAIQAAAAAA\x00LS0tLS0tLS0tLS0gWyBXZWxjb21lIHRvIERhcmtTaWRlIF0gLS0tLS0tLS0tLS0tLT4gDQogIA0KIFdoYXQgaGFwcGVuZD8gDQogLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSANCiBZb3VyIGNvbXB1dGVycyBhbmQgc2VydmVycyBhcmUgZW5jcnlwdGVkLCBiYWNrdXBzIGFyZSBkZWxldGVkLiBXZSB1c2Ugc3Ryb25nIGVuY3J5cHRpb24gYWxnb3JpdGhtcywgc28geW91IGNhbm5vdCBkZWNyeXB0IHlvdXIgZGF0YS4gDQogQnV0IHlvdSBjYW4gcmVzdG9yZSBldmVyeXRoaW5nIGJ5IHB1cmNoYXNpbmcgYSBzcGVjaWFsIHByb2dyYW0gZnJvbSB1cyAtIHVuaXZlcnNhbCBkZWNyeXB0b3IuIFRoaXMgcHJvZ3JhbSB3aWxsIHJlc3RvcmUgYWxsIHlvdXIgbmV0d29yay4gDQogRm9sbG93IG91ciBpbnN0cnVjdGlvbnMgYmVsb3cgYW5kIHlvdSB3aWxsIHJlY292ZXIgYWxsIHlvdXIgZGF0YS4gDQogIA0KIERhdGEgbGVhayANCiAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tIA0KIEZpcnN0IG9mIGFsbCB3ZSBoYXZlIGRvd25sb2FkZWQgbW9yZSB0aGVuIDUwMEdCIGRhdGEgZnJvbSB5b3VyIG5ldHdvcmsuIA0KICANCiBJbmNsdWRlZDogDQogLUFjY291bnRpbmcgZGF0YSANCiAtRmluYW5jZSBkYXRhIA0KIC1IUiANCiAtRW1wbG95ZWVzIGNvbmZpZGVudGlhbCBkYXRhKHBob3RvcywgYmVuZWZpdHMsIHRheGVzLCBldGMpIA0KIC1NYXJrZXRpbmcgDQogLUJ1ZGdldHMgDQogLVRheGVzKHNhbGVzIHRheCBjb21wbGlhbmNlLCBwcm9wZXJ0eSwgaW5jb21lIGFuZCBmcmFuY2hpc2UgdGF4ZXMsIGV0YykgDQogLVBheXJvbGxzIA0KIC1CYW5raW5nIGRhdGEgDQogLUFyYml0cmF0aW9uIA0KIC1TY2FucyANCiAtSW5zdXJhbmNlIA0KIC1SZWNvbmNpbGlhdGlvbnMgDQogLVJlcG9ydHMobW9udGhseSBiYW5rIGludmVudG9yeSwgbW9udGhseSBmaW5hbmNpYWwsIGNsYWltcyByZXBvcnRzLCBldGMpIA0KIC1BdWRpdHMoREhHLCBpbnN1cmFuY2UgYXVkaXRzLCBldGMpIA0KIC1CMkIgY2xpZW50cyBjb25maWcgZGF0YSANCiAtQ29uZmlkZW50aWFsaXR5IDIwMjAgDQogLTIwMjAsIDIwMjEgQnVzaW5lc3MgcGxhbnMgDQogLTIwMTksIDIwMjAsIDIwMjEgeWVhcnMgQ2xvc2luZyAoZnVsbCBkdW1wcykgDQogLWFuZCBhIGxvdCBvZiBvdGhlciBzZW5zaXRpdmUgZGF0YSANCiAgDQogWW91ciBwZXJzb25hbCBsZWFrIHBhZ2U6IGh0dHA6Ly9kYXJrc2lkYzNpdXg0NjJuNnl1bmV2b2FnNTJudHZ3cDZ3dWxhejN6aXJrbWg0Y256NmhoajdpZC5vbmlvbi8xNjIvdGhlZGl4aWVncm91cC9MQ2Z5SFJjd2ZmcllUYmxwWnZvUE8zWERicllQY051MHdWQXNINXA0OUxTakJmelRtdGRYVDQ4YXpYRmxNdTdxIA0KIE9uIHRoZSBwYWdlIHlvdSB3aWxsIGZpbmQgZXhhbXBsZXMgb2YgZmlsZXMgdGhhdCBoYXZlIGJlZW4gZG93bmxvYWRlZC4gDQogVGhlIGRhdGEgaXMgcHJlbG9hZGVkIGFuZCB3aWxsIGJlIGF1dG9tYXRpY2FsbHkgcHVibGlzaGVkIGlmIHlvdSBkbyBub3QgcGF5LiANCiBBZnRlciBwdWJsaWNhdGlvbiwgeW91ciBkYXRhIHdpbGwgYmUgYXZhaWxhYmxlIGZvciBhdCBsZWFzdCA2IG1vbnRocyBvbiBvdXIgdG9yIGNkbiBzZXJ2ZXJzLiANCiAgDQogV2UgYXJlIHJlYWR5OiANCiAtIFRvIHByb3ZpZGUgeW91IHRoZSBldmlkZW5jZSBvZiBzdG9sZW4gZGF0YSANCiAtIFRvIGRlbGV0ZSBhbGwgdGhlIHN0b2xlbiBkYXRhLiANCiAgDQogIA0KIFdoYXQgZ3VhcmFudGVlcz8gDQogLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSANCiBXZSB2YWx1ZSBvdXIgcmVwdXRhdGlvbi4gSWYgd2UgZG8gbm90IGRvIG91ciB3b3JrIGFuZCBsaWFiaWxpdGllcywgbm9ib2R5IHdpbGwgcGF5IHVzLiBUaGlzIGlzIG5vdCBpbiBvdXIgaW50ZXJlc3RzLiANCiBBbGwgb3VyIGRlY3J5cHRpb24gc29mdHdhcmUgaXMgcGVyZmVjdGx5IHRlc3RlZCBhbmQgd2lsbCBkZWNyeXB0IHlvdXIgZGF0YS4gV2Ugd2lsbCBhbHNvIHByb3ZpZGUgc3VwcG9ydCBpbiBjYXNlIG9mIHByb2JsZW1zLiANCiBXZSBndWFyYW50ZWUgdG8gZGVjcnlwdCBvbmUgZmlsZSBmb3IgZnJlZS4gR28gdG8gdGhlIHNpdGUgYW5kIGNvbnRhY3QgdXMuIA0KICANCiBIb3cgdG8gZ2V0IGFjY2VzcyBvbiB3ZWJzaXRlPyAgDQogLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSANCiBVc2luZyBhIFRPUiBicm93c2VyOiANCiAxKSBEb3dubG9hZCBhbmQgaW5zdGFsbCBUT1IgYnJvd3NlciBmcm9tIHRoaXMgc2l0ZTogaHR0cHM6Ly90b3Jwcm9qZWN0Lm9yZy8gDQogMikgT3BlbiBvdXIgd2Vic2l0ZTogaHR0cDovL2RhcmsyNHp6MzZ4bTR5MnBod2U3eXZua2tra2h4aW9uaGZyd3A2N2F3cGIzcjNiZGNuZWl2b3FkLm9uaW9uL1c1N01SSTlDN1laSlVaRUFCQkJZUlFMU1VURzIySlo5TUFIMFdUMUlTSEM0MDVLUDdaMlVXWTNBSTNKNjhETk0gDQogIA0KIFdoZW4geW91IG9wZW4gb3VyIHdlYnNpdGUsIHB1dCB0aGUgZm9sbG93aW5nIGRhdGEgaW4gdGhlIGlucHV0IGZvcm06IA0KIEtleTogDQogIA0KIHVnOGxncFgzV3JGemxFSjZIQldsd0puZjdqZW1oZm5seEJ3OXBvcmoxdXVZRlRnS2J4SlFKTFlpdGVRUzdEd2dabjdkSDBmczdxUFBXbVo2aW5QdjVHVG1TSlpOQWpHTFZJamQ0U29peVRkR3lvcGhmMHpQQnh4NnVFQU9KeE0wV29vNFpHZUtWb1VESHRac3FaTm5oTUY3YVBoNTRWbktwSUpYaVpEYlpadzRQMDZ4VHV3MVVNZWlURTd3ZGc3SFdaTWVwQVZUekVJMlcwNFJia1BGUUhmVWdFRGNzbER4YnI4M0J2b3BZVFlHS0ZSbXROVU1IOE9zT1pRck90djUweFdEYU9mYnF4YnpmSE1KbTMwUUdhR3BneWxKSFFac3NjejNYQm53SWR2bHdCSjlLTjREVmdGZ3ppUmR2d0pyZkNQNllOMUNZVE9RZ3cxcnpxbUlVNEcxeEdZdjdyRTNqaUJZMXM0RDNZMjZTYnBwVGNlQVZNdTFtS3g1Q0ZJRTNFYnRjQXNOdEVxTEhEYlBuTUN2VTZBcHdwMTdUWEdvYjh4WEpwRURCWmhJemRUYUN1eWJjcHJ3Y0ZOVE96Y2NqYklIODFXMzlNcmNKaTltTk8za0hSZTVmeG1JRkt2Yzl2OGFRRGloR3lDNjVEdGRhYnlCamlkWEkxTnlOT05UNFBUeXJ4WXFnZmZQc05ERnV6ejJ5TXJYaVRBd3RBUVBxbnk1QkJKUXNmVmhwTFhUdG5MdldnMSANCiAgDQogISEhIERBTkdFUiAhISEgDQogRE8gTk9UIE1PRElGWSBvciB0cnkgdG8gUkVDT1ZFUiBhbnkgZmlsZXMgeW91cnNlbGYuIFdlIFdJTEwgTk9UIGJlIGFibGUgdG8gUkVTVE9SRSB0aGVtLiAgDQogISEhIERBTkdFUiAhISE="

Parsing The Config

The first 128 bytes of the config are the RSA exponent followed by the 128 bytes RSA modulus.

After the modulus is a 32 buffer containing a null terminated ascii string representing the affiliate ID followed by some random data.

Next is a 22 bytes buffer containing a series of binary configuration flags.

Next is a DWORD indicating where the start of the next configuration value.

The following config values are base64 encoded and seperated by null bytes.

import base64

ptr = 0
rsa_exponent = ptxt_data[ptr:128]
ptr += 128
rsa_mod = ptxt_data[ptr:ptr+128]
ptr += 128
affiliate_id_data = ptxt_data[ptr:ptr+32]
affiliate_id = affiliate_id_data.split(b'\x00')[0]
ptr+= 32
config_flags = ptxt_data[ptr:ptr+22]
ptr+= 22
config_values_offset = struct.unpack('<I',ptxt_data[ptr:ptr+4])[0]
config_values_buffer = ptxt_data[ptr+config_values_offset:]
config_values = []
for c in config_values_buffer.split(b'\x00'):
    config_values.append(base64.b64decode(c).split(b'\x00\x00'))
print("Affiliate ID: %s\n" % affiliate_id)
for c in config_values:
    print("%s\n" % b' | '.join([s.replace(b'\x00',b'') for s in c]))
Affiliate ID: b'0607b8382472634'

b'$recycle.bin | config.msi | $windows.~bt | $windows.~ws | windows | appdata | application data | boot | google | mozilla | program files | program files (x86) | programdata | system volume information | tor browser | windows.old | intel | msocache | perflogs | x64dbg | public | all users | default |  | '

b'autorun.inf | boot.ini | bootfont.bin | bootsect.bak | desktop.ini | iconcache.db | ntldr | ntuser.dat | ntuser.dat.log | ntuser.ini | thumbs.db |  | '

b'386 | adv | ani | bat | bin | cab | cmd | com | cpl | cur | deskthemepack | diagcab | diagcfg | diagpkg | dll | drv | exe | hlp | icl | icns | ico | ics | idx | ldf | lnk | mod | mpa | msc | msp | msstyles | msu | nls | nomedia | ocx | prf | ps1 | rom | rtp | scr | shs | spl | sys | theme | themepack | wpx | lock | key | hta | msi | pdb |  | '

b'backup |  | '

b'sql | sqlite |  | '

b'vmcompute.exe | vmms.exe | vmwp.exe | svchost.exe | TeamViewer.exe | explorer.exe |  | '

b'sql | oracle | ocssd | dbsnmp | synctime | agntsvc | isqlplussvc | xfssvccon | mydesktopservice | ocautoupds | encsvc | firefox | tbirdconfig | mydesktopqos | ocomm | dbeng50 | sqbcoreservice | excel | infopath | msaccess | mspub | onenote | outlook | powerpnt | steam | thebat | thunderbird | visio | winword | wordpad | notepad |  | '

b'vss | sql | svc$ | memtas | mepocs | sophos | veeam | backup | GxVss | GxBlr | GxFWD | GxCVD | GxCIMgr |  | '

b'baroquetees.com | rumahsia.com |  | '

b'Welcome to DarkSide! \r\n  \r\n All Your Files Are Encrypted! \r\n  \r\n Find %s And Follow Instructions! |  | '

b'----------- [ Welcome to DarkSide ] -------------> \r\n  \r\n What happend? \r\n ---------------------------------------------- \r\n Your computers and servers are encrypted, backups are deleted. We use strong encryption algorithms, so you cannot decrypt your data. \r\n But you can restore everything by purchasing a special program from us - universal decryptor. This program will restore all your network. \r\n Follow our instructions below and you will recover all your data. \r\n  \r\n Data leak \r\n ---------------------------------------------- \r\n First of all we have downloaded more then 500GB data from your network. \r\n  \r\n Included: \r\n -Accounting data \r\n -Finance data \r\n -HR \r\n -Employees confidential data(photos, benefits, taxes, etc) \r\n -Marketing \r\n -Budgets \r\n -Taxes(sales tax compliance, property, income and franchise taxes, etc) \r\n -Payrolls \r\n -Banking data \r\n -Arbitration \r\n -Scans \r\n -Insurance \r\n -Reconciliations \r\n -Reports(monthly bank inventory, monthly financial, claims reports, etc) \r\n -Audits(DHG, insurance audits, etc) \r\n -B2B clients config data \r\n -Confidentiality 2020 \r\n -2020, 2021 Business plans \r\n -2019, 2020, 2021 years Closing (full dumps) \r\n -and a lot of other sensitive data \r\n  \r\n Your personal leak page: http://darksidc3iux462n6yunevoag52ntvwp6wulaz3zirkmh4cnz6hhj7id.onion/162/thedixiegroup/LCfyHRcwffrYTblpZvoPO3XDbrYPcNu0wVAsH5p49LSjBfzTmtdXT48azXFlMu7q \r\n On the page you will find examples of files that have been downloaded. \r\n The data is preloaded and will be automatically published if you do not pay. \r\n After publication, your data will be available for at least 6 months on our tor cdn servers. \r\n  \r\n We are ready: \r\n - To provide you the evidence of stolen data \r\n - To delete all the stolen data. \r\n  \r\n  \r\n What guarantees? \r\n ---------------------------------------------- \r\n We value our reputation. If we do not do our work and liabilities, nobody will pay us. This is not in our interests. \r\n All our decryption software is perfectly tested and will decrypt your data. We will also provide support in case of problems. \r\n We guarantee to decrypt one file for free. Go to the site and contact us. \r\n  \r\n How to get access on website?  \r\n ---------------------------------------------- \r\n Using a TOR browser: \r\n 1) Download and install TOR browser from this site: https://torproject.org/ \r\n 2) Open our website: http://dark24zz36xm4y2phwe7yvnkkkkhxionhfrwp67awpb3r3bdcneivoqd.onion/W57MRI9C7YZJUZEABBBYRQLSUTG22JZ9MAH0WT1ISHC405KP7Z2UWY3AI3J68DNM \r\n  \r\n When you open our website, put the following data in the input form: \r\n Key: \r\n  \r\n ug8lgpX3WrFzlEJ6HBWlwJnf7jemhfnlxBw9porj1uuYFTgKbxJQJLYiteQS7DwgZn7dH0fs7qPPWmZ6inPv5GTmSJZNAjGLVIjd4SoiyTdGyophf0zPBxx6uEAOJxM0Woo4ZGeKVoUDHtZsqZNnhMF7aPh54VnKpIJXiZDbZZw4P06xTuw1UMeiTE7wdg7HWZMepAVTzEI2W04RbkPFQHfUgEDcslDxbr83BvopYTYGKFRmtNUMH8OsOZQrOtv50xWDaOfbqxbzfHMJm30QGaGpgylJHQZsscz3XBnwIdvlwBJ9KN4DVgFgziRdvwJrfCP6YN1CYTOQgw1rzqmIU4G1xGYv7rE3jiBY1s4D3Y26SbppTceAVMu1mKx5CFIE3EbtcAsNtEqLHDbPnMCvU6Apwp17TXGob8xXJpEDBZhIzdTaCuybcprwcFNTOzccjbIH81W39MrcJi9mNO3kHRe5fxmIFKvc9v8aQDihGyC65DtdabyBjidXI1NyNONT4PTyrxYqgffPsNDFuzz2yMrXiTAwtAQPqny5BBJQsfVhpLXTtnLvWg1 \r\n  \r\n !!! DANGER !!! \r\n DO NOT MODIFY or try to RECOVER any files yourself. We WILL NOT be able to RESTORE them.  \r\n !!! DANGER !!!'