Meduza Stealer … Yes, you read it right, I did not misspelled it, is a new stealer that appeared on Russian-speaking forums at the beginning of June 2023. The stealer is written in C++ and is approximately 600KB in size. The DLL dependencies are statically linked to the binary, which reduces the detection. It’s also worth noting that the collected logs are not stored on the disk.
The stealer collects the data from 100 browsers which includes Chromium and Gecko browsers.
Other than browsers and cryptowallets, the stealer also collects sensitive information from password managers, Discord clients (Discord, DiscordCanary, DiscordPTB, Lightcord, DiscordDevelopment), and Telegram clients (Kotatogram, Telegram desktop).
With the new update of the stealer (version 1.3), the panel functionality has changed which allows the users to configure Telegram bot to receive the logs, the FileGrabber functionality was also added with the new update. The stealer also has the file size pumper feature that increases the file size to avoid sandbox and AV analysis; the feature is mostly deployed in all common stealers now, such as Vidar, WhiteSnake Stealer, and Aurora Stealer (RIP).
The stealer is priced at:
1 month - 199$
3 months - 399$
Meduza Stealer does not work in CIS (Commonwealth of Independent States) countries.
P.S: if anyone has the newest version of the stealer, please reach out to me ;)
An example of the received logs is shown below.
Technical Analysis
Logs are decrypted on the server side. Below is the snippet of master password decryption on Mozilla and other Gecko browsers. Taking, for example, the get key function. The code first checks if key4.db exists. This is the key database used by Firefox versions 58.0.2 and above. If key4.db exists, it opens an SQLite connection to the file and performs SQL queries to fetch the globalSalt and item2 data, which are used in decrypting the master key. It then checks if the decrypted text from item2 is equal to b’password-check\x02\x02’, a hardcoded string used by Firefox to verify the master password. If the master password is correct, it continues to the next step. Otherwise, it returns None, None, indicating a failure to retrieve the key and the algorithm. The function then queries the database to fetch a11 and a102. a11 is the encrypted master key, and a102 should match the constant CKA_ID. If a102 does not match CKA_ID, it logs a warning and returns None, None. It then decrypts a11 (the encrypted master key) using the decryptPBE function and the globalSalt. The first 24 bytes of the decrypted text are the key used to decrypt the login data. If key4.db does not exist, it checks for the existence of key3.db, which is the older key database used by Firefox. If key3.db exists, it reads the key data from the file and extracts the decryption key using the function extractSecretKey. It also hardcodes the cryptographic algorithm used (‘1.2.840.113549.1.12.5.1.3’, an OBJECTIDENTIFIER, is the identifier for the Triple DES encryption algorithm in CBC mode). If neither key4.db nor key3.db exists in the directory, it logs an error and returns None, None.
Below is the snippet of how the logs are parsed and sent to Telegram Bot. The logs are compressed with 7z.
The code below is responsible for adding tokens and validating their integrity, ensuring their authenticity before interacting with the main server. It performs validations on the received data, such as checking the timestamp and verifying the integrity of the data. The code checks the provided timestamp against the current UTC timestamp to ensure it is within an acceptable range. If the timestamp is invalid, an error response is returned. If the validations pass, the code encrypts the token and sends a request to the main server (hxxp://89.185.85[.]245) with the encrypted token and other necessary information. The code uses the HashGenerator class and the SHA-512 hash algorithm (sha512) to generate a hash of the concatenated values of token and data.utc_timestamp. It then compares this generated hash with the provided data.sign. If the hashes do not match, an error response is returned, indicating that the input data cannot be validated. The response from the server is processed, and if the authentication is successful (based on the success flag in the response), the received token is stored in the database for further use. A similar operation is performed in the payload. The payload is sent to a remote server as part of an HTTP request. The server will use the provided sign value to validate the integrity of the data by performing the same hash calculation on its end, taking the generated hash value for panel_hash obtained from the registry key into consideration.
As mentioned before, the panel handles the parsing and decryption of the collected data. You can see how it parses the data extracted from Chromium browsers using SQL queries in a pseudocode below.
Interestingly enough, we can also see the path of the Meduza Stealer’s source code:
C:\Users\79026\source\repos\MedusaServer\Src\Core\Parser\Chromium.cpp
Meduza Stealer performs panel hash verification as a part of the panel authentication/registration process. It queries the hash value assigned to PanelHash under Computer\HKEY_CURRENT_USER\SOFTWARE\Medusa.
Below is the mention of the log folder creation and builder output to notify that the main socket is listening on port 15666. Please note that the port is static and cannot be changed at this time.
Have you noticed that there is a mention of AuroraStealer.cpp? Also, if you compare the logs for Aurora and Meduza stealers. I wrote a blog on Aurora Stealer if you want to check it out here. I am not aware of any Aurora Stealer source code leaks so far. But if you know of any, I would love to hear about it.
Moreover, there is also a slight overlap in Telegram logs layout.
The code below is responsible for creating folders for gathered logs that are then archived.
In the code snippet below, you can see that the pointers to the vftables (virtual function tables) of classes, such as GeckoParser, SteamDecoder, TelegramParser, DiscordParser, and SystemParser are being assigned. These vftables act as a “lookup table” for the corresponding objects’ virtual functions. When a virtual function is invoked on an object, the stealer will refer to the appropriate vftable based on the object’s type at runtime to determine the specific implementation of the function to execute, for example, parsing the system information collected.
The stealer uses vpxor and pxor instructions to perform Vector Packed Bitwise XOR and Packed XOR operations on strings. The xor instruction in x86 assembly language performs a bitwise XOR operation between two operands, which can be registers or memory locations. It operates on single data elements rather than vectorized data. On the other hand, vpxor and pxor instructions are specifically designed for SIMD operations (Single instruction, multiple data), where multiple data elements are processed simultaneously in parallel. These instructions allow for parallel execution of XOR operations on packed data and can significantly improve performance in scenarios that involve processing large amounts of data in parallel.
The stealer retrieves the information about the native system and version information using RtlGetVersion and GetNativeSystemInfo functions accordingly and then parses the retrieved information based on the following decrypted strings:
Unknown Edition
Web Server (core installation)
Standard Edition (core installation)
Microsoft Hyper-V Server
Windows 10 IoT Core
Windows IoT Enterprise
Windows Home Server
Windows Storage Server
Standard Edition
Small Business Server Premium Edition
Small Business Server
Server Enterprise (core installation)
Enterprise Evaluation
Server Enterprise
Server Standard (core installation)
Datacenter Edition (core installation)
Datacenter Edition
Server Hyper Core V
Business Edition
Windows Essential Server Solution Management
Windows Essential Server Solution Additional
Professional Education
Meduza Stealer reaches out to https://api.ipify.org to determine the public IP of the infected machine.
The code below retrieves and processes geographic information based on the user’s location and then appends the result to “geo” tag.
The time zone information is retrieved via accessing the registry key SYSTEM\CurrentControlSet\Control\TimeZoneInformation and calling the function TimeZoneKeyName.
Telegram presence on the host is checked via the registry key SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall{53F49750-6209-4FBF-9CA8-7A333C87D1ED}_is1, specifically the InstallLocation value.
C2 Communication
C2 communication is super similar to Aurora Stealer. It is base64-encoded and parsed in a JSON format. As mentioned before, the stealer communicates with the server over the default port 15666.
Summary
Meduza Stealer developers also offer malware development services based on C/C++, Java, JavaScript/TypeScript, Kotlin (JVM), and Python programming languages. (No mention of GoLang? :) ). We might never find out the truth, but it is highly likely that Aurora Stealer developers are also behind Meduza Stealer.
According to Abaddon, who specializes in providing services similar to the Eye of God (one of the Russian Internet’s main data-leak hubs), the Botnet project was the reason Aurora left the market unexpectedly and taking its servers down; it failed to meet users’ expectations and delivered many promises for the product that they could not handle. It is worth mentioning that Aurora priced the botnet at 700$ for a month and 3000$ for lifetime access.
To summarize this blog, I wrote an IDAPython script to decrypt the strings for 32-bit samples of Meduza Stealers.
You can access the script on my GitHub page
Out of curiosity, I tried to pivot other samples based on the developer’s path and stumbled upon HydraClipper (MD5: add6ae21d25ffe8d312dd10ba98df778), which is apparently a clipper that is likely written by the same developer.
IDAPython string decryption script
Decrypted strings
Meduza Stealer Configuration Extractor
I was also inspired by @herrcore research with Unicorn Engine implementation and wrote the configuration extractor that grabs the C2 and build name on most samples. The extractor was written using Unicorn Engine and Python. It was my first time messing with Unicorn Engine, so any feedback is welcome.
You can grab the configuration from my GitHub page as well.