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.
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.
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.
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.
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.