Cryptojacking malware—a type of malware that tries to steal cryptocurrencies from users on infected machines. Curiously, this kind of malware isn’t nearly as famous as ransomware or even infostealer malware. We found this kind of strange since cryptocurrencies have been a popular subject in recent years, so you would think that malware that dabbles in the field would make some more headlines.
In this blog post, we’ll explore a previously unknown cryptojacking malware—which we called MassJacker—and in doing so, we’ll try to offer some insight into why such malware might not be as widespread and well-known. We’ll provide key insights on our investigation—like discovering over 750,000 unique addresses used by MassJacker—with one wallet in particular that was worth over $300,000!
Overview
For those less familiar with it, cryptojacking works by replacing the addresses of crypto wallets copied by the user with ones belonging to the attacker in the clipboard. By doing so, the attacker attempts to trick the victim into transferring money to the attacker’s address instead of the intended target.
MassJacker is heavily protected—and there is far too much to cover in a lot of detail in a blog post. So, we will briefly share the main steps and then dive deeper into some of the steps in later sections.
The infection chain begins at a site called pesktop[.]com. This site, which presents itself as a site to get pirated software, also tries to get people to download all sorts of malware. In MassJacker’s case, the malware executes a cmd script followed by a PowerShell script that downloads three more executables (see Figure 1).
We quickly identified one of the executables as Amadey, a popular botnet that’s been around for several years. The other two were dotnet executables, one 32-bit and one 64-bit. It seems they’re the same malware, just compiled for different architectures. Most of the blog will focus on the dynamic link libraries (DLLs) used by the 32-bit executable, which we’ll call PackerE and the steps that follow PackerE.
PackerE downloads an encrypted DLL, which we’ll call PackerD1. PackerD1 has five resources, each used to implement a different anti-analysis technique. Following all these techniques, PackerD1 loads a second DLL, PackerD2. PackerD2 also has several embedded resources. The most interesting resource contains, among other things, the MassJacker payload. PackerD2 then uses process injection to inject MassJacker into a process called InstalUtil.exe.
Figure 1 Infection Chain Diagram (Icons Taken From https://www.flaticon.com/ and https://iconduck.com)
JIT Hooking Techniques: Possible MassLogger Connection
PackerD1—First Resource: JIT Hooking
As we mentioned in the overview, the remainder of the blog will focus on the DLLs used by PackerE, starting with PackerD1. PackerD1 has five resources, each used for a different anti-analysis technique. The first of these resources contains relevant content for a technique called JIT Hooking.
Figure 2 StopMapper Function Before (Left) And After (Right) JITHooking
A lot of content has already been written on JIT Hooking, so we won’t go into too much detail about the technique. In short, JIT Hooking is a dotnet technique that replaces some functions with others during runtime by hooking the JIT compiler’s compileMethod (see Figures 2 and 3). This is done to make static analysis of the malware more difficult.
The data contained in the first resource used by the malware includes the replacement code and sizes required for the JIT Hooking technique. This data is parsed, and the malware hooks the compileMethod. Once hooked, PackerD1 moves to the next resource.
Figure 3 JIT Compilation and Hooking
PackerD1—Second Resource: Metadata Token Mapping
The second resource used by PackerD1 contains a list of pairs of metadata tokens. A metadata token is an identifier used by dotnet files to identify various types of objects. In the case of this malware, each pair has one token representing a field and another representing a function. Later in the code, the list is used to map fields to functions to create a form of control flow obfuscation.
These mappings are done in classes like ObserverProducer (see Figure 4). Rather than directly call the targeted functions, the PackerD1 creates an instance of the class. The class constructor then calls CheckAnnotation, which maps the class field (LoginException in Figure 4) to the target function. Finally, the class’s Invoke function is called, which invokes the mapped function.
Figure 4 Example of a Class That Uses Check Annotation to Map to a Specific Function
PackerD1: Connection to MassLogger
The first techniques used by PackerD1 suggest a connection to another malware called MassLogger. Several years ago, both CERT-AGID and Mandiant published information about MassLogger, whose packer seemed suspiciously similar to that of MassJacker. MassLogger also used JIT Hooking followed by a mapping using metadata tokens. In addition, much of the code shared by CERT-AGID and Mandiant seemed nearly identical to the code used by PackerD1. For example, CountAnnotation and smethod_22 are identical in all but the parameter names, as seen in Figure 5 (the bottom was upscaled with iloveimg due to the low resolution).
Figure 5 Comparison of Current Malware (Top) and Image of MassLogger from Mandiant (bottom)
We thought we were dealing with a new version of MassLogger for most of the analysis. However, when we finally finished reverse engineering, it was clear that we were dealing with something different. MassJacker was limited to cryptojacking, while MassLogger had many different capabilities. Still, these similarities are telling. We suspect the threat actors behind this malware and MassLogger are the same. As such, we decided to call this malware MassJacker.
Custom Virtual Machine and String Builder
PackerD1—Third Resource: Custom Virtual Machine
Returning to the infection chain, after investigating the first two resources in PackerD1, we continued down the rabbit hole to the third resource. This time, the resource contained “code” for a custom virtual machine (VM) implemented by the malware. Two scripts were stored as arrays (the beginning of the first script can be seen in Figure 6).
Figure 6 Start of instructions for Custom VM
The VM used a list that served as a stack and two main functions, DestroyBroadcaster and DefineBroadcaster. DestroyBroadcaster finds the next instruction to be executed in a loop until the end of the script. For each instruction found, DestroyBroadcaster calls DefineBroadcaster with two arguments, the next instruction and the stack (example in Figure 7). DefineBroadcaster reads whatever instruction it was given and executes it depending on the type of instruction.
Many of the instructions are equivalent to more familiar assembly instructions. For example, instructions of the form {163Hnumber} are equivalent to the instruction: “push number,” which pushes a number onto the stack. Other instructions in the VM, like those of the form {75HMetadataToken}, were far less “assembly-like.” This particular instruction was used to find a field based on the metadata token in the argument and assign it the top value on the stack.
Figure 7 State of the VM at Some Point While Executing the First Script
The first script is used solely for control flow obfuscation. It calculates a bunch of values and assigns them to various variables. Later in the code, if statements depend on those values, they will completely change the behavior of a function depending on what value was assigned.
The second script executed by the VM turned out to be the code used to deobfuscate the fourth resource from PackerD1.
PackerD1—Fourth Resource: String Builder
Thankfully, once deobfuscated, the fourth resource wasn’t too hard to understand. The resource contains many human-readable strings separated by single characters that are often not human-readable (Figure 8). Unsurprisingly, the non-readable characters are the length of the readable strings. Later in the code, PackerD1 reads the length followed by the string itself, using the length to know how much more to read after.
Figure 8 Some of the Strings from the Fourth Resource
Shortly after deobfuscating the fourth resource, the malware uses the strings to find the name of the fifth and last resource belonging to PackerD1. It then deobfuscates that resource, which results in a second dotnet DLL, PackerD2, that is loaded and executed.
PackerD2 Analysis
PackerD2 contained many more resources, packed by a packer known as Costura. The main resource was a serialized object that needed to be deserialized. Deserialization resulted in a class saved in a variable called “_Bridge.” Much of the remainder of the code from PackerD2 checks each of the class values and performs some type of action depending on their value, so it serves as a kind of configuration for the malware.
Many different behaviors were configured, including patching ETWrite and AmsiScanBuffer. The field of most interest to us in the configuration contained a long buffer of data (IncludeBridge in Figure 9). This buffer was an executable compressed with gzip preceded by 4 bytes representing the length of the decompressed file. PackerD2 decompresses the stream and injects the resulting executable, which turned out to be the MassJacker payload, into a newly spawned instance of InstalUtil.exe.
Figure 9 Deserialized Resources Containing a Large Buffer
The MassJacker Payload
The MassJacker payload, containing the cryptojacking functionality, begins with two additional anti-analysis techniques. The first simply zeros out the section and dotnet stream names of the MassJacker in memory. The second is an anti-debugging technique where the malware creates a new thread to check for a debugger. If there isn’t one present, it simply creates another thread to do the same, effectively creating an infinite loop of debugger checks.
Besides the anti-analysis techniques, MassJacker has a configuration containing regexes for crypto addresses and Command and Control (C2) addresses to download additional files named recovery.dat and recoverysol.dat. The downloaded files are AES-encrypted lists of wallets belonging to the threat actor. The recovery.dat has the addresses of many wallet types, while the recoverysol.dat file only has Solana wallets (a type of cryptocurrency).
Figure 10 The MassJacker Configuration
Finally, MassJacker creates an event handler to run whenever the victim copies anything. The handler checks the regexes, and if it finds a match, it replaces the copied content with a wallet belonging to the threat actor from the downloaded list.
778,531 Addresses Worth Over $300,000
While investigating the wallet addresses downloaded from the C2, we discovered that the threat actors used the same encryption scheme for quite some time without changing the key. This meant we could use MassJacker to decrypt older files from previous campaigns and recover additional addresses. While the sample we looked at originally used around 50,000 wallets belonging to the threat actor, adding the wallets from older files resulted in 778,531 unique addresses!
Figure 11 MassJacker Decryption Function
In practice, when we checked, most wallets were empty—and only 423 wallets had any money in them at any point. Despite the relatively few wallets, the total amount of money those wallets held at the time of checking was around $95,300. Adding the money that was previously held in those wallets but had already been transferred out brought the total up to around $336,700.
However, the amount of money might be a bit deceiving. On the one hand, there’s a decent chance we didn’t find all the wallets, so there might be more money than we accounted for. On the other hand, for reasons we’ll explain later, we suspect that most of the money in these wallets likely didn’t come from the cryptojacker but rather from other malicious activities. Also, the values of most cryptocurrencies in USD have been pretty volatile these past few years, so the amounts are only approximately what we’ve found to be worth around the middle of August.
Notable Addresses in MassJacker’s Operations
While it doesn’t make sense to parse through the details of every wallet with money, there were a couple of wallets that were particularly interesting. For those who want to investigate further, we’ve uploaded the file names and encryption data to our GitHub, so feel free to download the files from VT and dive deeper. We especially recommend inspecting the Tron and XRP wallets. Since Tron and XRP wallets are often tagged, law enforcement entities might be able to discern an identity by inspecting wallets that received money from or transferred to well-known crypto exchanges.
Do the wallets belong to a single threat actor? (ltc1qcvt96u7ul76ha5m3rmy9ajn00avfkmsqpcfpsh)
Some of you might know that Amadey is a malware-as-a-service (MaaS), meaning many different threat actors use it. Similarly, it seems that MassLogger was—and possibly still is—a MaaS. So, it’s likely that MassJacker is also sold and used by multiple threat actors. This raises the question: Given that we’ve got the wallets from several different files, do they all belong to the same threat actor?
This is a difficult question to answer, and while we don’t have definitive proof, we suspect they do. For starters, the file names and encryption key were shared for all the wallets. We’d expect different threat actors to at least use different file names for their files.
Another reason comes from the wallet with the address ltc1qcvt96u7ul76ha5m3rmy9ajn00avfkmsqpcfpsh. This is a Litecoin wallet that isn’t on the list of wallets that we’ve found. What’s notable about it is that it received money in several transactions from many different wallets from the list we found. More specifically, it received money from many different wallets that appeared in different files from those we found and decrypted. This suggests they all belong to a single actor concentrating their profits into this single wallet. However, there are other possible explanations, so this isn’t definitive.
The Motherload (CJpe4dUcV5Knc2XZKTVsTNHm2MpmJGJNWCJdkfbNdYF5)
The vast majority of the money we found sat in a single Solana wallet with the address “CJpe4dUcV5Knc2XZKTVsTNHm2MpmJGJNWCJdkfbNdYF5”. At the time of writing, this wallet held just over 600 SOL, which was worth approximately $87,000—but this is just the tip of the iceberg. The same wallet has been active for quite some time and has sent most of the money it held elsewhere. Adding up all the money it held since the start totals to over 2075 SOL, worth over $300,000!
In addition to money, the same wallet also seems to have a history of holding non-fungible tokens (NFTs). The Gorilla Reborn and Susanoo tokens in Figure 12 are NFTs.
Figure 12 NFT-Related Transactions for the Solana Wallet CJpe4dUcV5Knc2XZKTVsTNHm2MpmJGJNWCJdkfbNdYF5 (source: screenshot from solscan.io)
Oddly, there were over 350 transactions, with payments from many different addresses going to this wallet. It seems very unlikely for a wallet used exclusively by a cryptojacker to have so many transactions. It feels unreasonably lucky that so many people would pay into a single wallet while most of the attacker’s wallets got nothing. Again, there are other possible explanations.
Some quick Googling led us to some tweets about the wallet. Unfortunately, it’s unclear from the tweets if the wallet is being used for other malicious activities.
https://t.co/OqDiNJBrG4. That’s the wallet my hacked funds have gone to btw
— 𝑨𝒄𝒉𝒓𝒂𝒇◎🥷🏻 (@Achraf_yhy) February 7, 2023
Figure 13 X Mention of CJpe4dUcV5Knc2XZKTVsTNHm2MpmJGJNWCJdkfbNdYF5 Stealing Someone’s Money
There were other wallets with relatively many transactions. Once we removed all the wallets that seem to have received money from other sources, the total goes down to around $30,000. This is an underwhelming amount of money, especially considering how much effort seems to have gone into developing malware.
Final Thoughts: MassJacker’s Cryptojacking Impact
It seems that investigating cryptojackers can lead to a treasure trove of valuable information. We’ve discovered over 750,000 addresses and over $300,000 in various cryptocurrencies belonging to threat actors. Yet still, as we pointed out at the beginning of the blog, cryptojackers tend not to get nearly as much infamy as other kinds of malware.
It’s difficult to say why cryptojackers are so poorly known. One possibility is that there simply aren’t that many of them. If they really aren’t that profitable, it makes sense that there wouldn’t be many of them.
Another possibility is that they are more challenging to identify. When using sandboxes for analysis, ransomware and infostealers stick out because of the files they access. A cryptojacker, on the other hand, will only perform malicious behavior under very specific circumstances and might go unnoticed in a sandbox.
Static analysis can detect malicious files reasonably well but often fails to identify what is malicious about the file. For example, both PackerE and PackerD1 have over 35 detections. But for all of the detections, there isn’t any indication that the final payload is a cryptojacker.
Whatever the reason, we encourage more researchers to look for and investigate these kinds of malware. As we’ve seen, even if the malware itself isn’t very profitable, it can result in the discovery of additional wallets and more valuable data, which might even lead to the identification of the threat actors.
Ari Novick is a malware researcher at CyberArk Labs.