TL; DR
Ein Artefakt von Maven Central, veröffentlicht als io.github.davidtimur:c2-lab Es wurden neun Releases veröffentlicht, von denen acht während der Kompilierung eines beliebigen nachgelagerten Projekts, das die JAR-Datei in seinen Annotationsprozessor-Pfad einfügte, eine Remote-Access-Payload ausführten. Es war nicht erforderlich, die JAR-Datei im Anwendungscode zu importieren oder in einer Quellcodezeile darauf zu verweisen. Es gab kein Installationsskript, kein entsprechendes Postinstall-Äquivalent und keinen Lifecycle-Hook jeglicher Art, den die Tools hätten untersuchen können.
Der Ausführungsvektor ist eine einzelne 46-Byte-Datei innerhalb der JAR-Datei: eine Java-Dienstanbieterregistrierung, die eine Klasse benennt, die implementiert javax.annotation.processing.ProcessorDer Java-Compiler erkennt solche Registrierungen automatisch. Sobald sie gefunden sind, javac Die Klasse wird im Rahmen des normalen Kompilierungsprozesses instanziiert und ausgeführt. Das bedeutet, dass die Laufzeitumgebung der Nutzlast der Build-Rechner ist; in diesem Moment erfüllt der Build-Rechner seine einzige Aufgabe.
Im Laufe der neun Releases wurde der Command-and-Control-Kanal dreimal neu aufgebaut: zuerst über eine vom Operator bereitgestellte Callback-URL, dann über eine Reverse-Shell über einen ngrok-TCP-Tunnel und schließlich über einen HTTP-Polling-Kanal, dessen Pfade sich zweimal änderten. Die letzte Version installierte einen TLS-Trust-Manager ohne operative Funktion als JVM-Standard. Die Annahme jeglicher Zertifikate für die Dauer der Kompilierung.
Das Artefakt trug in seiner eigenen POM-Datei die Bezeichnung „C2 Lab Payload“ und war unter einer MIT-Lizenz veröffentlicht. Es war während des gesamten Beobachtungszeitraums auf Maven Central verfügbar. wurde inzwischen zusammen mit dem gesamten zugehörigen System entfernt. io.github.davidtimur Gruppe.
Anatomie: Der Compiler als Ausführungsmaschine
Das Java-Framework zur Verarbeitung von Annotationen ermöglicht es Bibliotheken, Code zur Kompilierzeit zu generieren – der Mechanismus hinter Lombok, Dagger und einer Vielzahl von ORM- und Serialisierungswerkzeugen. Ein Prozessor kündigt sich durch eine einfache Textdatei im JAR-Archiv an:
META-INF/services/javax.annotation.processing.Processor
Der Inhalt dieser Datei in jeder betroffenen Version, wortgetreu und vollständig:
io.github.davidtimur.c2lab.C2Processor
Die Datei ist in den Versionen 1.0.1 bis 1.0.8 byte-identisch, md5 7d2a08a5c8869a47eea9fa62487dfbe4. Version 1.0.0 enthält es nicht.
Wenn die Funktion Javak Beim Ausführen durchsucht es den Pfad des Annotationsprozessors nach diesen Serviceeinträgen und lädt die gefundenen Einträge. Im zu kompilierenden Projekt muss der Prozessor weder erwähnt noch annotiert oder konfiguriert werden. Die bloße Präsenz im Pfad genügt. Diese Eigenschaft unterscheidet JavacDoor von den Lieferkettenmustern, auf denen die meisten Tools basieren.
| Schnittmuster | Auslösen | Sichtbar als |
|---|---|---|
| npm install hook | npm install | scripts.postinstall im Manifest |
| Python-Importzeit-Nutzlast | erster Import des Moduls | Modul-Anweisung im Quellcode |
| JavacDoor | javac bei jedem nachgelagerten Projekt | ein Dienstregistrierungsdateiname |
Ein Installations-Hook ist eine Deklaration in einem Manifest, und das Manifest ist das Erste, was jeder liest. Eine Import-Payload befindet sich zumindest in lesbarem Quellcode. Eine Dienstregistrierung ist keines von beidem: Sie besteht aus einem Dateinamen und einer Zeile, die eine Klasse benennt, und das Verhalten befindet sich im kompilierten Bytecode in einem anderen Verzeichnis.
Die Payload-Klasse selbst führt eine Host-Aufklärung durch, indem sie eine Shell ausführt. Die Konstantenpools der kompilierten Klassen enthalten / Bin / sh, whoami, uname -a, pwd auf dem Unix-Pfad und tasklist auf dem Windows-Pfad, zusammen mit redirectErrorStream um die Fehlerausgabe des Kindprozesses in den erfassten Datenstrom einzufügen. Zwei JSON-Vorlagen übertragen die Ergebnisse vom Host – einem Registrierungs-Beacon:
{"host":"%s","os":"%s","user":"%s","dir":"%s"}
bevölkert aus der hostname Befehl und die os.name, user.name und Benutzerverzeichnis Systemeigenschaften und eine Ergebnis-Callback-Funktion:
{"version":"%s","host":"%s","time":"%s","output":"%s"}
Die im Ausgabeprogramm des Compilers hinterlassenen Fortschrittsmarkierungen sind ungewöhnlich offen: [C2] Ausführung zur Kompilierzeit abgeschlossen, [C2] Callback gesendet → HTTP , [C2] Shell verbunden mit .
Neun Veröffentlichungen, drei C2-Generationen
Die Releases sind nicht neun Kopien einer einzigen Nutzlast. Sie stellen ein Iterationsprotokoll dar, und das Lesen der Releases in der richtigen Reihenfolge zeigt, dass der Kanal neu aufgebaut wurde, während der Zustellungsvektor unverändert blieb.
| Loslassen | Wird beim Kompilieren automatisch ausgeführt | Kanal |
|---|---|---|
1.0.0 | Nein (keine Servicedatei) | Nur vom Betreiber bereitgestellte Rückruf-URL |
1.0.1 | Ja (Vektor eingeführt) | Vom Betreiber bereitgestellte Rückruf-URL |
1.0.2 | ja | Reverse Shell, ngrok TCP-Tunnel |
1.0.3 | ja | HTTP-Kanal, /register /cmd /out |
1.0.4 | ja | HTTP-Kanal, /register /cmd /out |
1.0.5 | ja | HTTP-Kanal, /register /poll /out |
1.0.6 | ja | HTTP-Kanal, /register /poll /out |
1.0.7 | ja | HTTP-Kanal, /register /cmd |
1.0.8 | ja | HTTP-Kanal + TLS-Validierung deaktiviert |
Drei Details in dieser Tabelle sind besonders hervorzuheben, da jedes einzelne die Lesart des Artefaktsets verändert.
Der Vektor kommt bei 1.0.1 an, nicht bei 1.0.2. Version 1.0.0 enthält dieselbe Aufklärungs- und Rückruflogik, jedoch keine Servicedatei und keine Importe zur Annotationsverarbeitung; sie wird nur ausgeführt, wenn sie aufgerufen wird. Ab Version 1.0.1 ist die Servicedatei vorhanden und die kompilierten Klassen importieren javax.annotation.processing.SupportedSourceVersionJede Bewertung, die die beiden Ticketversionen – 1.0.0 und 1.0.2 – vergleicht, kommt zwar zu dem richtigen Schluss, dass sich etwas geändert hat, identifiziert aber fälschlicherweise, wo.
Die umgekehrte Hülle existiert in genau einer Version. Version 1.0.2 enthält 0.tcp.ngrok[.]io, die Logzeile [C2] Shell verbunden mit 0.tcp.ngrok[.]io:19823, ein interaktives Banner c2-Schaleund ein Rahmenabschluss __ENDE__Die Versionen ab 1.0.3 enthalten keine dieser Funktionen mehr und erreichen stattdessen einen HTTPS-Host. Eine Abmahnung, die sich ausschließlich auf die betroffenen Versionen bezog, hätte einen nicht mehr existierenden TCP-Endpunkt angeführt, während der in sechs späteren Versionen vorhandene, aktive HTTP-Kanal unerwähnt blieb.
Die finale Version entfernt die Transportverifizierung. Version 1.0.8 fügt eine Klasse hinzu, die implementiert javax.net.ssl.X509TrustManager deren Zertifikatsprüfungsmethoden nichts bewirken, ein immer wahrer Hostnamenprüfer, der über setDefaultHostnameVerifierUnd eine trustAll Eine Routine installiert beides als Standardeinstellungen der JVM. Dadurch akzeptiert die JVM für den Rest des Kompilierungsprozesses jedes Zertifikat von jedem Host – nicht nur für den Datenverkehr der Nutzdaten selbst, sondern auch für alle weiteren TLS-Verbindungen, die der Build anschließend durchführt.
Der HTTP-Kanal wird in allen sechs Versionen, die ihn verwenden, von einem einzigen Host bereitgestellt: tableful-fervor-crazed.ngrok-free[.]devJede Anfrage enthält den Header ngrok-skip-browser-warningDadurch wird die Zwischenseite unterdrückt, die kostenlose ngrok-Tunnel Browsern bereitstellen. Pfadsätze ändern sich mit jeder Version. /aus verschwindet bei Version 1.0.7, und /cmd wechselt mit /Umfrage — aber der Gastgeber bleibt immer derselbe.
Die Selbstausschluss-Erkennung
Jede POM-Datei ab Version 1.0.1 legt ein Compilerargument für den eigenen Build des Artefakts fest:
-proc:none
Dieses Flag deaktiviert die Annotationsverarbeitung. Seine Wirkung hier ist vorcise: Wenn das Projekt, das den Prozessor enthält, selbst kompiliert wird, wird der Prozessor nicht ausgeführt.
Dieses Flag hat ganz normale Anwendungsfälle. Ein Projekt, das einen Annotationsprozessor mitbringt, muss oft vermeiden, diesen Prozessor während des Bootstraps auf sich selbst anzuwenden, und die Dokumentation des Build-Tools empfiehlt genau dies. Für sich genommen beweist es nichts.
Zusammen mit der Funktionsweise des Prozessors beschreibt dies eine spezifische Asymmetrie: Der Code wird auf den Rechnern aller ausgeführt, die gegen das Artefakt kompilieren, nicht aber auf dem Rechner, der das Artefakt erstellt. Das Flag erscheint in derselben Version – 1.0.1 –, die die Servicedatei einführt, und in jeder nachfolgenden Version. Die Korrelation zwischen „der Version, in der die Ausführung zur Kompilierzeit beginnt“ und „der Version, in der die Ausführung zur Kompilierzeit lokal deaktiviert wird“ ist das mit Abstand nützlichste Analysesignal im Artefaktset und ist in einer einfachen POM-Datei ohne Dekompilierung sichtbar.
Wir stellen den Effekt fest und belassen es dabei. Nichts in den Artefakten gibt Aufschluss darüber, warum die Flagge gesetzt wurde.
Ein weiteres Metadatenelement verdient Erwähnung, hauptsächlich um es hier zu verwerfen. Die POM-Datei nennt das Projekt „C2 Lab Payload“, beschreibt es als „C2 Lab Payload-Artefakt“ und lizenziert es unter der MIT-Lizenz. Solche Selbstbezeichnungen werden mitunter als Beleg dafür angeführt, dass es sich bei einem Paket um eine Forschungsanwendung handelt.cisEs handelt sich eher um eine potenzielle Bedrohung als um eine akute Bedrohung, und gelegentlich ist diese Interpretation korrekt – ein deklarierter Canary-Test ohne erreichbare Infrastruktur ist etwas anderes. Dies trifft hier jedoch nicht zu. Ein funktionsfähiges Implantat, das in einem öffentlichen Repository veröffentlicht und von jedem Nutzer erreichbar ist, dessen ausgehende Infrastruktur in neun Releases dreimal neu aufgebaut wurde, ist unabhängig von der Bezeichnung in seinen Metadaten eine aktive Funktion. Der Name in der POM-Datei ändert nichts daran, was auf einem Rechner passiert, der damit kompiliert.
Indikatoren für Baumaschinen
Wenn ein Build-Host gegen dieses Artefakt kompiliert, befinden sich die Beweise in den Build-Protokollen und der Netzwerktelemetrie und nicht in einem permanenten Implantat auf der Festplatte – die Payload wird innerhalb des Compilerprozesses ausgeführt und beendet sich mit diesem.
Im JAR-Archiv oder im lokalen Repository-Cache
META-INF/services/javax.annotation.processing.ProcessorBenennungio.github.davidtimur.c2lab.C2Processor- Dienstdatei MD5
7d2a08a5c8869a47eea9fa62487dfbe4 - Klassen unter
io/github/davidtimur/c2lab/:C2Processor,C2Task,Taskund in Version 1.0.8 die innere KlasseTask$1
In der Ausgabe des Build-Prozesses
[C2] compile-time execution complete[C2] callback sent → HTTP[C2] callback failed:[C2] shell connected to- Interaktives Banner
c2-shell; Frame-Terminator__END__
In-Prozess-Telemetrie
javacals Elternteil von/bin/sh -c(Unix) oder ein Windows-Befehlsinterpreter- Kindbefehle
whoami,uname -a,pwd(Unix) odertasklist(Windows) untergeordnet einem Kompilierungsschritt
In der Netzwerktelemetrie
- Ausgehendes TCP zu
0.tcp.ngrok[.]io:19823(Version 1.0.2) - HTTPS zu
tableful-fervor-crazed.ngrok-free[.]dev, Pfade/register,/cmd,/poll,/out(Versionen 1.0.3 bis 1.0.8) - Anfrageheader
ngrok-skip-browser-warning: true - Anfragetexte, die übereinstimmen
{"host":...,"os":...,"user":...,"dir":...}or{"version":...,"host":...,"time":...,"output":...}
In der Konfiguration
- Umgebungsvariablen
CALLBACK,CALLBACK_URL; Systemeigenschaftcallback.url
Herausgeber-Metadaten
- Gruppe an
io.github.davidtimur; Verlagsadressedavudboi999@gmail[.]com; SignaturschlüsselE520C345EF94423D
Bei einem Build, der Version 1.0.8 ausgeführt wurde, ist eine zusätzliche Prüfung erforderlich. Da diese Version standardmäßig einen permissiven Trust Manager für die gesamte JVM installiert, wurden alle später in derselben JVM hergestellten TLS-Verbindungen – z. B. zur Auflösung von Abhängigkeiten, zum Hochladen von Artefakten oder im Rahmen eines Deployment-Schritts – ohne Zertifikatsvalidierung durchgeführt. Der Datenverkehr aus diesem Zeitraum sollte daher nicht als authentifiziert gelten.
Warum kompilierte Artefakte unterschiedliche Scans benötigen
JavacDoor ist ein nützlicher Testfall, weil er gleich zwei gängige Annahmen widerlegt, und keiner der Fehler ist spezifisch für die Tools eines bestimmten Anbieters.
Die erste Annahme ist, dass gefährlicher Code sich in einem Manifest ankündigt. Ein Großteil der Werkzeuge für die Lieferkette ist auf den Lebenszyklus ausgerichtet. hooksDenn bei npm und PyPI findet die eigentliche Action meist dort statt. JavacDoor hat keinen Hook. Auslöser ist eine Service-Registrierungsdatei, deren Name ein Java-Interface und deren Inhalt ein Klassenname ist. Um dies statisch abzufangen, muss man sie entsprechend behandeln. META-INF/services/javax.annotation.processing.Processor als eigenständiger Ausführungspunkt, gleichwertig mit einem Nachinstallation Skript – und dann der benannten Klasse im Bytecode folgen. Ökosysteme verfügen über eigene automatische Erkennungsmechanismen dieser Art, und jeder ist ein Einstiegspunkt, unabhängig davon, ob die Tools ihn als solchen auflisten oder nicht.
Die zweite Annahme ist, dass Zeichenketten in Quelldateien gespeichert sind. Bei einem JAR-Archiv befinden sich die Endpunkte, die Shell-Befehle, die JSON-Vorlagen und die Log-Markierungen alle in den Konstantenpools von .Klasse Dateien. Tools, die Text durchsuchen, finden nichts – nicht weil die Zeichenketten verschleiert sind, sondern weil sie sich in einem strukturierten Binärcontainer befinden, den eine Textsuche nicht analysieren kann. Alle Netzwerkindikatoren in diesem Beitrag stammen aus der Analyse von Konstantenpools. Einer davon ist ein vollständig geformtes https:// Die URL liegt offen in einer Klassendatei; ein Textscan des lesbaren Inhalts der JAR-Datei würde sie trotzdem nicht aufdecken. Es gibt hier keine Kodierung zu überwinden, nur ein Containerformat, das gelesen werden muss.
Beide Lücken weisen dieselbe Struktur auf: Ein Artefaktformat wurde als eine Ansammlung von Dateien behandelt, anstatt als Struktur mit definierter Semantik. Die Korrektur ist unspektakulär: Man analysiert den Container, listet die automatischen Erkennungspunkte des Ökosystems auf und verfolgt sie bis in den kompilierten Code. Speziell für Build-Zeit-Vektoren ist eine dritte Prüfung kostengünstig und überraschend aufschlussreich: Man vergleicht die Auswirkungen eines Artefakts auf die Nutzer mit den Ausnahmen, die es für sich selbst vorsieht. Ein Artefakt, das einen Kompilierzeit-Prozessor registriert und gleichzeitig die Kompilierzeitverarbeitung für den eigenen Build deaktiviert, liefert in zwei Zeilen Klartext-POM wichtige Informationen über sich selbst.
Für Teams, die heute Maven-Artefakte verwenden, ergeben sich folgende drei praktische Maßnahmen:
- Behandeln Sie den Pfad des Annotationsprozessors als Ausführungsgrenze. Abhängigkeiten, die dort landen, führen Code in Ihrem Build aus. Wenn ein Build keine Annotationsverarbeitung benötigt, -proc:none ist defensiv genauso nützlich wie es hier offenbar lokal war; wo dies der Fall ist, sollte der Prozessorsatz explizit festgelegt werden, anstatt ihn vom Kompilierungs-Classpath zu erben.
- Compiler-Unterprozesse protokollieren. Ein Kompilierungsschritt, der eine Shell erzeugt, ist in den meisten Projekten eine Anomalie und kann trivialerweise gemeldet werden.
- Metadaten dürfen nicht als Beweismittel betrachtet werden. Die Begriffe „Labor“, „Test“, „Payload“ und „PoC“ in einem Paketnamen oder einer Beschreibung stellen keine Bereichsbeschränkungen dar. Erreichbarkeit und Verhalten hingegen schon.
Das Artefakt und seine gesamte Gruppe wurden entfernt. Maven Nach unserem Bericht liefert Central nun sowohl für den Artefaktpfad als auch für den Gruppenpfad den Fehlercode 404 zurück, und der Central-Index meldet keine übereinstimmenden Koordinaten. Damit ist dieses Artefakt geschlossen. Der Vektor bleibt jedoch erhalten; er ist eine dokumentierte Funktion des Java-Compilers und steht jedem zur Verfügung, der eine JAR-Datei veröffentlicht.
Referenzen
Dieser Beitrag zitiert keine externen Quellen. Alle Ergebnisse basieren auf einer statischen Analyse der neun veröffentlichten JAR-Dateien, die vor ihrer Entfernung von Maven Central abgerufen wurden. Der Code in den Artefakten wurde zu keinem Zeitpunkt ausgeführt.




