Challenge 4 of Zero2Auto biweekly challenges

Table of Contents

Operation DreamJob challenge

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

The aim for this challenge AND FIX SENTENCE

The goal of this fourth challenge is slightly more different than the others, and relies on some level of static analysis to complete, so it may be difficult to get through. However, OSINT is also a possible method of attack if you are struggling, and in malware analysis there isn’t really such a thing as “cheating”, it’s just making your life easier!

So, the challenge for this week: We recently received a fairly large file of around 4MB in size, masquerading as the legitimate KeePass software. It is strongly believed that the file has been backdoored and contains malicious code, though we are unable to locate where the malicious code is stored and the purpose of it. Are you able to locate the malicious code, and identify the core functionality?

Unpacking

KEEPASS VERSION

could bindiff for future, if old version found

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.