141 npm Packages Abuse Registry as Adware Hosting

SafeDep Team
6 min read

Table of Contents

A single npm account, terminal3airport ([email protected]), published 141 packages between May 7 and May 27, 2026. Every package contains the same payload: a web proxy unblocker built on the Scramjet framework, disguised as a tutoring website, monetized through popunder ads and external tracking scripts. The packages carry no install hooks and no credential stealers. The attack is npm registry abuse: using the registry as free, disposable CDN infrastructure for adware distribution.

TL;DR

Impact:

  • Users who visit the hosted pages are subjected to popunder ads opening hxxps://abdct[.]com/ on first interaction
  • External scripts loaded from cdn[.]21baseballacademy[.]com and woofbeginner[.]com execute in the user’s browser
  • All activity is tracked via Google Analytics G-0VL3ZSBXDH
  • The web proxy bypasses network content filters (school/corporate firewalls), routing traffic through Scramjet service workers
  • Discord OAuth handles user authentication, with .well-known/discord verification embedded in packages

Indicators of Compromise (IoC):

  • npm maintainer: terminal3airport / [email protected]
  • Domains: 21baseballacademy[.]com, cdn[.]21baseballacademy[.]com, abdct[.]com, woofbeginner[.]com
  • Google Analytics ID: G-0VL3ZSBXDH
  • GitHub: github[.]com/lucideproxy/svg
  • Discord verification hash: bb7ce9f4c508bc87f13889c18031ca42ad8c1bd5
  • 141 npm packages (full list below)

The Packages

terminal3airport-malicious-packages.csv
ecosystem name version published
1 npm changiairportpromax 1.1.3 2026-05-07
2 npm ilovefemboys 1.1.3 2026-05-07
3 npm whatsadmaidk 1.1.2 2026-05-26
4 npm backup1-gg 1.1.2 2026-05-26
5 npm backup2-asd 1.1.2 2026-05-26
6 npm backup3-ff 1.1.2 2026-05-26
7 npm backup4-gasp 1.1.2 2026-05-26
8 npm backup5-updated 1.1.2 2026-05-26
9 npm backupgenuine-updated 1.1.2 2026-05-26
10 npm fflc-updated 1.1.2 2026-05-26
11 npm midnightrush 1.1.2 2026-05-26
12 npm pasirianspirit 1.1.2 2026-05-26
13 npm lowkeybored 1.1.2 2026-05-26
14 npm lowkirkuenly 1.1.2 2026-05-26
15 npm kirkland 1.1.2 2026-05-26
16 npm omgyesyesyes 1.1.2 2026-05-26
17 npm captainindia 1.1.2 2026-05-26
18 npm bomboclatwallahi 1.1.2 2026-05-26
19 npm thebigyahu 1.1.2 2026-05-26
20 npm omglucidesotuff 1.1.2 2026-05-26
21 npm crazynut 1.1.2 2026-05-26
22 npm bismillahitidakimas 1.1.2 2026-05-26
23 npm howmanygreatbritain 1.1.2 2026-05-26
24 npm miguelphonk 1.1.2 2026-05-26
25 npm ratelimitsucks 1.1.2 2026-05-26
26 npm ratelimitsucks1 1.1.7 2026-05-27
27 npm ratelimitsucks2 1.1.7 2026-05-27
28 npm ratelimitsucks3 1.1.7 2026-05-27
29 npm ratelimitsucks4 1.1.7 2026-05-27
30 npm ratelimitsucks5 1.1.7 2026-05-27
31 npm ratelimitsucks6 1.1.7 2026-05-27
32 npm backupsitetuff3 1.1.7 2026-05-27
33 npm backupsitetuff6 1.1.7 2026-05-27
34 npm backupsitetuff9 1.1.7 2026-05-27
35 npm backupsitetuff10 1.1.7 2026-05-27
36 npm ratelimitsucks9 1.1.7 2026-05-27
37 npm ratelimitsucks10 1.1.7 2026-05-27
38 npm timmytuffknuckles3 1.1.7 2026-05-27
39 npm timmytuffknuckles6 1.1.7 2026-05-27
40 npm timmytuffknuckles9 1.1.7 2026-05-27
41 npm sixseven1 1.1.7 2026-05-27
42 npm sixseven2 1.1.7 2026-05-27
43 npm sixseven3 1.1.7 2026-05-27
44 npm sixseven4 1.1.7 2026-05-27
45 npm sixseven5 1.1.7 2026-05-27
46 npm sixseven6 1.1.7 2026-05-27
47 npm speed1 1.1.7 2026-05-27
48 npm speed2 1.1.7 2026-05-27
49 npm speed3 1.1.7 2026-05-27
50 npm speed4 1.1.7 2026-05-27
51 npm speed5 1.1.7 2026-05-27
52 npm imillegal1 1.1.7 2026-05-27
53 npm imillegal2 1.1.7 2026-05-27
54 npm imillegal3 1.1.7 2026-05-27
55 npm imillegal4 1.1.7 2026-05-27
56 npm imillegal5 1.1.7 2026-05-27
57 npm ishowfeet1 1.1.7 2026-05-27
58 npm ishowfeet2 1.1.7 2026-05-27
59 npm ishowfeet3 1.1.7 2026-05-27
60 npm ishowfeet4 1.1.7 2026-05-27
61 npm ishowfeet5 1.1.7 2026-05-27
62 npm ishowfeet6 1.1.7 2026-05-27
63 npm ishowfeet7 1.1.7 2026-05-27
64 npm ishowfeet8 1.1.7 2026-05-27
65 npm ishowfeet9 1.1.7 2026-05-27
66 npm ishowfeet10 1.1.7 2026-05-27
67 npm ishowfeet11 1.1.7 2026-05-27
68 npm ishowfeet12 1.1.7 2026-05-27
69 npm ishowfeet13 1.1.7 2026-05-27
70 npm ishowfeet14 1.1.7 2026-05-27
71 npm ishowfeet15 1.1.7 2026-05-27
72 npm ishowfeet16 1.1.7 2026-05-27
73 npm ishowfeet17 1.1.7 2026-05-27
74 npm ishowfeet18 1.1.7 2026-05-27
75 npm ishowfeet19 1.1.7 2026-05-27
76 npm ishowfeet20 1.1.7 2026-05-27
77 npm nottuff1 1.1.7 2026-05-27
78 npm nottuff2 1.1.7 2026-05-27
79 npm nottuff3 1.1.7 2026-05-27
80 npm nottuff4 1.1.7 2026-05-27
81 npm nottuff5 1.1.7 2026-05-27
82 npm nottuff6 1.1.7 2026-05-27
83 npm nottuff7 1.1.7 2026-05-27
84 npm nottuff8 1.1.7 2026-05-27
85 npm nottuff9 1.1.7 2026-05-27
86 npm nottuff10 1.1.7 2026-05-27
87 npm nottuff11 1.1.7 2026-05-27
88 npm nottuff12 1.1.7 2026-05-27
89 npm nottuff13 1.1.7 2026-05-27
90 npm nottuff14 1.1.7 2026-05-27
91 npm nottuff15 1.1.7 2026-05-27
92 npm nottuff16 1.1.7 2026-05-27
93 npm nottuff17 1.1.7 2026-05-27
94 npm nottuff18 1.1.7 2026-05-27
95 npm nottuff19 1.1.7 2026-05-27
96 npm nottuff20 1.1.7 2026-05-27
97 npm nottuff21 1.1.7 2026-05-27
98 npm nottuff22 1.1.7 2026-05-27
99 npm nottuff23 1.1.7 2026-05-27
100 npm nottuff24 1.1.7 2026-05-27
101 npm nottuff25 1.1.7 2026-05-27
102 npm nottuff26 1.1.7 2026-05-27
103 npm nottuff27 1.1.7 2026-05-27
104 npm nottuff28 1.1.7 2026-05-27
105 npm nottuff29 1.1.7 2026-05-27
106 npm nottuff30 1.1.7 2026-05-27
107 npm abuden1 1.1.7 2026-05-27
108 npm abuden2 1.1.7 2026-05-27
109 npm abuden3 1.1.7 2026-05-27
110 npm abuden4 1.1.7 2026-05-27
111 npm abuden5 1.1.7 2026-05-27
112 npm abuden21 1.1.7 2026-05-27
113 npm abuden22 1.1.7 2026-05-27
114 npm abuden23 1.1.7 2026-05-27
115 npm abuden24 1.1.7 2026-05-27
116 npm abuden25 1.1.7 2026-05-27
117 npm abuden26 1.1.7 2026-05-27
118 npm abuden27 1.1.7 2026-05-27
119 npm abuden28 1.1.7 2026-05-27
120 npm abuden29 1.1.7 2026-05-27
121 npm abuden210 1.1.7 2026-05-27
122 npm abuden211 1.1.7 2026-05-27
123 npm abuden212 1.1.7 2026-05-27
124 npm abuden213 1.1.7 2026-05-27
125 npm abuden214 1.1.7 2026-05-27
126 npm abuden215 1.1.7 2026-05-27
127 npm abuden216 1.1.7 2026-05-27
128 npm abuden217 1.1.7 2026-05-27
129 npm abuden218 1.1.7 2026-05-27
130 npm abuden219 1.1.7 2026-05-27
131 npm abuden220 1.1.7 2026-05-27
132 npm abuden221 1.1.7 2026-05-27
133 npm abuden222 1.1.7 2026-05-27
134 npm abuden223 1.1.7 2026-05-27
135 npm abuden224 1.1.7 2026-05-27
136 npm abuden225 1.1.7 2026-05-27
137 npm abuden226 1.1.7 2026-05-27
138 npm abuden227 1.1.7 2026-05-27
139 npm abuden228 1.1.7 2026-05-27
140 npm abuden229 1.1.7 2026-05-27
141 npm abuden230 1.1.7 2026-05-27
141 rows
| 4 columns

All 141 packages are single-version publishes. Every package except package.json (which differs only in the name field) is byte-identical. SHA-256 of the shared index.html payload across the v1.1.2 and v1.1.7 generations: the content matches.

Three publishing waves stand out:

WaveDateVersionCountNotes
1May 71.1.32Readable code, Scramjet referenced directly
2May 261.1.223Obfuscated JS, randomized filenames
3May 271.1.7116Mass-published via auto-publish.sh script

How the Spam Works: auto-publish.sh

The v1.1.7 packages include the automation script that published them, left in the tarball:

backupsitetuff6/package/auto-publish.sh
#!/bin/bash
BASE="backupsitetuff"
TOTAL=10
PARALLEL=3
publish_one() {
NAME="$1"
echo "Building $NAME"
node -e "
const fs = require('fs');
const pkg = require('./package.json');
pkg.name = '$NAME';
fs.writeFileSync('package.json', JSON.stringify(pkg, null, 2));
"
npm publish --silent > /dev/null 2>&1
echo "Published: $NAME"
}
count=1
while [ $count -le $TOTAL ]
do
while [ $(jobs -rp | wc -l | tr -d ' ') -ge $PARALLEL ]
do
sleep 0.1
done
publish_one "${BASE}${count}" &
count=$((count + 1))
done
wait
echo "Done publishing all"

The script rewrites the name field in package.json, publishes, and moves to the next name. It processes 3 packages in parallel. The attacker ran variants of this script with different BASE prefixes (nottuff, ishowfeet, abuden, sixseven, speed, imillegal, ratelimitsucks, timmytuffknuckles, backupsitetuff) to produce the 116 packages in the May 27 wave. Package names like ishowfeet, ilovefemboys, bomboclatwallahi, and imillegal point to a teenage operator.

Package Structure and Entry Point

Every package.json sets main: "sw.js", but no install hooks exist. The packages are not designed to be imported as Node.js modules. They are static web assets meant to be served via a CDN or hosting provider. The sw.js file is a service worker for the Scramjet web proxy:

// sw.js (v1.1.2/v1.1.7 generation, obfuscated variable names replaced)
importScripts('./8cfc2/hgshm.js');
const { ScramjetServiceWorker } = loadWorker();
const proxySw = new ScramjetServiceWorker();
self.addEventListener('install', () => {
void self.skipWaiting();
});
self.addEventListener('activate', (event) => {
event.waitUntil(self.clients.claim());
});

The service worker intercepts all fetch events on its origin, routes them through Scramjet’s proxy engine, and injects a script into every proxied HTML response. That injected script hooks window.open, anchor clicks, and form submissions to capture new-tab navigation and relay it to the parent frame via postMessage. The window.open.__lucideIntercepted flag names the project: “Lucide Proxy,” matching the GitHub repository URL github[.]com/lucideproxy/svg.

The SEO Disguise

The HTML pages masquerade as legitimate tutoring businesses. The v1.1.3 wave uses “Northstar Tutoring” branding; the v1.1.2/v1.1.7 waves use “Riverbend Tutoring.” Both claim to serve Portland, Oregon. The visible content is a loading page:

index.html
<title>Riverbend Tutoring | After-School Coaching &amp; Test Prep</title>
<meta
name="keywords"
content="academic coaching, after school coaching,
weekly tutoring, study skills coaching, executive function support,
college prep, ACT prep, SAT prep, AP exam prep, homework help..."
/>

A hidden <div style="display: none" aria-hidden="true"> block stuffs 800+ words of SEO keyword text about tutoring services, test prep, and study habits. Search engines index it; users never see it. The og:url metadata points to hxxps://21baseballacademy[.]com, linking the fake tutoring site to the ad infrastructure.

Adware Payload

The index.html loads three monetization layers:

1. Popunder ad (inline script):

// index.html, inline <script>
(function () {
var k = 'p7k2x';
var cd = 9e5; // 900,000 ms = 15 minutes cooldown
var u = 'https://abdct.com/';
function ok() {
try {
var t = +localStorage.getItem(k) || 0;
return Date.now() - t >= cd;
} catch (_) {
return true;
}
}
function fire() {
if (!ok()) {
done();
return;
}
try {
localStorage.setItem(k, String(Date.now()));
} catch (_) {}
try {
window.open(u, '_blank', 'noreferrer');
window.focus();
} catch (_) {}
done();
}
if (ok()) {
['click', 'keydown', 'touchstart'].forEach(function (e) {
document.addEventListener(e, fire, true);
});
}
})();

On the first user interaction (click, keypress, or touch), this opens hxxps://abdct[.]com/ in a new tab and refocuses the original window. It rate-limits itself to once every 15 minutes using localStorage. The use of capture-phase event listeners ensures the popunder fires before any other handler can prevent it.

2. External ad script:

<script src="https://cdn.21baseballacademy.com/script/jrqK2HPsliMjRW5Q.js" defer></script>

This domain serves additional monetization JavaScript. At the time of analysis, the endpoint returned an empty response.

3. Monetization script in the main bundle (v1.1.3 generation):

// assets/index-_PhgPvMS.js (extracted URL)
'https://woofbeginner.com/0a/91/35/0a913561831bdf2c26dcf18b852b5cc1.js';
'https://woofbeginner.com/jivd2xu8?key=c6851a038da578a80eeb201e0588c84c';

The woofbeginner[.]com domain loads additional ad scripts with a monetization key parameter. At analysis time, this endpoint returned a 500 error.

4. Google Analytics tracking:

<script async src="https://www.googletagmanager.com/gtag/js?id=G-0VL3ZSBXDH"></script>
<script>
window.dataLayer = window.dataLayer || [];
function gtag() {
dataLayer.push(arguments);
}
gtag('js', new Date());
gtag('config', 'G-0VL3ZSBXDH');
</script>

All 141 packages report user activity to the same Google Analytics property.

Obfuscation

The v1.1.3 wave (May 7) shipped readable source. Waves 2 and 3 ran the React application code through a hex variable name obfuscator, randomized file names (e.g., a3g0q43tbe.js, 73sxysj46r.js), and replaced all identifiers with _0x prefixed hex patterns:

// assets/a3g0q43tbe.js (first 200 chars)
(function(_0x1b35e9,_0x379ecc){const _0x47926c={_0x46fe72:0x7ae,
_0x53a4a0:0xaad,_0xdcea50:0xe56,_0x2a9cda:0xae0,...

The obfuscator builds a shuffled string array, then resolves literals at runtime through a rotation function and index-offset lookups. Directory names also changed: the Scramjet runtime moved from runtime/scramjet/ to 8cfc2/, baremux from runtime/baremux/ to d1g0y/.

The service worker (sw.js) and index.html stayed readable across all waves. The popunder code, external script URLs, and analytics IDs sit in plain text.

Discord Authentication

The application uses Discord OAuth for user authentication. A .well-known/discord file in the v1.1.2/v1.1.7 packages contains the verification hash bb7ce9f4c508bc87f13889c18031ca42ad8c1bd5. The React bundle includes a Discord sign-in flow that sends verification codes to users via Discord DM.

The AI branding SVGs in branding/ (Anthropic, OpenAI, DeepSeek, xAI, Gemini, Lucide) and a Roblox shortcut SVG suggest the proxy targets students who want to access AI chatbots and gaming sites from school networks.

Conclusion

These packages do not steal credentials, install backdoors, or compromise build pipelines. They abuse npm as free static hosting for an ad-monetized web proxy targeting students. Anyone who lands on these pages through search results or shared links gets popunder ads, third-party tracking, and a service worker that intercepts all their proxied web traffic.

The auto-publish.sh script left in the tarball, the juvenile package names, and the “TY WAVES + CHATGPT ILY” comment in the service worker point to a young operator monetizing a Scramjet-based proxy unblocker through ad fraud. One account published 116 packages in under 35 minutes. npm had no rate limit to stop it.

References

  • npm
  • malicious-package
  • supply-chain-security
  • adware
  • registry-abuse
  • malware

Author

SafeDep Logo

SafeDep Team

safedep.io

Share

The Latest from SafeDep blogs

Follow for the latest updates and insights on open source security & engineering

Background
SafeDep Logo

Ship Code.

Not Malware.

Start free with open source tools on your machine. Scale to a unified platform for your organization.