Pikabot is a backdoor active since early 2023. It gives other actors a way into the network for ransomware, crypto-mining, data theft and remote control. Mail threads carrying an archive or a PDF were the first distribution route; I logged five more.
Distribution methods I observed
1 email thread with archive / PDF attachment
2 password-protected archive (password given in the mail)
3 further archive variant, different inner file
4 email, script runs a chain of fallbacks:
try the file directly -> try through a ping-based delay -> download from a URL
-> run the payload with rundll32
5 mail with a link -> zip -> JavaScript downloader
6 Excel file where the URL sits inside xl/drawings/_rels/drawing1.xml.rels
In method 6 the malicious URL is not in a macro at all: unzipping the workbook and reading the relationship file shows it. Heavily commented JavaScript from the later waves, once the comment lines are stripped, shows a download saved as MI.jpg, which is actually the Pikabot DLL, started with regsvr32.
The loader
1 force the "je" branch not to be taken so an exception fires
2 break on VirtualAlloc and VirtualProtect
3 first alloc: RW memory; two spaghetti-coded decryption layers (jump after
every instruction, changes each build, not worth reading)
4 VirtualProtect makes it executable; CALL into the decrypted code (~0x3000 bytes)
5 that code allocates ~75000 bytes and decrypts a full PE
6 section protections are set one by one, last the relocation section
7 control passes to the new PE
The dumped file holds two more PE files in its resources. They are clean and are there to distract a static reader. The real payload is a PNG-looking resource decrypted after many anti-debug checks.
Anti-debug and timing tricks
IsDebuggerPresent, CheckRemoteDebuggerPresent
vectored exception handler triggered with int3
NtQueryInformationProcess (debug port)
OutputDebugString + SetLastError probe
two GetTickCount64 calls and an rdtsc compare to detect single-stepping
GetWriteWatch to notice memory tracking
NtGlobalFlag in the process environment block
Beep() calls used as delays
The decryption loop works on fifteen bytes at a time, 0xbc1 times, to rebuild a payload of about 45 KB. The core injector then checks the process name; if it is wrong it deletes the decrypted file and exits.
Region filter
It exits on systems whose locale is Georgian, Kazakh, Tajik, Russian, Taiwanese, Belarusian or Slovenian.
After install
- A fixed mutex and a registry key are created.
- A PowerShell script from the server and the encrypted C2 list are stored under a key in HKCU\Software.
- Self-deletion is done with
cmd /C ping localhost && DEL /F /S /Q /A <file>.
cmd run a command through cmd.exe
exe / dll inject a downloaded executable or DLL
shellcode inject and run shellcode
ipconfig network details
processes list processes
knock_timeout change the beacon interval
Hashes
loader 45930399ef1b9bbefe69c443706e6573fa508ee34a11f2b9a865de8f2e3569b7
2nd stage 88c107b61850780224b0efccc1406d0cfec692a01524144dbcd36bfd5ec4ec74
core injector f84e36b4f2030ca6ecb037a85a35d56a324426ecf08b85c9c5cd3587ba3a6742