Fun with Minecraft infostealer malware

Introduction

One day, a friend and I were bored one night, so we decided to look for some fun Minecraft mod malware floating around on the internet. We each dug around for our own samples, and I came across this video on YouTube:

Pasted image 20250305140159.png

This just looks like a traditional cheat for gaining experience in the large Minecraft server, Hypixel. So, I downloaded the file, decompiled the Java, and spent some time reversing its functionality.

There wasn’t anything immediately outright malicious, but maybe a little bit sketchy, and this led me down a rabbit hole until I found the final payload, which was an infostealer!

Infection sequence

This malware has four primary files, although I was only able to get ahold of three of them.

  • Hikari-1.7.2.jar - Loaded and executed through Minecraft’s mod loader, downloads and executes a DLL file called HikariDLL.dll.
  • HikariDLL.dll - Upon execution, downloads and executes a .jar file called discord.jar.
  • discord.jar - The primary infostealer payload, downloads and executes a Jar file called load.jar. Has components to steal Discord tokens, browser credentials, sends screenshots of the victim machine, and more.
  • load.jar - Downloaded by discord.jar, but the file was unavailable.

With that, let’s get into the details of each!

Hikari-1.7.2.jar

MD5: c1de2f27030bca62ffbd70ace40d2052

Passing this into VirusTotal, there doesn’t seem to be anything inherently suspicious right off the bat. Pasted image 20250305190949.png

This theme continues as I started looking at the decompiled Java. There really isn’t much going on here. Just some dependencies, one class called CustomGui.class, and a text file with some Minecraft mod information called mcmod.info.

Pasted image 20250309192055.png

Looking at mcmod.info, we do get a Github link, which could come in handy later.

Pasted image 20250306164820.png

Looking at CustomGui.class, we can see a DLL file is downloaded from the endpoint hxxp://84.19.3.61:2/HikariDll.dll, written to a temporary file, and is called through System.load(), a library used to load and run Windows DLLs.

We can see that it calls a DLL export called startHikari().

Pasted image 20250306175810.png

So, after Hikari-1.7.2.jar is loaded and executed through Minecraft, it loads and executes a DLL file. Let’s look at that!

HikariDLL.dll

MD5: 3fe4976bada010959103f35ae92b53ce

Passing this into VirusTotal, we do receive a few hits, claiming this DLL is just generically malicious. Of course, false positives do exist depending on the behavior of the program, so I’ll do some manual analysis to see if anything sticks out.

Pasted image 20250305192519.png Pasted image 20250305193030.png

Static analysis

Loading this into various PE analysis tools (CFF Explorer, pestudio, etc.) for some basic static analysis, we can extract some generic information like the language this was written in, as well as some of the imports and exports of the DLL.

Here, we can see this is a C++ program.

Pasted image 20250306161739.png

Taking a look at the DLL exports, we can see that Java_com_example_CustomGui_startHikari is called, which seems to make sense given that the previous file, Hikari-1.7.2.jar, calls this export.

Pasted image 20250306160705.png

Looking at the DLL imports, there’s not much going on here, and nothing seems outright malicious or immediately suspicious.

Pasted image 20250306160832.png

Looking at the specific imports called, there were a few flagged, but whether this is malicious or suspicious depends on the context of the functionality. I did make note of the network-related ones, such as InternetReadFile, InternetOpenUrlA, and more.

Pasted image 20250319153627.png

Running strings against the DLL file, however, did show some interesting information.

We can immediately see a URL pointing to hxxp://84.19.3.61:2/discord.jar, as well as some components making up a web request (POST /apiv1/token HTTP/1.1, Content-Type: application/json, etc.) We can also see an explicit path for the DLL’s Program Database file, which does disclose a username!

Pasted image 20250305192819.png

I then immediately pulled down the discord.jar file.

Dynamic analysis

Unfortunately, I couldn’t do any dynamic analysis, as HikariDLL.dll needs to be directly loaded by Hikari-1.7.2.jar, and Hikari-1.7.2.jar needs to be directly loaded by Minecraft’s mod loader.

While I could configure an environment to properly execute this code, we have a relatively good idea of what’s going on given the limited amount of static analysis done.

discord.jar

MD5: 0da78f971d191d8f326f8f5fc0f27215

Here’s where things get fun!

Passing the MD5 hash VirusTotal revealed some interesting information. We receive much more hits than the previous two files, with results more specifically pointing to this potentially being some form of trojan malware.

Pasted image 20250305193006.png Pasted image 20250305193021.png

Looking at the decompiled Java, there’s immediately a lot more going on, as we have more external dependencies and libraries called. Looking through most of them, really the only ones that seem to matter for the trojan functionality are here:

Pasted image 20250505121901.png

Main.class

Immediately, there are two variants of a function called addonLoad(). Both of them check the public IP address of the victim host using Amazon AWS’ checkip endpoint, and further attempts to download a file called load.jar from hxxp://188.225.35.18.

If the download is successful, the program then executes a method inside load.jar called huy() on a class called dev.loveeev.Start, which likely performs other forms of exfiltration.

Unfortunately, at the time I obtained the sample, the hxxp://188.225.35.18/load.jar resource was unavailable and hasn’t come back online since.

Looking at the variants of addonLoad(), the first variant, addonLoad(String session, String TEST), uses hardcoded placeholder strings such as “THIS STALCRAFT STEAL” and “TEST”, likely for testing/debugging purposes.

Pasted image 20250316130257.png

The second variant, addonLoad(Object minecraft, String hwid, String pizza), directly interacts with a minecraft object, using reflection to extract session-related information such as the token, SSSID, username, UUID, and public IP address.

Pasted image 20250505152520.png

Lastly, both call the sendWebHook() function which formats the gathered information as JSON, and uploads it to hxxp://188.225.35.18:5000/api. This labels and formats the aforementioned token, username, UUID, and public IP address, but not the SSSID or the session.

Pasted image 20250505152609.png

Before we continue, it’s worth noting that the following files (Start.class, Data.class, MurderTasks.class) are never directly referenced within Main.class, despite being present in /org/examples. Main.class acts independently, interacting solely with the load.jar file that was downloaded. Nonetheless, the following components still reveal lots of interesting behavior, so let’s keep going!

Start.class

After looking at the remaining code pieces, Start.class is really the key program that orchestrates and handles most of the exfiltration functionality.

Looking at the main() function, the following happens:

  • Calls getDriver, which loads a jdbc.sqlite database driver
  • Calls Data.class, which obtains user browser config and credential locations (Local State, Login Data)
  • Calls MurderTasks.class to kill browser instances
  • Calls sendData to format and upload the exfiltrated data returned from Passwords.class, Huyna.class, and Autofill.class.
  • Calls ScreenshotSender.class to take a screenshot of the host and upload it

Pasted image 20250305213605.png

Before diving into the other classes, let’s take a closer look at the local functions, being getDriver and sendData.

getDriver simply attempts to load the org.sqlite.JDBC driver. jdbc:sqlite is a common database used for storing browser credentials, and is used by many web browsers. If it cannot load the driver locally, it will attempt to download it directly from the Apache Maven repository at runtime, followed by storing it in %APPDATA% in a file called sqlite-jdbc-3.43.0.0.jar.

Pasted image 20250305212612.png

Next, sendData has three hard-coded endpoints:

  • hxxp://84.19.3.61:1/apiv1/discord
  • hxxp://84.19.3.61:1/apiv1/autofills
  • hxxp://84.19.3.61:1/apiv1/passwords

With the information passed in from main(), it will format this information accordingly and call sendPostRequest to upload the information with the previously-initialized URL strings.

Pasted image 20250506125920.png

With that, let’s take a look at the immediate classes that were called, being Data.class, MurderTasks.class, Passwords.class, Huyna.class, Autofill.class, and ScreenshotSender.class!

Data.class & BrowserConfig.class

This is the first class called from Start.class, and it’s relatively simple. It defines static file paths for the Local State and Login Data files used by various Chromium-based web browsers, relative to the user’s %APPDATA% and %LOCALAPPDATA% directories. These paths are stored in a hash map called BROWSERS, with each entry tied to a specific browser.

Pasted image 20250506132845.png

The Local State file contains browser-specific configuration data such as extensions, user preferences, and encrypted keys. These encoded encryption keys can be extracted and potentially used to decrypt saved credentials.

The Login Data file stores website credentials stored in the browser. It primarily contains the the URL of the website saved, as well as the associated username and password.

For Chromium-based browsers, these passwords are typically encrypted using DPAPI (Windows Data Protection API), but decryption is theoretically possible using the key found in the Local State file, especially if done on the same host where the credentials were initially created.

For each of the valid browsers found, the full paths for the Local State and Login Data files are essentially stored in BrowserConfig.class.

Pasted image 20250506142758.png

MurderTasks.class

Like Data.class, MurderTasks.class is another relatively straightforward and simple program. When called, this will list the running processes, looking for chrome.exe, browser.exe, brave.exe, msedge.exe, opera.exe, and vivaldi.exe.

If any of the predefined browser names are found, it will invoke the Windows command line (cmd.exe /c) to execute Powershell’s taskkill command with the /IM (Image Name) and /F (Force) flags. The full command looks something like this:

cmd.exe /c taskkill /IM <browser name> /F

This is likely done to ensure the browser data files (Login Data, Local State) are not process locked or in use when the malware attempts to interact with them.

Pasted image 20250506133944.png

Passwords.class

This one is pretty neat. Given the data obtained and stored from Data.class and BrowserConfig.class, this program will attempt to extract and decrypt credentials in the Login Data database for each browser installed.

It will first loop through all of the paths defined in BrowserConfig.class and ensure these paths exist. If they do, it will call extractAndWritePasswords() with the current browser, Local State path, and Login Data path.

Pasted image 20250506143548.png

Following this, extractAndWritePasswords() will attempt to extract the encryption key out of the Local State file and then decrypt it using Windows DPAPI. After this, the program will create a copy of the Login Data database and perform SQL queries against it to extract the origin_url, username_value, and password_value.

It will ensure the password is not empty and is greater than 15 characters long, and if it is, it will call decryptPassword to perform the operations necessary to decrypt the password.

Pasted image 20250506143825.png

Looking at the actual operations used to decrypt the passwords obtained, we can see that decryptPassword utilizes AES/GSM, with some additional handling involved. Looking at Win32CryptUnprotectData, this is previously used to call the DPAPI function to decrypt the encryption key on host.

Pasted image 20250506145805.png

Huyna.class

This is another interesting one! This will attempt to find various discord tokens from different paths on the system, extract and decrypt them, followed by verifying the tokens are valid.

Looking at the DiscordTypes array, we can see that various locations relative to the user’s %APPDATA% and %LOCALAPPDATA% directories are specified, all pointing to a file called leveldb, which contains information like usernames, tokens, and more.

Pasted image 20250506155800.png

When getDiscordToken is called, the DiscordTypes paths are verified to be associated with discord (as stated by the contains("iscord"))). If a directory is found, it will read in the Local State information, obtaining the Discord key.

However, if the path exists but does not contain "iscord", it will utilize regex to find the Discord token within the .ldb file directory.

Pasted image 20250506155825.png

Then, for any Discord tokens discovered and decrypted, it will test them against Discord’s official validation endpoint (https://discord.com/api/v9/users/@me). If the token is valid, the program will then output the user’s ID as well as their username.

Pasted image 20250506163648.png

Autofill.class

This is another browser-based exfiltration program, but instead of targeting stored passwords, this one targets autofill information, such as name, addresses, and card information. It defines static file paths for the Local State and Web Data files used by various Chromium-based web browsers, relative to the user’s %APPDATA% and %LOCALAPPDATA% directories. These paths are stored in a hash map called BrowserPaths, with each entry tied to a specific browser.

We’ve seen the Local State file in the past, which contains browser-specific configuration data such as extensions, user preferences, and encrypted keys. However, this is the first time Web Data has been seen, which contains information such as autofill or form data, which may contain credit card info, addresses, and more.

Pasted image 20250506152642.png

After setting these paths, extractAutofillData() is called, which first validates the path exists. If it does, it will call extractBrowserAutofillData, which connects to the Web Data database using the jdbc:sqlite driver, and specifically queries for autofill information. If the information is valid and not empty, it will organize the autofill field name, the value stored within it, and the browser it was extracted from.

Pasted image 20250506152915.png

ScreenshotSender.class

The last of the classes called from Start.class is ScreenshotSender.class. Simply put, this calls Robot a native java.awt package that allows a program to capture screen images among other things, to take a screenshot and write it to a temporary .png file on disk.

After this, it will prepare the screenshot image for upload, sending it up to hxxp://202.181.188.121:1/apiv1/screen.

Pasted image 20250506153113.png

Conclusion

This whole thing started with a seemingly harmless, although sketchy, Minecraft mod, and ended with a full-blown infostealing malware analysis project! I definitely wasn’t expecting this chain of events to unfold, and it was a fun rabbit hole to go down.

In traditional fashion, I did attempt to send funny messages and garbage data following the syntax to each endpoint, but sadly, I was unsuccessful! Just for fun, here’s a quick list of all of the IOCs:

  • hxxp://84.19.3.61:2/HikariDll.dll
  • hxxp://84.19.3.61:2/discord.jar
  • hxxp://188.225.35.18/load.jar
  • hxxp://188.225.35.18:5000/api
  • hxxp://84.19.3.61:1/apiv1/discord
  • hxxp://84.19.3.61:1/apiv1/autofills
  • hxxp://84.19.3.61:1/apiv1/passwords
  • hxxp://202.181.188.121:1/apiv1/screen

For my first blog post analyzing malware found in the wild, this was a lot of fun! I don’t have a ton of experience with Java, but this was a great little way to get somewhat familiar with some of the characteristics of the language. :)