Hunting PrivateLoader: Pay-Per-Install Service

Cat hunting

PrivateLoader is a malware loader from a pay-per-install malware distribution service, that has been used to distribute info stealers, banking trojans, other loaders, and even ransomware, on machines running windows. First seen in early 2021 being hosted on websites that claim to provide cracked software, the customers of the service are able to selectively deliver malware to victims based on location, financial activity, environment, and specific software installed.

Let’s have a look at the malware and try to find a way to detect and hunt it.

Encrypted Stack Strings#

Here’s a sample analyzed by Zscaler on April 2022:


Let’s open it on Ghidra. Going into the entry point, following the code, looking for interesting functions, I quickly spot the function at 0x406360. It’s calling LoadLibraryA but the lpLibFileName parameter is built dynamically at runtime using the stack. Its seems that we found a string encryption technique. Both the string and the xor key are loaded into the stack. Looking a bit more through the function, its seems that this is the way most of the strings are loaded:

LEA       EAX=>local_50,[ESP + 0x10]
MOV       dword ptr [ESP + local_50[0]],0x84038676
MOV       dword ptr [ESP + local_50[4]],0xeb71eb3c
MOV       dword ptr [ESP + local_50[8]],0x36fb7b30
MOV       dword ptr [ESP + local_50[12]],0xab7d1f0c
MOVAPS    XMM1,xmmword ptr [ESP + local_50[0]]
MOV       dword ptr [ESP + local_30[0]],0xea71e31d
MOV       dword ptr [ESP + local_30[4]],0xd9428759
MOV       dword ptr [ESP + local_30[8]],0x5a971f1e
MOV       dword ptr [ESP + local_30[12]],0xab7d1f0c
PXOR      XMM1,xmmword ptr [ESP + local_30[0]] ; kernel32.dll
PUSH      EAX  ; LPCSTR lpLibFileName for LoadLibraryA
MOVAPS    xmmword ptr [ESP + local_50[0]],XMM1
CALL      ESI=>KERNEL32.DLL::LoadLibraryA

After XOR the encrypted string with the key, we get kernel32.dll.

Detecting The Malware#

This uncommon string decryption technique can be leveraged to write a Yara rule for detection and hunting purposes. To reduce the number of false positives and increase the rule performance, we can add some plaintext unicode strings used on the C2 communication and a few minor conditions. Here’s the rule:

After running this rule on VirusTotal retro hunting, I got over 1k samples on a 1 year timeframe. By manually analyzing some of the matches, I couldn’t find any false positives. As a first attempt of hunting and detecting PrivateLoader, this rule seems to yield good results.

Decrypting The Strings#

Now, to faster analyze the malware and better understand its behavior, we should build a string decryptor to help us on our reversing efforts and better document the code. With the help of Capstone disassembly framework, and some trial and error, here’s the script:

After running it against the sample we are analyzing, we get the following strings:

0x4013ee GetCurrentProcess
0x401469 CreateThread
0x4014ba CreateFileA
0x401506 Sleep
0x401572 SetPriorityClass
0x4015ec Shell32.dll
0x401657 SHGetFolderPathA
0x40183b null
0x402078 rb
0x402795 :1080
0x402839 \n
0x402f2d :1080
0x402fd1 :
0x4036ce .
0x4038ac .
0x403972 .
0x403a34 .
0x4043c0 HOST:
0x40446e :
0x4048a3 HOST:
0x404965 HOST:
0x404dcd HOST:
0x404f84 :
0x405439 URL:
0x405a5e https://
0x405ad8 .tmp
0x405bf6 \
0x4063e9 kernel32.dll
0x40644a WINHTTP.dll
0x4064a5 wininet.dll
0x407616 WinHttpConnect
0x407682 WinHttpOpenRequest
0x40771a WinHttpQueryDataAvailable
0x4077b2 WinHttpSendRequest
0x40784a WinHttpReceiveResponse
0x4078e2 WinHttpQueryHeaders
0x407956 WinHttpOpen
0x4079b5 WinHttpReadData
0x407a20 WinHttpCloseHandle
0x407b09 Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/93.0.4577.63 Safari/537.36
0x408402 http://
0x4084ab /
0x408582 ?
0x40951a HEAD
0x409fa8 Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/93.0.4577.63 Safari/537.36
0x40a1f0 wininet.dll
0x40a25b InternetSetOptionA
0x40a2ef HttpOpenRequestA
0x40a38d InternetConnectA
0x40a421 InternetOpenUrlA
0x40a49e InternetOpenA
0x40a4f2 HttpQueryInfoA
0x40a567 InternetQueryOptionA
0x40a5fb HttpSendRequestA
0x40a694 InternetReadFile
0x40a737 InternetCloseHandle
0x40a7ad Kernel32.dll
0x40a801 HeapAlloc
0x40a852 HeapFree
0x40a8a3 GetProcessHeap
0x40a8f3 CharNextA
0x40a938 User32.dll
0x40a994 GetLastError
0x40a9e5 CreateFileA
0x40aa36 WriteFile
0x40aa87 CloseHandle

We can now go back to Ghidra and continue our analysis, now with more context of what might be the malware’s behavior.

Network IOCs#

As a bonus, we get some network IOCs that can be used for defense and tracking purposes: