APRIL 25, 2024

Uncorking Old Wine: Zero-Day from 2017 + Cobalt Strike Loader in Unholy Alliance

Executive Summary
  • The Deep Instinct Threat Lab discovered a suspected targeted operation against Ukraine
  • The operation is using CVE-2017-8570 as the initial vector
  • The operation could not be attributed to any known threat actor
  • The operation used a custom loader for Cobalt Strike Beacon
  • Deep Instinct is detecting all stages of the attack

Campaign Overview

fig1-campaign-attack-flow.png
Figure 1: Campaign overview

Deep Instinct Threat Lab observed a malicious PPSX file uploaded from Ukraine to VirusTotal at the end of 2023:

fig2_vt_upload.png
Figure 2: VT upload information

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.

fig3_ppsx_image.png
Figure 3: PPSX content

The PPSX file includes a remote relationship to an external OLE object:

fig4_remote_relationship.png
Figure 4: Remote relationship

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:

fig5_real_ip.png
Figure 5: Real domain IP

The contents of the scriptlet are highly obfuscated:

fig6_scriplet.png
Figure 6: Obfuscated scriplet content

After de-obfuscation:

fig7-scriptlet_de-obfuscated.jse.png
Figure 7: Scriplet deobfuscated

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.

Key Points
  • Drops the payload disguised as Cisco AnyConnect VPN file under the path:
    • C:\Users\<Username>\AppData\Roaming\Cisco\AnyConnect\vpn.sessings
  • Gains persistence by modifying these registry keys:
    • HKCU\Software\Microsoft\Command Processor\AutoRun: start regsvr32 /s C:\<path>\vpn.sessings - This would execute the malware every time cmd.exe gets executed.
    • HKCU\Software\Microsoft\Windows\CurrentVirsion\Run: cmd /Q /C whoami - This is actual persistence. It achieves this by executing whoami, which triggers the loader every time the system starts.

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.

DLL Payload Analysis

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.

Loader Analysis

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.

fig8-loader_export_table.jse.png
Figure 8: Loader export table

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:

fig9-terminate_parent.jse.png
Figure 9: Terminate parent

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!

figX-annoyed-researcher.png

Stalling Execution (Possible Sandbox Evasion)

The loader dynamically loads the low-level NtDelayExecution WinAPI Call, and stalls for 20 seconds without any clear functional reason.

fig10-delay_execution.jse.png
Figure 10: Delay execution

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.

Check CPUID (Anti-VM)

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:

fig11-check_vm.jse.png
Figure 11: Checks if the malware executed inside a VM

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.

Unhooking NTDLL (Anti-AM)

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:

fig12-loading_both_dlls.jse.png
Figure 12: Loading both ntdlls

They are called:

  • MemNTDLL : for the loaded module from memory
  • DiskNTDLL : for the copy of ntdll.dll from disk

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:

fig13-nonsense_code.jse.png
Figure 13: Nonsense code

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)

fig14-DllEntryPoint_CPUID.jse.png
Figure 14: DllEntryPoint check VM

If the check is false, it does not unhook and instead diverts the execution flow into ... can you guess?

More bloat code:

fig15-unhook_and_bloat_code.jse.png
Figure 15: Unhook and 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:

fig16-arbitrary_offsets.jse.png
Figure 16: Arbitrary offsets

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:

fig17-after_reconstruction.jse.png
Figure 17: After reconstruction

Decrypts Payload (Cobalt Strike)Network

Uses a standard decryption routine

  • Hash base_data/key_seed
  • Derive key from hash
  • Decrypt:

fig18-decrypt_payload.jse.png
Figure 18: Decrypt payload (Beacon)

Self-DLL Injects Cobalt Strike

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:

  • OpenProcess
  • AllocateMemory inside it
  • WriteProcessMemory with the payload
  • CreateRemoteThread inside itself
  • WaitForSingleObject until that thread finishes

fig19-self_dll_injection.jse.png
Figure 19: Self DLL injection

Extracted Cobalt Strike Config:

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.

fig20-ExtractedConfig.png
Figure 20: Extracted config

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.

fig21-location.png
Figure 21: Registration location

Conclusion

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.

MITRE

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

IOCs

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)