1 Punkte von GN⁺ 4 시간 전 | Noch keine Kommentare. | Auf WhatsApp teilen
  • Nachdem auf LinkedIn eine Stelle als Software Engineer angeboten wurde, wurde ein privates GitHub-Repository als scheinbar normale React/Web3-Aufgabe übermittelt; beim Ausführen lief auf dem Entwicklerrechner JavaScript-Malware, die in tailwind.config.js versteckt war
  • Die 30.987 Byte große Konfigurationsdatei versteckte nach Tausenden Leerzeichen 27 KB obfuskierten Code, lud von einem Remote-Server eine mit AES-256-CBC verschlüsselte Stage-2-Payload herunter, speicherte sie unter %TEMP%\pack und führte sie anschließend mit node pack aus
  • Eine etwa 10-minütige Beobachtung in einer isolierten Windows-11-ARM-VM zeigte, dass die Payload vier Komponenten startete: eine Remote-Control-Backdoor, einen Browser- und Wallet-Stealer, einen rekursiven Dateiscanner und einen Clipboard-Monitor
  • Über Socket.IO wurden Terminal, SSH, Bildschirm, Tastatur und Maus gesteuert; außerdem wurden Chromium-Datenbanken, Speicher von Wallet-Erweiterungen, SSH-Keys, Quellcode, Konfigurationsdateien und weitere Daten gesammelt. Auf dem Datei-Upload-Port wurden 2.588 Anfragen beobachtet
  • Aufgaben-Repositories von unbekannten Personen sollten als nicht vertrauenswürdiger Code behandelt und in einer Einweg-VM oder einem Container ohne echte Browserprofile, SSH-Keys, Cloud-Zugangsdaten oder Wallets geprüft werden. Bei einer Infektion sollten nach Netzwerktrennung und Beweissicherung auch Geheimnisse rotiert werden

Als normale Web3-Recruiting-Aufgabe getarntes Repository

  • Der LinkedIn-Nutzer Wayan Adrian bot eine Software-Engineer-Stelle bei shrapnel.com an und übermittelte als Aufgabe das private GitHub-Repository tech-active-workplace-frontend-main des Accounts yevhen-o
  • Die NFT-Kampagnenplattform namens BLAIEXS wirkte nach außen wie ein gewöhnliches React/Web3-Projekt
    • Das React-Frontend bot ein Marken- und Admin-Dashboard, Kampagnenverwaltung, einen Ablauf zur NFT-Erstellung und Ähnliches
    • Die Express-API verarbeitete Authentifizierung, Dashboard-Daten, KYB-Prüfungen, NFT-Erstellung und -Claims, Datei-Uploads sowie einige Stub-Endpunkte
    • Das Seed-Skript erzeugte Demo-Konten für Superadmin, Marke und Verbraucher, eine Kampagne Demo NFT Drop sowie ein NFT Demo Collectible mit 100 verfügbaren Exemplaren
  • Der tatsächliche Aufgabenumfang war eine einfache MetaMask-Netzwerkanzeige
    • Die asynchrone Funktion getChainId() in src/utils/ethereum.js sollte so implementiert werden, dass sie eth_chainId aufruft und den geparsten Hex-Wert oder null zurückgibt
    • getNetworkLabel(chainId) in derselben Datei sollte so implementiert werden, dass sie im bestehenden Objekt NETWORKS einen lesbaren Netzwerknamen findet
    • src/components/Wallet/ConnectWalletButton.js sollte so geändert werden, dass nach dem Verbinden der Wallet beide Funktionen aufgerufen werden
  • Nach Abschluss der Implementierung wurde der Entwicklungsserver gestartet, fuhr aber lange nicht hoch; als der Nutzer etwas Verdächtiges bemerkte, zog er das Internetkabel ab

In tailwind.config.js versteckter Ausführungscode

  • Laut ls -la war tailwind.config.js 30.987 Byte groß, im Editor waren jedoch nur 97 Zeilen zu sehen; wc -c bestätigte dieselbe Größe
  • In der Nähe von Zeile 95 war hinter Tausenden Leerzeichen ein großer, kaum lesbarer JavaScript-Blob versteckt
  • Der Code nutzte typische JavaScript-Obfuskationstechniken
    1. Wichtige Strings wurden in einem großen Array gespeichert
    2. Das Array wurde rotiert, bis die Prüfsumme stimmte
    3. Der Zugriff auf Strings wurde hinter Decoder-Funktionen versteckt
    4. Eindeutige Namen wurden durch numerische Indizes und Wrapper-Aufrufe ersetzt
  • Einer der beiden Decoder stellte Strings in Base64-Form wieder her, der andere wendete einen stringspezifischen Schlüssel und eine RC4-ähnliche Stromchiffre an; zudem wurde das String-Array rotiert, bevor die Decoder-Indizes stimmten
  • Auch ohne die Malware auszuführen, ließ sich die Obfuskation in folgender Reihenfolge entfernen
    1. Das String-Array extrahieren
    2. Die Array-Rotation bis zur erwarteten Prüfsumme nachbilden
    3. Die Decoder-Funktionen neu implementieren
    4. Aufrufe wie b(0x214), c(0x2f1, "key") durch die tatsächlichen Strings ersetzen
    5. Wrapper-Objekte und Hilfsfunktionen vereinfachen, um die Ausführungsabsicht offenzulegen

Verhalten des Stage-1-Droppers

  • Der deobfuskierte Code lud child_process, os, fs, path und crypto und installierte axios sowie socket.io-client im temporären Verzeichnis
  • Handler auf Prozessebene für uncaughtException und unhandledRejection waren so konfiguriert, dass Fehler ignoriert werden
  • Der Ablauf zur Ausführung der Remote-Payload war wie folgt
    • Die UID lautet 59e605dd78fb2aafccd1b622f06a00ca
    • Daten werden von http://45.146.252.17/api/service/59e605dd78fb2aafccd1b622f06a00ca abgerufen
    • UID und der String salt werden an crypto.scryptSync übergeben, um einen 32-Byte-Schlüssel abzuleiten
    • Die Antwort wird im Format base64_iv:base64_ciphertext getrennt und mit aes-256-cbc entschlüsselt
    • Der Klartext wird als Datei pack im temporären Verzeichnis geschrieben
    • Ausführung per child_process.execSync("node pack", { windowsHide: true, cwd: os.tmpdir() })
  • Es handelt sich um einen Malware-Dropper, der statt eigener Funktionalität andere Schadsoftware herunterlädt und ausführt
  • Der heruntergeladene Code hatte weder Signaturprüfung noch Hash-Allowlist, Herkunfts-Pinning, Pfadbeschränkung oder Schutz vor Ausführung, sodass der Remote-Endpunkt Code mit den Rechten des Betriebssystemkontos ausführen konnte, das die Tailwind-Konfiguration geladen hatte

Dynamische Analyse in einer isolierten VM

  • Um das Host-System nicht offenzulegen, wurde mit UTM eine Einweg-Windows-11-ARM-VM eingerichtet
    • Arbeitsspeicher 4096 MiB
    • Festplatte 64 GiB
    • shared/NAT-Networking
    • Gemeinsame Ordner deaktiviert
  • Es wurden Tools installiert, um das Malware-Verhalten aus mehreren Perspektiven zu erfassen
    • Wireshark: Erfassung von Paketen sowie C2- und Exfiltrations-Traffic
    • Sysinternals Sysmon: dauerhafte Windows-Event-Telemetrie
    • Procmon: Erfassung laufender Prozesse sowie Registry- und Dateisystemaktivitäten
  • Um die Sammelziele der Malware zu prüfen, wurden Köderdaten platziert
    • SSH-Schlüsselpaar
    • Privates GitHub-Repository
    • Kryptowährungs-Wallet
    • Browserdaten
    • Weitere Dateien, die für einen Infostealer interessant sein könnten
  • In der VM wurde yarn start ausgeführt und etwa 10 Minuten lang beobachtet; anschließend wurden run1-full.pcapng, run1-sysmon.evtx und stage2-pack.bin gesichert

Im Netzwerk bestätigter Infektionsablauf

  • Die HTTP-Anfragen an 45.146.252.17 waren je nach Port nach Rollen aufgeteilt
    • Port 80: Abruf der Stage-1-Payload, Host-Registrierung, Log-Übertragung
    • Port 7641: Socket.IO-Command-and-Control-Backdoor
    • Port 7646: 2.588 Datei-Uploads
    • Port 7649: 3 größere, aber zahlenmäßig wenige Uploads von Browserdaten
  • Die erste Anfrage war GET /api/service/59e605dd78fb2aafccd1b622f06a00ca; die Antwort lag im Format base64_iv:base64_ciphertext mit Content-Length: 150897 vor
  • tailwind.config.js entschlüsselte die Antwort mit AES-256-CBC, schrieb den Klartext nach %TEMP%\pack und führte ihn mit node pack aus
  • Die Merkmale der gesicherten Stage-2-Payload waren wie folgt
    • SHA-256: 68a64d8c015c06fd70bcb8c5878c1e430da827dd00b62f8e6ef69e76bb94de5b
    • Größe: 113136 Byte
    • Format: einzeiliges ASCII-JavaScript
  • Auch das entschlüsselte pack war erneut obfuskiert, ließ sich aber mit derselben Technik wie in der ersten Stage nach demselben Verfahren deobfuskieren

Zweistufige Payload aus vier Programmen

  • pack war kein einzelnes Skript, sondern ein kleiner Prozessbaum, der die folgenden vier Programme ausführte
    1. schreibt die Command-and-Control-Backdoor scdata in ein temporäres Verzeichnis und führt sie aus
    2. schreibt den Browser- und Wallet-Stealer ldata in ein temporäres Verzeichnis und führt ihn aus
    3. führt einen rekursiven Dateiscanner mit node -e direkt im Speicher aus
    4. führt einen Clipboard-Monitor mit node -e direkt im Speicher aus
  • Die wichtigsten Einstellungen waren UID 59e605dd78fb2aafccd1b622f06a00ca, Benutzerschlüssel 308, Host 45.146.252.17, Socket.IO-Port 7641, Key-Port 7648, Browser-Upload-Port 7649 und Datei-Upload-Port 7646

scdata: interaktive Remote-Control-Backdoor

  • scdata wird als Datei nach %TEMP% geschrieben und dann mit npm i axios socket.io-client ... && node scdata ausgeführt; es bleibt als lang laufender Kindprozess aktiv
  • Nach dem Start richtet es sich so ein, dass es wie ein legitimer Prozess wirkt
  • Außerdem installiert es zusätzliche Pakete, die für Remote-Control-Funktionen nötig sind
    • socket.io-client, ssh2, node-pty: Terminal- und SSH-ähnliche Funktionen
    • sharp, screenshot-desktop, clipboardy, @nut-tree-fork/nut-js: Screenshots, Clipboard, Mausbewegungen/-klicks/-scrollen, Tastatureingaben
  • Socket.IO-Events zeigen den Umfang der Fernsteuerung direkt
    • Terminal: start-terminal, terminal-input, terminal-resize, stop-terminal, command
    • Host-Prüfung und Erfassung: whour, capture
    • Eingabesteuerung: mouseMove, mouseClick, mouseScroll, keyTap, keyCombo
    • Clipboard: copyText, pasteText
    • SSH und Beenden: start_ssh, ssh_input, kill
  • Im PCAP waren das Polling und der WebSocket-Handshake auf Port 7641 sowie das erste Server-Event whour zu sehen; in Sysmon wurden nacheinander node.exe scdata, PowerShell-Abfragen von Systeminformationen und die Installation zweier Gruppen von npm-Paketen erfasst
  • Über einfachen Informationsdiebstahl hinaus stellt es dem Angreifer sogar Shell- und Desktop-Steuerungsfunktionen bereit

ldata: Stealer für Browser- und Wallet-Daten

  • ldata wird nach %TEMP% geschrieben und mit npm i axios && node ldata ausgeführt; der Prozessname lautet npm-cache
  • Es sammelt Browserprofile und Speicher von Wallet-Erweiterungen und sendet sie an http://45.146.252.17:7649/upload; den LevelDB-Status prüft es unter /cldbs
  • Die Ziel-Browser sind je nach Betriebssystem breit angelegt
    • Windows: Chrome, Edge, Brave, LT Browser
    • macOS: Chrome, Brave, Opera, LT Browser, Edge und der lokale Login-Schlüsselbund
    • Linux: entsprechende Profilordner Chromium-basierter Browser
  • Aus den Verzeichnissen Default oder Profile* lädt es die folgenden Chromium-Datenbanken hoch
    • Login Data
    • Login Data For Account
    • Web Data
  • Anschließend durchsucht es Local Extension Settings/<extension-id>; unter den hartcodierten Wallet-Erweiterungs-IDs befindet sich auch MetaMasks nkbihfbeogaeaoehlefnkodbefgpgknn
  • Bei den ersten acht Wallet-Erweiterungspfaden wird das LevelDB-Verzeichnis in einen temporären Ordner kopiert, einzelne Dateien werden hochgeladen, und anhand der cldbs-Antwort des Servers wird entschieden, ob der Vorgang abgeschlossen ist oder erneut versucht wird
  • Die drei auf Port 7649 beobachteten Uploads waren die folgenden Datenbanken
    • Login_Data.sqlite: 51.200 Byte
    • Login_Data_For_Account.sqlite: 51.200 Byte
    • Web_Data.sqlite: 262.144 Byte
  • Die Testdaten enthielten keine gespeicherten Passwörter oder Kartenzeilen, aber sechs Autofill-Werte und Browser-Metadaten
  • Chromium-basierte Browser verschlüsseln einige Felder lokal, daher lassen sich allein aus den Datenbanken nicht immer Klartext-Geheimnisse rekonstruieren
    • In Kombination mit Betriebssystem-Secrets, Cookies, Erweiterungsspeichern und einem entsperrten Browser wird es jedoch Teil einer Pipeline zum Diebstahl von Zugangsdaten
  • Nachdem die benötigten Browserdaten und LevelDBs hochgeladen wurden oder der Server den Vorgang als abgeschlossen markiert, beendet es sich und wartet nicht auf weitere Befehle des Operators

Rekursiver Dateiscanner und Clipboard-Monitor

  • Der rekursive Dateiscanner erstellt keine separate Datei, sondern wird mit node -e ausgeführt und beginnt die Suche im Home-Verzeichnis des Benutzers
    • Unter Windows zählt er mit Get-CimInstance Win32_LogicalDisk Laufwerksbuchstaben auf und prüft auch das Stammverzeichnis jedes Laufwerks
    • Zu den Sammelmustern für Dateien gehören *.env*, *.md, *.pem, *.ini, *.secret, *.json, *.js, *.ts, *.csv, *.txt, *.doc, *.docx, *.pdf, *.xlsx, .zsh_history, .bash_history
    • ~/.ssh, ~/.aws, ~/.azure, ~/.config, ~/.foundry werden automatisch als interessante Ordner behandelt
    • Auch Namen wie metamask, bitcoin, btc, solana, secret phrase, private key werden gesucht
    • Ergebnisse werden an http://45.146.252.17:7646/upload übertragen
  • In der Köderumgebung wurden unter anderem id_ed25519, id_ed25519.pub, History.txt, seed.js, password.js, tokens.js, ConnectWalletButton.js, ExportWallet.js, RegisterWallet.js, tailwind.config.js, aptos-cli.json tatsächlich hochgeladen
  • Während ldata Browser- und Wallet-Speicher übernimmt, sammelt der Dateiscanner breit SSH-Schlüssel, Konfigurationen, Quellcode, lokale Secrets, Shell-Historien und Projektdateien
  • Auch der Clipboard-Monitor wird mit node -e ausgeführt; sein Prozessname ist npm-compiler.log
    • Nach einigen Sekunden Wartezeit liest er wiederholt die Zwischenablage aus
    • Unter macOS verwendet er pbpaste, unter Windows powershell -NoProfile -NonInteractive Get-Clipboard
    • Geänderte Werte werden an http://45.146.252.17/api/service/makelog gesendet
  • Auch ohne das Format zu interpretieren, kann er Passwörter, Recovery-Phrasen, private Schlüssel, API-Tokens, Einmalcodes, GitHub-URLs, Wallet-Adressen und Deployment-Secrets, die der Benutzer kopiert, unverändert erfassen

Grundsätze vor dem Ausführen einer Recruiting-Aufgabe

  • Aufgaben-Repositories von unbekannten Personen sollten bis zur Sicherheitsprüfung als nicht vertrauenswürdiger ausführbarer Code behandelt werden
  • Vor yarn install, npm install, yarn build, yarn start sollten die folgenden Punkte geprüft werden
    • package.json
    • Lifecycle-Skripte
    • Konfigurationsdateien
    • offensichtliche Dependency-Hooks
  • In diesem Fall versteckte sich der Schadcode in tailwind.config.js, das viele leicht übersehen; Konfigurationsdateien, Dependencies und Build-Tools können ebenfalls als Code ausgeführt werden
  • Interview-Projekte sollten in einer Einweg-VM oder einem Container ausgeführt werden, in der bzw. dem keine echten Secrets eingebunden sind
    • persönliches Browserprofil
    • Passwortmanager
    • SSH-Schlüssel
    • Kryptowährungs-Wallet
    • GitHub-Token
    • Cloud-Zugangsdaten
    • Banking-Sitzung
    • primäres E-Mail-Konto
  • Für Aufgaben, die ein Konto oder eine Wallet erfordern, sollte eine neue Testidentität verwendet werden, die kein Guthaben enthält und bei keinem anderen Dienst wiederverwendet wird
  • Bei Web3-Aufgaben sollte ausschließlich ein Testnet genutzt werden; selbst wenn es harmlos wirkt, sollte man eine echte Wallet nicht mit unbekannten Projekten verbinden

Indikatoren zur Prüfung einer Infektion

  • Unter macOS und Linux sollten zuerst die folgenden Punkte geprüft werden
    • laufende Prozesse im Zusammenhang mit node, pack, scdata, ldata, npm-compiler, vhost.ctl
    • Verbindungen zu 45.146.252.17 oder zu den Ports 7641, 7646, 7649
    • pack, scdata, ldata, vhost.ctl in temporären Verzeichnissen sowie unter Downloads, Desktop, Documents und Library
    • Markierungsdatei */.npm/vhost.ctl
    • Zeichenketten wie IP, UID, /api/service/makelog, node scdata, node ldata, node pack innerhalb des heruntergeladenen Projekts
  • Unter Windows sollten die folgenden Punkte geprüft werden
    • Prozesse node, npm, cmd, powershell, deren Befehlszeile pack, scdata, ldata, node -e, IP-Adressen oder Get-Clipboard enthält
    • offene TCP-Verbindungen zu 45.146.252.17 oder zu den Remote-Ports 7641, 7646, 7649
    • pack, scdata, ldata, vhost.ctl, .npm\vhost.ctl unter %TEMP%
    • bekannte C2-Zeichenketten im Verzeichnis, in das das Interview-Repository geklont wurde
  • Wenn ein laufender Node-Prozess, eine Verbindung zu dieser IP oder eines der Artefakte pack, scdata oder ldata gefunden wird, muss das System als kompromittiert betrachtet werden
  • Diese Indikatoren sind eine erste, auf die analysierten Samples zugeschnittene Prüfung und kein vollständiges forensisches Verfahren

Bereinigung infizierter Systeme und Austausch von Geheimnissen

  • Zuerst sollte der Computer vom Netzwerk getrennt werden; in der infizierten Umgebung sollte man nicht weiter suchen, debuggen oder neue Geheimnisse kopieren
  • Wenn Beweise benötigt werden, sollten vor dem Löschen die folgenden Daten gesichert werden
    • Prozessliste
    • Netzwerkverbindungen
    • verdächtige Dateien
    • Browserverlauf
    • bösartiges Repository
  • Danach die Prozesse im Zusammenhang mit node pack, node scdata, node ldata, node -e, npm-compiler, vhost.ctl beenden und pack, scdata, ldata, .npm/vhost.ctl aus den temporären Verzeichnissen entfernen
  • Wenn es sich um eine Wegwerf-VM handelt und normale Node-Arbeiten nicht erhalten werden müssen, kann unter macOS und Linux pkill node oder unter Windows das erzwungene Beenden aller node-Prozesse verwendet werden
  • Das bösartige Repository und node_modules löschen; falls eine weitere Analyse nötig ist, eine ZIP-Kopie offline aufbewahren
  • Das Löschen von Dateien allein reicht nicht aus; die folgenden Geheimnisse müssen ersetzt bzw. verworfen werden
    • SSH-Schlüssel
    • GitHub- und npm-Token
    • Cloud-Schlüssel und API-Schlüssel
    • Deployment-Geheimnisse
    • im Browser gespeicherte Passwörter
    • aktive Login-Sitzungen
  • Falls Wallet-Seeds oder private Schlüssel mit dem infizierten System in Berührung gekommen sein könnten, sollte auf einem sauberen Gerät eine neue Wallet erstellt und das Guthaben übertragen werden

Angriff auf die Vertrauensgrenzen von Entwicklungswerkzeugen

  • Klassische Antivirenprodukte erkannten dieses Repository nicht, und auch ein beliebter AI Coding Agent, der zur Bearbeitung der Aufgabe genutzt wurde, identifizierte den im Projekt versteckten Schadcode nicht
  • Da Tailwind JavaScript-Konfigurationsdateien als Node-Module auswertet, wird die IIFE in Zeile 95 ausgeführt, sobald Entwicklungswerkzeuge, Build-Skripte, Editor-Integrationen, die Tailwind CLI, Bundler oder CI-Jobs die Konfiguration laden
  • Die normale Tailwind-Konfiguration reichte bis Zeile 94; dahinter waren etwa 27 KB obfuskierter Code angehängt
  • Die heruntergeladene Payload wird mit denselben Betriebssystemrechten ausgeführt wie der Benutzer, der die Konfiguration geladen hat
    • Sie kann auf lokale Dateien, Browserprofile, gespeicherte Zugangsdaten, Daten von Wallet-Erweiterungen, SSH-Schlüssel, Umgebungsvariablen, Paket-Token, Cloud-Zugangsdaten, Quellcode und die Zwischenablage zugreifen
    • Wird sie in CI ausgeführt, können auch Deployment-Geheimnisse, Build-Artefakte, Registry-Zugangsdaten und Produktions-Access-Token offengelegt werden
  • Die notwendige Korrektur besteht darin, den obfuskierten JavaScript-Blob aus tailwind.config.js vollständig zu entfernen

Noch keine Kommentare.

Noch keine Kommentare.