The Kuluoz malware family first emerged in 2012, so it’s relatively old and doesn’t use advanced techniques. Since it’s Christmas eve and everybody is home, I decided to unwrap a malware sample before everyone else unwraps presents tomorrow.

For this post we will use a Windows 32-bit executable which presents itself as an invoice from FedEx. The sample:

$ sha256sum FedEx_Label_ID_Order_83-27-4534US.exe
4f96daf417ddaba8faaf2b45fe5cc044a12f611875e99eef53255b11ded9668c

It even has a misleading icon, which I’m sure tricked a few boomers:

Misleading exe icon

Definitely not a Windows Executable

I originally downloaded this sample from kernelmode.info, which identified it as Kuluoz. The site is now offline unfortunately, but savy readers can figure out where to obtain this file.

Static Analysis

Before running this malicious code, let’s try and reverse it using our brain.

Pre-Analysis

For pre-analysis, readpe and pescan fail to find anything interesting, which isn’t unusual. Manalyze failed to find anything as well, but maybe I used it wrong.

The “cloud” tools fared better, and actually gave me hits: here are the Hybrid Analysis and VirusTotal reports.

To its credit, ClamAV raised the alarm:

$ clamscan FedEx_Label_ID_Order_83-27-4534US.exe.disabled 
Loading:    12s, ETA:   0s [========================>]    8.59M/8.59M sigs       
Compiling:   2s, ETA:   0s [========================>]       41/41 tasks 

/home/cam/sandbox/FedEx_Label_ID_Order_83-27-4534US.exe.disabled: Win.Trojan.Dapato-1688 FOUND

----------- SCAN SUMMARY -----------
Known viruses: 8585273
Engine version: 0.104.1
Scanned directories: 0
Scanned files: 1
Infected files: 1
Data scanned: 0.04 MB
Data read: 0.04 MB (ratio 1.00:1)
Time: 14.868 sec (0 m 14 s)
Start Date: 2021:12:24 16:51:30
End Date:   2021:12:24 16:51:45

Interestingly ClamAV claims it’s a copy of the Dapato dropper, so I’m not sure what the authoritative source or lineage of this sample is. But this is my blog, so I get to say it’s Kuluoz.

Stage 1

Let’s load this into a disassembler to see what this bad boy has (NB: labels and variable names in this post are my additions):

The entry point

The entry point

Well, that’s not helpful. If you’re wondering, the rough pseudo-code equivalent is:

int main(int a, int b, int c) {
    mw_init_1();
    mw_init_2(0x13);
    mw_init_3();
    (*extracted_payload)(a, b, c, 0x78, 0x16, 0x12);
    return 0xc;
}

where extracted_payload is an instruction stream containing the decrypted second stage of the malware.

mw_init_1 through mw_init_3 do some ugly bit mixing that I don’t feel like reversing, so let’s try dumping it the dumb way, cause I ain’t reversing THAT:

Nasty decryption code I don't care to reverse

The decryption code, yuck!

Fine, I’ll bring up the VM and we’ll dump it by hand. If you would like to follow along at home past this point, I highly recommend you set up a Virtual Machine for this.
Full disclosure: I no longer use the linked VM setup and do everything on a Linux laptop with Gnome Boxes, which just works™.

Dynamic Analysis

I didn’t have any issues with anti-analysis measures in this sample, so maybe that’s why I could just dump the second stage with x64dbg:

Stage 2 entry

Entry point to stage 2

Stage 2

Sorry, but I don’t have any interesting analysis for stage 2 of this sample. It looks like manually unrolled encryption code. It’s probably machine-generated so there’s no point in reversing it. I just decided to watch for what it unpacks and executes, which is stage 3.


After all this I discovered that UnpacMe could have done everything up to this point automagically. But hey – this is about the learning, and I prefer open source software when possible. The unpacked stage 3 is the b18ed7...a23cd5 “unpacked child” in UnpacMe.

Stage 3

For stage 3 things get interesting. The payload (the actual malware) is deployed via hollow process injection. There is no per se Windows executable to dump, so I manually dumped the machine instructions in good ol' x64dbg. The code for the malicious payload is its final state, but will first be injected into a benign-looking system process. Ergo, the payload is a separate “stage”.

To deploy the payload, stage 3 creates a suspended svchost.exe process. After that the malicious code then maps a new section (i.e. address mapping) to the suspended process' address space. The loader then copies the final payload to suspended process' memory space.

To have the process execute the malicious code instead of its legitimate code, its PEB is modified to point to the malicious code. This is done with APIs such as ZwMapViewOfSection, ZwUnmapViewOfSection, ZwReadVirtualMemory, and friends.

After everything is set up, the payload is executed within svchost.exe with ZwResumeThread. After resuming the hollowed-out process, Stage 3 exits:

Stage 3 jumps to the payload

End of stage 3

The Payload

The payload uses a few methods to avoid detection, none of which hamper our ability to analyze the code. For example, minimal effort is made to hide the C2 domains by constructing strings on the stack, byte-by-byte:

Stack string example

C2 stack strings

In addition, the sample uses dynamic import resolution with GetProcAddress to resolve the Win32 library calls such as ShellExecuteA and URLDownloadToFile. Nothing special there, just standard obfuscation/anti-detection.

Then the malware bit of the software… finally. The core loop of the “bad” program is straightforward. There are only 4 commands the client malware understands, one of which is duplicated. The commands are idle, run executable, update executable (plus a duplicate), and uninfect the computer. It’s simple, but enough to cause harm to a computer.

The malware polls the C2 server over HTTP using InternetReadFile. At the risk of oversimplifying things, the pseudo-code is something like:

infected = is_infected();

while (true) {
    sleep_minutes(2);
    n_bytes = fetch_c2_command(user_agent_str, buffer);
    got_command = false;

    if (n_bytes >= 4) {
        got_command = true;
        command = first_four_bytes(buffer);

        if (command == "idl=") {
            idle();
        } else if (command == "upd=" || command == "rrm=") {
            // The "rrm=" command has the same code,
            // but is put here for brevity
            path = exclude_first_four_bytes(buffer);
            download_and_exec_file(path);
            delete_current_file();
        } else if (command == "run=") {
            path = exclude_first_four_bytes(buffer);
            download_and_exec_file(path);
        } else if (command == "rem=") {
            delete_current_file();
            remove_malware();
        } else {
            got_command = false;
        }
    }
    
    if (got_command) {
        if (!infected) {
            infect_computer();
            installed = true;
        }
    }
}

To infect a computer, the malware places a urlmon entry in the registry at HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Run, with an exe dropped at %APPDATA%\urlmon.exe. This gives it persistence across boots.

Takeaways

The malware here was fairly simple, but it was fun to analyze. Here are some takeaways:

  • Reverse engineering stuff is fun!
  • Don’t open .exes with a PDF icon, grandpa.
  • The obligatory IOCs:
    • urlmon found within HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Run
    • %APPDATA\urlmon.exe
    • hxxp://infopepsigoood[.]ru/kleo/index.php?r=gate&id=[0-9a-zA-Z]*&group=[0-9a-zA-Z]*(&.*)?\
  • Shout-out to Open Analysis for their tools and great videos.