NPM package targeting crypto wallets uses new language to evade detection
new-malware-targets-exodus-crypto-wallet-banner

I’ve identified an NPM package deploying a new malware strain targeting the Exodus Wallet application.  While this attack lacked finesse, it’s interesting because it was written in a new language to evade detection.

I thought it was worth a write-up in my blog as it is an example of the efforts that malicious actors make to deploy software supply chain attacks and bypass EDR and package scanning.

Let’s dig in!

react-scripts-win-list-files

Introducing “react-scripts-win”

The package named “react-scripts-win” was published by the “reactstickerz” NPM user on January 22nd, 2025.   The package includes 4 files in total, and one of them is a Windows x64 binary named “node_runtime.exe”.  That .exe file is 2.2MB in size.

 

When this package was scanned by GitHax it identified it as malicious.  By the way, if you want to check out GitHax, you can at https://githax.com

react-win-scripts-malicious-npm-package

When analyzing a NPM package you always want to start your analysis with the package.json file.  For one thing it will tell us if the payload is leveraging a pre- or post-install script.

react-scripts-win-package-json

Nothing malicious in the package.json file, and we can see that the react-scripts-win script is not leveraging an install script to immediately install the payload upon installation.

The “main” key on line 5 tells us the entry-point for this package which is the index.js file.  Let’s take a look at this file now.

react-scripts-win-index-js

The payload isn’t hidden

Hmm, what do we have here?  The index.js file is adding a file to the Windows start menu so that the node_runtime.exe gets started at boot time.

Okay, now that we know that this package is adding a Windows binary to the start menu, we know this package is probably dodgy. 

Let’s zoom out and take a look at the package a bit more.

malicious-npm-package-react-scripts-win-nine-versions

The author iterated quickly

This package had at least 9 different versions:  1.0.0, 1.0.1, 1.0.2, 1.0.5, 1.1.1, 1.5.0, 1.8.0, 1.9.0 and 2.0.0.

I analyzed all nine versions, and they are each different. There are two distinct malware payloads spread across these nine versions.

react-scripts-win-two-payloads

What does the malware do?

First, I ran node_runtime.exe in a sandbox to see how it behaves.  Oddly, the sandbox didn’t identify any malicious behaviour.  Two other sandboxes reported the same thing, and SentinelOne didn’t alert on the file either.  When I searched for the file in Virustotal, it didn’t throw up any alerts.  Hmmm, interesting…

This file payload must be new, and something about it must be unique.

react-scripts-win-sandbox

Malware analysis workflow

I wanted to know what the node_runtime.exe file did, so I inspected the binary file directly to see if I could understand more about the payload.   When executed, the node_runtime.exe file was looking for exodus.wallet files and if it found any, it would zip them up with Powershell and send them to a Discord server.

react-scripts-win-binary-payload

The node_runtime.exe file was looking for exodus.wallet files and if it found any, it would zip them up with Powershell and send them to a Discord server.

Exodus is a popular cryptocurrency wallet.  Exodus.wallet files are the encrypted local vault that the Exodus wallet stores your keys in.  You can ecrypt the wallet file with password, but I don’t know if the wallet is encrypted by default.  There are many attacks right now focusing on exfiltration of these wallet files, and I have personally seen many dozens of malicious NPM packages targeting these files via different methods.  Makes sense, right?  If you are a bad guy looking for financial return, why not target the wallet files that store the keys to unlock crypo assets?

Seems like a pretty straight forward software supply chain attack, right?  But a major question remains:  Why isn’t this obviously malicious binary executable being detected by Virustotal, SentinelOne and the sandboxes?

Introducing the V language

As I was analyzing the binary I noticed hundreds of mentions of something called “v” or “V”, as well as “V panic:” and “v hash:”.  I saw a function named “vcalloc_noscan” which allowed me to trace these messages back to a GitHub repo https://github.com/vlang/v.  

v-programming-language

What is V language?

Turns out that V or Vlang is a relatively new programming language (started 2019) written by Alexander Medvednikov.  V is essentially a wrapper for C but it comes with its own web framework, ORM, graphics library and really easy cross-compilation.  Sounds like the perfect language to write malware in right?  C compatible, fast, comes with al the things you need for malware built right in!

Now, don’t get the wrong impression.  I’m not saying that V is bad or malicious.  It appears to be a legitimate language with hundreds of contributors.  Actually, it sounds like a pretty dope project to me.  I used to write a lot of C, and if V adds a web framework and easy database integration via an ORM, that’s impressive.

Why V for this new infostealer?

My suspicion is that the author of this malware used V to evade security tools like EDR.  V was heavily influenced by Golang and Rust, so the way it handles files, io, and network access are all different than C/C++.   The way that V will open and write to a file, or a network endpoint will look different when you examine the code.  V is relatively new, having only been created in 2019, so it’s not surprising that security tooling has limitations when analyzing V binaries.

V doesn’t use C symbols, for example, which might explain how it evaded Virustotal and SentinelOne.  Static analyzers will find it difficult to analyze the V code as they are typically written for existing languages like C/C++ and .NET.   I need to spend more time understanding this part.

I showed this malware sample to a friend who works in exploit development. They agreed that this was probably the author’s reason for choosing V but we couldn’t know for sure.  

Regardless, now that I understand more about V, I will keep an eye out for it and have already written a new detection rule for GitHax.

C2 server identified

When I compared the different versions of the package contents I noticed that the most recent version of the NPM package, 2.0.0, had one thing that the earlier versions didn’t:  It is making requests to a webserver at hxxp://45[.]90[.]12[.]203:3000.

node_runtime-exe-c2-server

When we analyzed this file’s behaviour in a custom sandbox with networking enabled, we observed the node_runtime.exe file making outbound requests to this web server.  More detailed analysis reveals that these requests appear to be a realtime keylogger sending keystrokes back to its c2 server at the above IP address.

node_runtime-exe-c2-call-home

Do we know anything about the threat actor?

The author of the react-scripts-win package is an NPM user named “reactstickerz” who used the email “vlscalmdev@gmail.com” when they created their NPM account.  There’s a user in GitHub named vlscalmdev.

github-user-vlscalmdev

That GitHub user had one public repository named nvqmwqmdw321.  That repository has one file in it, index.bat.  If you look at index.bat’s content you’ll see that it includes the same payload we saw earlier in the node_runtime.exe payload.

nvqmwqmdw321-code-repo

Connecting the dots

The contents of the index.bat file in the nvqmwqmdw321 repository mentioned the same exodus.wallet target, the same destination zip file name, and exact same Discord exfiltration webhook being used in the binary payload, so we can assume that the NPM package was created by this GitHub user.  

Tradecraft is evidence of someone learning as they go

The author was innovative in their use of the new V language, but they failed to hide their project in several other areas.

  • The author deployed public NPM packages which allowed me to find and inspect their work.  If they had just used private packages, I wouldn’t have been able to analyze the nine different versions of the package.
  • The payload code wasn’t obfuscated or hidden.  The main payload was right out in the open in the index.js file.  Once I saw that, I knew I was dealing with a malicious threat.
  • The Windows binaries node_runtime.exe and conhost.exe were relatively simplistic binary payloads.  The focus on exodus.wallets was obvious and gave away the intent.  This is probably because the author was learning the V language and how to write malware as they went along.
  • While the authors general opsec is good in the sense that the names they used don’t appear to be used anywhere else, they did left artifacts of the attack in GitHub which allowed me to connect that user to the other attack components.  
  • While the current tradecraft is poor, the author will only get better going forward, so while this first attempt wasn’t very effective, future versions will be more effective and harder to find.

Indicators of Compromise (IOCs)

Based on my research the “react-scripts-win” NPM packages has several IOCs you can look for:

NPM Packages:

react-scripts-win versions 1.0.0, 1.0.1, 1.0.2, 1.0.5, 1.1.1, 1.5.0, 1.8.0, 1.9.0 and 2.0.0.

Files:

node_runtime.exe sha256 hash:

89b9510f7dde20f436592fe45e3706a6ef3973342c8e421b2d0631cb779d6559 (npm package version 2.0.0)

6acfebee7f46e324b0b878a2c541d841c2f451b86a367a9c5e6564240c56b86c (npm package version 1.0.0)

conhost.exe sha256 hash:

7c55d664aefde46e7aa951d59b02da94a7d64566901a006b602fabeceb4314a0 (npm package version 1.5.0)

Discord exfiltration URL:

hxxps://discord[.]com/api/webhooks/1331149056533332018/fvP7osMwqZTWLzuctLKOhOgccgml1pH-tFycFIivrIurpQoJ1eC6vTBQy4tidKai1XRV

IP addresses:

45[.]90[.]12[.]203

Email addresses:

vlscalmdev@gmail.com

GitHub user:

https://github.com/vlscalmdev

 

All of these IOCs are being tracked in GitHax already, so if you want to check it out go to https://githax.com and search for the package name “react-scripts-win”.

What can you do?

You can use our indicators of compromise above to hunt for this threat in your environment if you think you might have been affected.  In the meantime, NPM has removed this package from the public repository, and some SCA tools have now marked this package as malicious.

Using SCA tools is essential but doesn’t provide coverage for malicious packages, which is ironic, right?  SCA protects you from accidental code issues that *might* be a threat, but they don’t protect you from packages that were purposefully designed to be malicious.

 

Thanks!

Several friends helped out with the research, and while I can’t name them, I just wanted to say thank you.  You know who you are.

paul-circular-githax-photo

Paul McCarty

SourceCodeRed.com Security Research & Trainer

 

https://www.linkedin.com/in/mccartypaul/

Read about Software Supply Chain Red Teaming