Challenge 2 of Zero2Auto biweekly challenges

Table of Contents

IcedID malware challenge

This is a malware challenge related to the Zero2Automated course (number 2 out of currently 4).

The aim for this challenge was to unpack this IcedID binary, figure out how the configuration was stored, and develop a script to automatically extract the config information. The malware bazaar link also lets us know that the following IOC exists: ilekvoyn[.]com

Unpacking

In this case the file is a DLL file containing a number of exports.

Exports:

  • DllRegisterServer
  • HdQZgne
  • IfkPmdu
  • cJPSuqHBMN
  • pcufUY
  • rHqnYSA
  • zlmkoZLQMd

To execute this DLL for dumping via x64dbg it is executed with the help of rundll32.exe. Out of the existing exports, the DllRegisterServer looks the least suspicious, so we will start by executing that function.

"C:\Windows\System32\rundll32.exe" c:\Users\me\Desktop\0581f0bf260a11a5662d58b99a82ec756c9365613833bce8f102ec1235a7d4f7.dll,DllRegisterServer

To dump the extracted file, we set a breakpoint on VirtualAlloc and VirtualProtect.

After returning fom VirtualAlloc, the register rax contains 180000000, this area is later filled with a PE file.

memory

A future analysis could include figuring out, how the file is exactly loaded into memory, as there no visible imports of VirtualAlloc and VirtualProtect, LoadLibraryA and GetProcAddress.

The allocated memory is then dumped and fixed via PE-bear so that the dump can be loaded into a disassembler.

pe-bear

Decrypting the configuration

This new file is not very large, so the number of functions is limited and a function for gathering data and sending data via http requests can be identified. Furthermore, the path from the _start of the file to the decryption function is rather short, basically a thread is started and sleeps for a bit followed by a call to the decryption function. Before these functions are called there is a function that iterates over some memory and XORs part of that area with another part of data.

decrypt

xor     r8d, r8d  {0x0}
lea     r9, qword ptr [config]
sub     rcx, r9

lea     rdx, qword ptr [r8+r9]
inc     r8
mov     al, byte ptr [rdx+0x40]
xor     al, byte ptr [rdx]
mov     byte ptr [rcx+rdx+0x40], al
cmp     r8, 0x20
jb      0x180002435

ret      {__return_addr}

This code uses the first 0x20 bytes at the offset config to decrypt the next bytes.

The following screenshot shows the data at the config offset and at the bottom the output of the Python script presented in the next step.

config

Python script - binja

This is a small python script executed in the binja script editor to extract the c2 domain out of the config, the output is visible at the end of the previous screenshot.

bv.begin_undo_actions()
r = current_selection
start = r[0]

c2 = ""
for i in range(0x20):
    key_byte = (bv.read_int(start + i,1))
    byte_to_decrypt = bv.read_int(start + i + 0x40, 1)
    decode = (key_byte ^ byte_to_decrypt) % 256
    if i >= 4:
        c2 += chr(decode)

print("C2:", c2)
bv.commit_undo_actions()

C2 domain: ilekvoyn[.]com

It is possible that the config contains a bit more data, this might be interesting for a future look at it, the goal of this short writeup was just to extract the C2 domain.