Case Study
MetaStealer made its debut on Russian hacking forums on March 7, 2022. The stealer is said to incorporate the functionality, code, and panel of Redline Stealer. The developer claims to have improved the stub of the payload. It is priced at $150 per month, mirroring the price of Redline Stealer.
Note: Some samples of MetaStealer have been found in sandbox platforms like Triage, Joe Sandbox, Any.run and classified as Redline or “another” MetaStealer” that appears to be written in C++. You can find an example here. Additionally, SentinelOne has reported a separate MetaStealer targeting MacOS devices that is written in Golang. It’s important to note that these are not the same malware variants. To clarify, the MetaStealer I am analyzing is written in C#.
The developer of MetaStealer actively advertises crypter[.]guru crypting services for their stealer users, as can be seen in the screenshot below.
I will provide a brief overview of some of the stealer’s functionalities, but we won’t delve into extensive detail as it shares many similarities with Redline Stealer. For a more comprehensive analysis, you can refer to my Redline writeup here
Technical Analysis
The generated MetaStealer build is automatically obfuscated with Confuser Core 1.6.0. Notably, the binary description contains the text “METRO 2022 Dev,” suggesting that the malware developer may be a fan of the Metro franchise :)
I proceeded with cleaning up the sample a bit to make it more readable and reversible. We go to the entry point of the binary and notice some interesting code within class “MainFrm” and “ReadLine” methods. Within “ReadLine” method, we see a while loop that continues as long as a boolean variable flag is false. Inside this loop, it calls StringDecrypt.Read(Arguments.IP, Arguments.Key), which retrieves two arguments IP and key. The retrieved data is split into an array of strings using the “|” character as a delimiter.
The Read method takes two string parameters, b64 and stringKey. The method first checks if the b64 parameter is null, empty, or consists only of white-space characters (if (string.IsNullOrWhiteSpace(b64)). If b64 is not null or white-space, the method performs a series of operations:
- It first decodes b64 from Base64 format. The result of this decoding is a string (StringDecrypt.FromBase64(b64)).
- It then applies an XOR operation to the decoded string using stringKey as the key.
- The result of the XOR operation is then decoded again from Base64 format.
Looking at the Arguments table, we can see some interesting base64-encoded strings:
This is StringDecrypt class, where XOR decryption takes place:
For each character in input, it performs an XOR operation with the corresponding character in stringKey as shown in the Arguments table. The index for stringKey is determined by i % stringKey.Length, ensuring that if stringKey is shorter than input, it will repeat from the very beginning. The exact similar string encryption is used for Redline as well.
Upon decrypting the string in CyberChef, we get the C2 IP and the port number.
Next, we will look at method_03. The code is responsible for setting up network communication.
- It attempts to establish a network channel to a remote endpoint specified by the address argument. This involves creating a ChannelFactory with a specific binding and endpoint address.
- It then sets up credentials and bypasses certificate validation.
- Next, it adds an “Authorization” message header with a hardcoded value (token/key) that is likely for authentication purposes (for example, {xmlns=”ns1”>ead3f92ffddf3eebb6b6d82958e811a0})
- It then returns true if the connection setup is successful, false if any exception occurs
method_0 contains MSValue1, which is a call to a method on a WCF (Windows Communication Foundation) service channel and the connector object is a proxy facilitating the remote method invocation.
Next, we will reach method_2:
It calls this.connector.OnGetSettings(), which seems to be a method call to obtain some data from C2. The result is assigned to the msobject variable. OnGetSettings method is responsible for retrieving settings data and packaging it into an instance of the MSObject18 class.
Each MSValue (MSValue10, MSValue11, MSValue12 etc.) stores the configuration retrieved from C2:
- MSValue10 - stores the grabber configuration:
- MSValue11 - stores the paths to the “User Data” folder for various browsers and applications such as Steam and Battle.net to steal the sensitive information from:
- MSValue12 contains additional browser paths and Thunderbird:
- MSValue13 - stores cryptowallet information under %AppData%
- MSValue14 - stores cryptowallet extensions:
- MSValue15 - stores the environmental variables for tokens and keys:
Let’s look at the Redline sample where it stores the configuration from the sample I analyzed at the end of 2022 and MetaStealer. We can see that MetaStealer is using MSObject* instead of Entity* objects as well as MSValue* instead of Id*. MetaStealer also uses a different type of collections. Redline Stealer uses *System.Collections.Generic.IEnumerable
Next, MetaStealer proceeds with decrypting the binary ID, which is the same XOR algorithm described earlier for retrieving the IP address. Further down, I stumbled across the code that is responsible for extracting the data from the byte array and performing the string replacement. Thanks @cod3nym for pointing out that it’s part of ConfuserEx default constant encryption runtime.
Some of the retrieved strings are then getting replaced:
The stealer retrieves the memory with the WMI query SELECT * FROM Win32_OperatingSystem. Next, it retrieves the Windows version via the registry:
Interestingly enough, the stealer checks if the directory at the AppData\Local\ElevatedDiagnostics path exists. If the directory does not exist, it creates the directory. If the directory exists, it then checks if it was created more than 14 days ago (by comparing the directory’s creation time to the current time minus 14 days). If the directory is older than 14 days, it deletes and recreates it. This stealer might be trying to clean up old diagnostic reports to hide any traces of execution.
The code below is responsible for screenshot capture.
- GetVirtualDisplaySize method retrieves the size of the virtual display on a system, which encompasses all the screen area across multiple monitors
- GetImageBase method is designed to capture an image of the virtual display. First, it retrieves the virtual display size using the GetVirtualDisplaySize method. It then creates a new Bitmap object with the dimensions of the virtual display.
- ConvertToBytes method is used to convert an Image object to a byte array, presumably for storage or transmission. If the provided image is not null, it saves the image into a MemoryStream in PNG format. The contents of the memory stream are then converted to a byte array.
MetaStealer uses the WMI query SELECT * FROM Win32_DiskDrive to retrieve information (Serial number) of the physical disk drives.
The code below computes an MD5 hash based on the user’s domain name, username, and serial number retrieved from the query above. The GetHexString method is used to convert bytes into a hexadecimal representation. It processes each byte in the byte array, converting every 4 bits into a hexadecimal character and adds hyphens after every 2 characters (equivalent to every 4 hexadecimal digits) in the generated hash) and then removes them (for example 4E6B8D28B175A2BE89124A80E77753C9). The result is stored in MSValue1 within MSObject7. This will be the HWID value.
The stealer proceeds with enumerating the infected system for FileZilla (C:\Users\username\AppData\Roaming\FileZilla\recentservers.xml). Next, it enumerates AV products using the following WMI queries:
- SELECT displayName FROM AntiVirusProduct
- SELECT displayName FROM AntiSpyWareProduct
- SELECT displayName FROM FirewallProduct
The stealer then proceeds with enumerating the directories for VPN apps such as NordVPN, OpenVPN Connect, ProtonVPN within FileScannerRule class. It retrieves a list of FileInfo objects by scanning a directory specified by msobject.MSValue1 using the EnumerateFiles method. The SearchOption parameter determines whether the search is recursive (SearchOption.AllDirectories) or limited to the top directory only (SearchOption.TopDirectoryOnly).
The stealer retrieves information about running processes via the query SELECT * FROM Win32_Process Where SessionId=’“ as well as the command line for each process:
Search method is responsible for searching for files within certain directories (Windows, Program Files, Program Files (x86)). The BaseDirectory is where the search begins, for example, “C:\Users\username\AppData\Local\Battle.net”.
GetBrowser method gets the information on the installed browsers on the infected machine. 1. It attempts to access the Windows Registry to retrieve information about web browsers installed on the system. It opens a specific Registry key path under HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432Node\Clients\StartMenuInternet This key is used to store information about web browsers on 64-bit Windows systems. If the first attempt to open the key is unsuccessful, it falls back to opening a similar key path without the “WOW6432Node” part under HKEY_LOCAL_MACHINE\SOFTWARE\Clients\StartMenuInternet (32-bit Windows systems). After successfully opening the appropriate Registry key, it retrieves the names of its subkeys (which represent different web browsers) using the GetSubKeyNames() method. Within the loop of iterating through the list of browsers, it creates an instance of an object named MSObject4, which is used to store information about each web browser. The stealer opens a subkey under the current browser’s key path, which corresponds to the “shell\open\command” key, to retrieve the command line associated with launching the browser. This command line is stored in msobject.MSValue3. It then checks if msobject.MSValue3 is not null and then retrieves the file version of the browser executable using FileVersionInfo.GetVersionInfo(msobject.MSValue3).FileVersion.
The processor information is retrieved via the query SELECT * FROM Win32_Processor”* within *GetProcessors method.
The list of installed programs is retrieved within ListofPrograms method by accessing the registry key SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall.
The basic information gathered such as the timezone, the build ID, stealer name, username, Windows version, screen size, MD5 hash (based on the user’s domain name, username, and serial number), language settings are stored under results variable and the stealer configuration is stored under settings variable.
Here is the snippet of the C2 communication with MetaStealer:
Redline for comparison:
So how do you differentiate between two stealers if they are very similar? That’s right, the easiest way is probably based on traffic. The traffic for MetaStealer would slightly be different than for Redline Stealer. MetaStealer would have the indicator hxxp://tempuri.org/Contract/MSValue1 as well as MSValue1, MSValue2, etc., whereas Redline Stealer will have hxxp://tempuri.org/Entity/Id1.net as well as Id1, Id2, etc.
As for the binary, we can also look for Id, MSValue, Entity, MSObject patterns like in the screenshot below:
View of the Settings panel:
The Domain Detector settings are used to sort the logs out based on specific domains, the captured logs configured will be displayed as PDD (if the domain is found in credentials), CDD (if the domain is found in cookies) in the Logs panel as well as generated in the Logs file as DomainsDetected.txt. The Misc section allows the user to clone the certificate of the binary and file information and apply it to the stealer build as well as to increase the file size and apply VirusTotal leak monitoring (to monitor if the file is submitted to VT).
Black Lists section allows the user to blacklist countries (it’s worth noting that. compared to Redline, MetaStealer Stealer does not have an anti-CIS (Commonwealth of Independent States) feature) that prevents the stealer from running in CIS countries), IPs, HWIDs and build IDs.
Binder/Crypt section allows the user to bind/merge binaries and obfuscate them with ConfuserEx. The user then can launch the merged binary from the disk or directly in memory with process hollowing using the following APIs:
- CreateProcessInternalW
- ZwUnmapViewOfsection
- ZwaAllocateVirtualMemory
- ZwWriteVirtualMemory
- ZwGetThreadContext
- LocalFree
- ZwSetContexThread
- ZwResumeThread
- ZwClose
We can test run the Yara rule that I provided at the end of this article for MetaStealer relying specifically on strings that are unique to MetaStealer on unpac.me. After the successful scan, we see 216 matches and 138 of them are detected as “Redline”
Configuration Extractor
You can access the configuration extractor here
Yara Rule
Indicators of Compromise
Name | Indicators |
---|---|
MetaStealer Sample | 78a04c5520cd25d9728becca1f032348b2432a3a803c6fed8b68a8ed8cca426f |
MetaStealer Sample | 1ab93533bff654a20fd069d327ac4185620beb243135640c2213571c8902e325 |
MetaStealer Sample | 5f690cddc7610b8d4aeb85b82979f326373674f9f4032ee214a65758f4e479be |
MetaStealer Sample | 65f76d89860101aa45eb3913044bd6c36c0639829f863a85f79b3294c1f4d7bb |
MetaStealer Sample | c2f2293ce2805f53ec80a5f9477dbb44af1bd403132450f8ea421a742e948494 |
MetaStealer Sample | 8502a5cbc33a50d5c38aaa5d82cd2dbf69deb80d4da6c73b2eee7a8cb26c2f71 |
MetaStealer Sample | 727d823f0407659f3eb0c017e25023784a249d76c9e95a288b923abb4b2fe0dd |
MetaStealer Sample | 941cc18b46dd5240f03d438ff17f19d946a8037fbe765ae4bc35ffea280df976 |
MetaStealer Sample | 1a83b8555b2661726629b797758861727300d2ce95fe20279dec098011de1fff |
MetaStealer Sample | c90a887fc1013ea0b90522fa1f146b0b33d116763afb69ef260eb51b93cf8f46 |
MetaStealer Sample | 19034212e12ba3c5087a21641121a70f9067a5621e5d03761e91aca63d20d993 |
MetaStealer Sample | 2db8d58e51ddb3c04ff552ecc015de1297dc03a17ec7c2aed079ed476691c4aa |
MetaStealer Sample | de01e17676ce51e715c6fc116440c405ca4950392946a3aa3e19e28346239abb |
References
https://malpedia.caad.fkie.fraunhofer.de/details/win.metastealer
https://tria.ge/231027-nzmhssfg49/behavioral2
https://www.esentire.com/blog/esentire-threat-intelligence-malware-analysis-redline-stealer
https://www.unpac.me/yara/results/da81f160-887e-4284-bc17-b132c121c015