Pikabot: six distribution methods and an anti-debug gauntlet

by psyrun / 2023-08-08 / Loader

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