141 npm Packages Abuse Registry as Adware Hosting
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[.]comandwoofbeginner[.]comexecute 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/discordverification 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
| 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 |
| No matching rows | ||||
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:
| Wave | Date | Version | Count | Notes |
|---|---|---|---|---|
| 1 | May 7 | 1.1.3 | 2 | Readable code, Scramjet referenced directly |
| 2 | May 26 | 1.1.2 | 23 | Obfuscated JS, randomized filenames |
| 3 | May 27 | 1.1.7 | 116 | Mass-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:
#!/bin/bash
BASE="backupsitetuff"TOTAL=10PARALLEL=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
waitecho "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:
<title>Riverbend Tutoring | After-School Coaching & 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 Team
safedep.io
Share
The Latest from SafeDep blogs
Follow for the latest updates and insights on open source security & engineering

forge-jsxy: 22 Versions of an Actively Developed npm RAT
forge-jsxy picked up where the taken-down forge-jsx left off, publishing 22 versions over 22 days. Each release added new capabilities: crypto wallet scanning, Chromium extension theft, WebRTC data...

Polymarket npm Packages Steal Crypto Wallet Keys
Nine coordinated npm packages target Polymarket traders with a social-engineered postinstall prompt that exfiltrates raw private keys to a Cloudflare Worker. The attacker published all packages...

Megalodon: Mass GitHub Repo Backdooring via CI Workflows
Over 5,700 malicious commits were pushed to GitHub repositories on May 18, 2026, replacing GitHub Actions workflows with base64-encoded secret exfiltration payloads. The "megalodon" campaign targeted...

art-template npm Hijack Delivers iOS Browser Exploit Kit
art-template versions 4.13.3 through 4.13.6 were compromised via maintainer account takeover. The browser bundle injects scripts that deliver a full iOS exploit kit: WebAssembly type confusion, JIT...

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