Deep Instinct Threat Lab observed a malicious PPSX file uploaded from Ukraine to VirusTotal at the end of 2023:
The file name suggests that it was shared via the Signal application; however, this doesn’t necessarily mean the file was initially sent to the victim via the application.
The PPSX (PowerPoint Slideshow) file appears to be an old instruction manual of the US Army for mine clearing blades (MCB) for tanks.
The PPSX file includes a remote relationship to an external OLE object:
The use of the “script:” prefix before the https URL indicates the use of CVE-2017-8570, which is a bypass to the more known CVE-2017-0199.
The remote script, which is named "widget_iframe.617766616773726468746672726a6834.html,” was hosted at the domain “weavesilk[.]space,” which is protected by CloudFlare. However, during our analysis, we managed to identify the real hosting behind the domain, which is a Russian VPS provider:
The contents of the scriptlet are highly obfuscated:
After de-obfuscation:
The second stage dropper is an HTML file containing JavaScript code that would get executed via Windows cscript.exe
It’s responsible for persistency, decoding, and saving the embedded payload to disk.
This persistence technique is unusual and could cause the payload to be executed multiple times unnecessarily, though because it looks benign, the persistence would be harder to spot by incident response and could also cause a privilege escalation if a high-privilege user or process executes cmd.exe.
The sample includes a loader/packer Dynamic Link Library (DLL) named vpn.sessings that loads a Cobalt Strike Beacon into memory and awaits instructions from the C&C server
Cobalt Strike was already analyzed many times, so we won’t go into detail, but the loader contained a couple of interesting behaviors.
The sample is a DLL that executes using Regsvr32.
Most of the important logic is inside DllRegisterServer.
The exported methods strangely have names of undocumented low-level WinAPI calls (Nt, Zw, Rt), but most were empty and unused.
The loader attempts to terminate the parent process (anti-debugging), iterating over running processes and looking for itself. Then, it attempts to terminate its parent process:
When it is executed from the “Command Processor” registry key, it runs under a “Non-Existent Process,” meaning there is no parent process it could terminate. However, while debugging, it terminates together with the debugger, slowing analysis – and annoys the researcher!
The loader dynamically loads the low-level NtDelayExecution WinAPI Call, and stalls for 20 seconds without any clear functional reason.
Possible explanations could be to either slow debugging or evade sandboxes.
It may exploit the fact that preventive sandbox emulations need to deal with large volumes of malware and can only last for short periods of time (2-5 min).
Most solutions skip long sleeps/delays, but here it was executed 20 times, each for a second, possibly trying to bypass automatic skips while also delaying the execution slightly.
One-second delays look like a short amount, but because these calls are usually hooked for monitoring, each call takes longer and could exclude important indicators from emulation windows.
The loader executes CPUID using an inline ASM instruction to determine whether the malware is being executed in a virtual machine.
The CPUID x86 instruction returns a negative value if executed in a VM (the 2^31 bit is set to 1; signed values over 0x80000000 are negative).
We can see in the instruction below that it would return true if CPUID returns a positive number:
If this condition is false, it simply exits, but the interesting thing is that it performs this check in other places. This includes a check at DllEntryPoint (executed before DllRegisterServer), which assigns a global variable that can later change some of the behavior.
The dynamic link library ntdll.dll is the lowest-level Windows API call interface between User Mode and Kernel Mode.
Because of that, anti-malware vendors usually place hooks inside various calls to monitor for malicious behavior. If malware wants to avoid detection, it needs to find a way to circumvent these protections.
The idea is straightforward: a pre-loaded ntdll.dll in memory contains inline hooks inside various API calls, which the malware needs to bypass or overwrite to avoid detection.
In the screenshot below, we can see a memory mapping of ntdll.dll from disk, together with the fetching of the loaded module of ntdll.dll from the memory of the running process:
They are called:
This looks like obvious unhooking, but the rest of the code was overwhelmingly complicated, and for some reason, it didn’t unhook in our tests.
It took some time, and after digging deeper, we’ve encountered a couple of additional tricks the malware authors did to slow analysis.
I’ve divided these delaying techniques into several points:
1. Over-complicated bloat code
It’s difficult to understand the exact purpose, but we suspect it was done intentionally to waste threat analysts’ time, as we can see in the example code below:
This piece of code is called from the function responsible for unhooking ntdll.dll, and the variables lp_text_base_DiskNTDLL/MemNTDLL are LONG_PTR addresses of both DLLs in memory.
Because MemNTDLL always wants to be loaded by the operating system at the highest User Space module address – 0x77000000 (32-bit) above – with some variation due to ASLR, the condition lp_text_base_DiskNTDLL > lp_text_base_MemNTDLL would never be true.
At least from this context.
This function also gets called from other locations full of bloat code, not related to unhooking.
2. Hidden additional VM check (as mentioned above)
If the check is false, it does not unhook and instead diverts the execution flow into ... can you guess?
More bloat code:
Malware developers are known to create dummy behavior to slow down analysis.
It’s an effective trick, and knowing that, it’s better to move on without diving into overly complicated code with no clear purpose, especially if it doesn’t get executed under normal conditions.
3. Structs with manual offsets
The authors also used arbitrary offsets while manually loading various PE structures to make the reconstruction more difficult:
After reconstruction, it’s easier to see that it looks for the .text section (containing all the function implementations), changes the permissions to PAGE_EXECUTE_READWRITE, and then sends it to be overwritten with the original ntdll.dll:IOCs:
Uses a standard decryption routine
This is meant to hide the payload, avoid storing the file on disk (file-less), bypass remote injection heuristics, and, as always, complicate the analysis.
The procedure is as follows:
The Cobalt Strike config contains a public key for asymmetric key exchange for encrypted communications with the C&C.
The licence_id : 0 indicates that this is a cracked version of Cobalt Strike.
The Cobalt Beacon has a detailed config with the C&C address/domain name, URI, public key, and even the process that it would inject into (dllhost.exe).
It awaits instruction from the C&C server, located at petapixel[.]fun (disguised as a popular photography site), also hidden behind Cloudflare, and registered in an EU country with GDPR masking, making it more difficult to investigate.
It did mention it was registered in Warsaw, Poland.
The Deep Instinct Threat Lab could not attribute these attacks to any known threat actor or exclude the possibility that this was part of a red team exercise.
The evidence shows that this sample was uploaded from Ukraine, the second stage (weavesilk[.]space) was hosted and registered under a Russian VPS provider, and the Cobalt beacon C&C (petapixel[.]fun) was registered in Warsaw, Poland.
The binary (vpn.sessings) contains a custom loader/packer for the Cobalt Strike Beacon with various techniques to slow analysis and bypass cybersecurity solutions. Most of the techniques are not new but could be unique enough to be used as a fingerprint.
The Cobalt Strike Beacon by itself is a professional pen-testing tool designed for evaluating computer security by red teams, but this is the leaked cracked version, so we can’t trace it to any legitimate user.
Cobalt is an advanced tool with a wide range of capabilities, such as stealing sensitive data, elevating privileges, propagating to other computers in the network, downloading tools, and more. Without additional clues, it’s hard to understand the exact purpose of the attack.
The lure contained military-related content, suggesting it was targeting military personnel. But the domain names weavesilk[.]space and petapixel[.]fun are disguised as an obscure generative art site (http://weavesilk.com) and a popular photography site (https://petapixel.com). These are unrelated, and it’s a bit puzzling why an attacker would use these specifically to fool military personnel.
As of the day of discovery, the loader was undetectable by most engines, while Deep Instinct prevented it on day 0.
Tactic | Technique | Description | Observable |
---|---|---|---|
Initial Access | T1566 | Phishing | PowerPoint signal-2023-12-20-160512.ppsx containing RELS exploit |
Execution | T1059.007 | Command and Scripting Interpreter: JavaScript | widget_iframe.617766616773726468746672726a6834.html containing obfuscated JavaScript |
Persistence | T1547.001 | Registry Run Keys | HKCU\Software\Microsoft\Windows\CurrentVirsion\Run: cmd /Q /C whoami |
Persistence | Event-Triggered Execution | HKCU\Software\Microsoft\Command Processor\AutoRun: start regsvr32 /s C:\<path>\vpn.sessings New sub-technique submitted to MITRE | |
Defensive Evasion | T1218.010 | System Binary Proxy Execution: Regsvr32 | start regsvr32 /s C:\<path>\vpn.sessings |
Defensive Evasion | T1055 | Process Injection | Performed a self-injection after unpacking the Cobalt Strike Beacon, using the classic CreateRemoteThread method |
Defensive Evasion | T1027.002 | Software Packing | Unpacked the Cobalt Beacon using CryptDecrypt |
Discovery | T1057 | Process Discovery | Iterated the running processes to terminate its parent for anti-debugging |
Defensive Evasion | T1497 | Virtualization/Sandbox Evasion | Performed NtDelayExecution to stall execution, possibly to evade automatic sandbox emulation by exploiting time constraints |
Command and Control | T1573 | Encrypted Channel | C&C communication using HTTPS on port 443 |
weavesilk[.]space
109.107.178[.]241
petapixel[.]fun
SHA256 | Description |
---|---|
b0b762106c22e44f7acaa3177baabd64ea28990d16672e1f902b53f49b2027c4 | signal-2023-12-20-160512.ppsx |
0bc0e9410f4a9703ff0b5af7ec9383a1cc929572ade09fbd2c69ed2ae1486939 | widget_iframe.617766616773726468746672726a6834.html |
976f57442452cd54cada011c565ada0c01f5b1460e31ee6cea330d210d3e8f50 | vpn.sessings (cobalt strike loader DLL) |