
This report was written for a job interview (spoiler alert: I didn't get it) and has been sitting in my notes for half a year. windows-sandbox-init was used to create this report.
Executive Summary
I confirmed the file is malicious. It functions as a downloader designed to fetch and execute a second-stage payload on the victim's machine. The binary is a 64-bit Windows executable compiled with Visual C++ and stands out because it links the libcurl library for its network operations. The malware attempts to evade automated analysis by sleeping for extended periods, effectively timing out sandboxes before it performs any malicious activity. Once active, it secures persistence by creating a scheduled task that runs with elevated privileges every time a user logs in. The network logic is robust, utilising a list of fallback URLs and even DNS over HTTPS to locate the C2 server if direct connections fail.
Classification and Attribution
We can classify this sample as a Downloader or Loader. It does not appear to contain the final payload itself but serves as the initial foothold to retrieve it from external servers. The specific use of libcurl and the custom obfuscation techniques, such as the stack-based string construction and the specific sleep loops, suggest it is likely a custom tool rather than a generic commodity malware found in mass campaigns. While I haven't linked it to a specific known threat actor based on these artifacts alone, the techniques imply a moderate level of sophistication intended to bypass standard perimeter defenses.
Prevention and Detection
Defenders can detect this threat by monitoring for specific host and network behaviors. The creation of a scheduled task requesting highest privileges is a significant indicator. On the network side, the traffic pattern involving a HEAD request followed by a hardcoded sleep interval and then a GET request provides a strong signature for intrusion detection systems. Blocking the identified C2 domains and restricting DNS over HTTPS traffic to unauthorized public resolvers like Google or Cloudflare would disrupt its communication channel. Additionally, endpoint protection systems should flag the creation of new executable files in protected directories like System32 by non-system processes.
Initial Assessment
File Inspection Using DIE
File Type and Imports
This file is a PE64 executable built by VC++, interestingly, it links libcurl.

Looking at the imported functions, libcurl is probably used for HTTP requests with a C2 server.

It also imports some Windows service operation functions.

Strings
Some interesting strings can be found. They probably are used for error messages, and there is a file download error message implying that there might be code using libcurl to download files, typical C2 behaviour.
http:// is found but not any URLs, meaning that URLs are constructed during run time.
Some regex patterns are found, they might be used to parse C2 responses.
And JSON related strings indicates the existence of a JSON parser, it might parse JSON responses from the C2.

More interesting strings are located, they appear to be used for runtime string construction, likely using some strfmt functions. And {}Windows\System32\backup_f64.exe is something that can't be ignored, the malware might use this path to save its downloaded file.
schtasks /create /tn "{}" /sc ONLOGON /tr "{}" /rl HIGHEST /f creates a scheduled task with highest privilieges.

The final XML string essentially asks the user to give it elevated privileges, so the malware would run with administrator privileges. This allows the task creation to succeed.

Further Investigation
Looking at the disassembly in Ghidra, and considering the strings we saw, this binary is not packed, all functions are clearly visible.
The next step is to find the main function of this malware. Considering what we saw in DIE, it is very likely to have a main function that constructs the C2 URL, and requests it, saves it to disk, create scheduled task for persistence.
It does have a main function invoked by entry point, however, it has nothing to do with the C2 logic we assumed, instead, it seems to be a winmain function.

The main function calls some other functions. c2_main_persistence_tasksch has many of the interesting strings, and complex logic which calls more functions, and it's the first real function it calls. Let's make this our starting point.
Live Analysis
Anti-Debugging
To make my life easier, I rebased Ghidra using the memory map in x64dbg. Then I set a breakpoint at c2_main_persistence_tasksch.
We want to know what it does with schtasks /create /tn "{}" /sc ONLOGON /tr "{}" /rl HIGHEST /f, so we stop at the relevant function strfmt. We are going to inspect its return values so the breakpoint will be before its ret.

However, the process freezes. Looking at the call stack, we see why.

Unsurprisingly, this malware tries to sleep to make it impossible to be analysed.

anti_dbg_sleep ensures that the sleep is executed by trap the execution in its loop, it validates actual sleep time, if not met, it will enforce the sleep. This defeats many simple sandboxes that patch Sleep call.
void anti_dbg_sleep(longlong *psleep_interval)
{
char cVar1;
ulonglong uVar2;
longlong time_now;
longlong sleep_time;
while( true ) {
get_time(&time_now);
sleep_time = *psleep_interval;
if (sleep_time == time_now) {
return;
}
cVar1 = -1;
if (time_now <= sleep_time) {
cVar1 = '\x01';
}
if (cVar1 < '\x01') break;
sleep_time = sleep_time - time_now;
if (sleep_time == 86400000000000) {
Sleep(86400000);
}
else {
/* sleep time less than 24h */
if (sleep_time < 86400000000000) {
uVar2 = sleep_time / 1000000;
if ((longlong)(uVar2 * 1000000) < sleep_time) {
uVar2 = (ulonglong)((int)uVar2 + 1);
}
Sleep((DWORD)uVar2);
}
else {
Sleep(86400000);
}
}
}
return;
}
Simply patching the function with ret solves this problem.

Scheduled Task Persistence
We can see the command string being constructed in strfmt, the final result is schtasks /create /tn \"downloader\" /sc ONLOGON /tr \"C:\\Users\\WDAGUtilityAccount\\Desktop\\downloader.exe\" /rl HIGHEST /f". So it adds itself as a task that runs at logon with admin.



Checking existing malware process
It scans System32 directory for all file names, then tries to match with ^x\d{6}\.dat$ regex pattern.

Then it skips the service query code.

The service query code essentially uses the matched file name as service name, and verify its status, if running, it will skip C2 logic and exit.

This is highly likely a mechanism to prevent running duplicated instances of the malware.
C2 Breakdown
C2 Orchestrator
The malware skips to the next function, which contains all its major code and calls many more functions, we call it c2_orchestrator_loop.
It has many lines of code, and some useful strings such as {}Windows\\System32\\backup_f64.exe, {}Windows\\System32\\czero_log, it also invokes std::basic_ofstream<>::vftable to open the "log" file.
What about backup_f64.exe? We can see start \"\" \"{}\" in the code, before that it has to download the payload for it to be started. The function responsible for that is likely the complex one right before it.

For now, we assume network_orchestrator downloads a file and save it to \\Windows\\System32\\backup_f64.exe then runs it.
In the debugger we set breakpoints at each call to observe what's going on before each function call, hopefully figuring out their purposes.
C2 URLs
And we have an interesting one, it returns a URL that we haven't seen anywhere before, it must have been constructed by that function, we call it make_url.

More interestingly, make_url has a string decrypt_data error: {}. This indicates that the URL data is encrypted then decrypted dynamically.

We don't care how it decrypts, since it already got us one URL, and there are more calls to it, let's note down all URLs.
http://unvdwx.com/un1/uhard.dat
https://raw.githubusercontent.com/rootunvbot/mydata/refs/heads/main/mainuhard.dat
https://github.com/unvdwl/dwl/raw/main/mainuhard.dat
http://rootunvdwl.com/un1/uhard.dat
4 URLs are found, this looks like a list of C2 URLs.
Before network_orchestrator, the list is accessible from the stack.

network_orchestrator will loop through the list trying to connect to its C2. In fact, look at its function parameters as it gets executed for the first time.

CURL HTTP Worker and C2 Protocol
network_orchestrator initialises libcurl handle, then one function uses the handle to do its stuff, we call it c2_http_worker.

Let's run it through, and we get a string returned.

In FakeNet, it shows a HEAD request.

In Ghidra we see it tries to match some string values.

Unsurprisingly, it's trying to validate its C2 server by checking the HTTP response headers.
It looks for application/octet-stream in returned HTTP headers, in this case not present.
Another line of code iVar4 = memcmp(pcVar14,"text/plain; charset=utf-8",0x19); looks for a text based response.

Trigger the C2 Logic to Run the Payload
Let's write a FakeNet rule to satisfy the malware and see what it will do with the C2 response.
def HandleHttp(req, method, post_data=None):
req.send_response(200)
req.send_header('Content-Type', 'application/octet-stream')
req.end_headers()
# Read a valid EXE and send it as the body.
try:
with open('defaultFiles/FakeNetMini.exe', 'rb') as f:
req.wfile.write(f.read())
except:
# Fallback dummy binary (Will save but likely crash/do nothing)
req.wfile.write(b"MZ" + b"\x90" * 1024)
This script tries to trigger its text based workflow, FakeNetMini.exe is supposed to be spawned if our hypothesis is correct.
As shown in the screenshots, the malware is tricked into downloading our dummy executable and executing it.
It validates Content-Type.

And it tries to write the file to the pre-defined path C:\\Windows\\System32\\backup_f64.exe.

Constructs the path string.

And CreateProcessW, run the downloaded executable in System32 directory.

The executable is executed.

Finally it creates a file czero_log in the same System32 directory.

Text-based C2 Response
From the test, this malware saves whatever it reads from C2 to backup_f64.exe and runs it, regardless of Content-Type.
def HandleHttp(req, method, post_data=None):
req.send_response(200)
req.send_header('Content-Type', 'text/plain; charset=utf-8')
response_body = "Answer: cmd.exe"
req.send_header('Content-Length', len(response_body))
req.end_headers()
req.wfile.write(response_body.encode('utf-8'))
After the malware finishes its lifecycle, it saves and runs this (text) file as an executable.

Fallback Resilience
What if the malware can't reach its C2?

As anti_dbg_sleep functions is called every time before anything important happens, we can just set a breakpoint there and observe the registers and stack.
We can observe all the hotspots in this malware by stopping at its supposedly anti-debugging function since it's so eager to prevent us from analysing whatever that's around that function.
And a DNS-over-HTTPS server address is constructed, it's not hard to guess its use.

Follow the call stack, the DoH function is found, we call it resolve_via_doh.

As we can observe from later on, it tries to resolve all C2 names using multiple DoH servers.
https://dns.google/resolve?name=
https://dns.nextdns.io/resolve?name=
https://doh.dns.sb/dns-query?name=
Notably, it also tries to resolve a backup name https://dns.google/resolve?name=rootunvdwl.com with the same DoH logic.

Comments
comments powered by Disqus