TL; DR
Un artefact Maven Central publié sous le nom de io.github.davidtimur:c2-lab Neuf versions ont été publiées, dont huit exécutaient une charge utile d'accès à distance lors de la compilation de tout projet en aval plaçant le fichier JAR dans son chemin d'accès au processeur d'annotations. Aucun code d'application n'avait besoin de l'importer. Aucune ligne de code source n'avait besoin d'y faire référence. Il n'y avait ni script d'installation, ni équivalent post-installation, ni aucun point d'entrée de cycle de vie permettant aux outils d'inspecter.
Le vecteur d'exécution est un unique fichier de 46 octets à l'intérieur du fichier JAR : un enregistrement de fournisseur de services Java qui nomme une classe implémentant javax.annotation.processing.ProcessorLe compilateur Java détecte automatiquement ces enregistrements. Une fois trouvés, javac L'instanciation et l'exécution de la classe se déroulent normalement lors de la compilation. Autrement dit, l'environnement d'exécution de la charge utile est la machine de compilation, et cette dernière effectue actuellement sa fonction principale.
Au cours des neuf versions, le canal de commande et de contrôle a été reconstruit à trois reprises : d’abord une URL de rappel fournie par l’opérateur, puis un shell inversé via un tunnel TCP ngrok, puis un canal d’interrogation HTTP dont les chemins ont été modifiés deux fois de plus. La dernière version a installé un gestionnaire de confiance TLS sans action comme gestionnaire par défaut de la JVM. accepter tout certificat pour la durée de la compilation.
L'artefact contenait les mots « C2 Lab Payload » dans son propre fichier POM et une licence MIT. Il était en ligne sur Maven Central pendant toute la période où nous l'avons observé. a depuis été retiré, ainsi que l'intégralité de son contenu. io.github.davidtimur en groupe.
Anatomie : le compilateur en tant que moteur d'exécution
Le framework de traitement des annotations de Java permet aux bibliothèques de générer du code à la compilation ; c’est le mécanisme sous-jacent à Lombok, Dagger et à de nombreux outils ORM et de sérialisation. Un processeur s’annonce par un fichier texte brut inclus dans le fichier JAR.
META-INF/services/javax.annotation.processing.Processor
Le contenu de ce fichier dans chaque version concernée, mot pour mot et intégralement :
io.github.davidtimur.c2lab.C2Processor
Le fichier est identique octet par octet entre les versions 1.0.1 et 1.0.8, md5 7d2a08a5c8869a47eea9fa62487dfbe4. La version 1.0.0 ne la contient pas.
Lorsque vous javac Lors de son exécution, JavacDoor analyse le chemin du processeur d'annotations à la recherche de ces entrées de service et charge celles qu'il trouve. Aucun élément du projet compilé n'a besoin de mentionner le processeur, d'annoter quoi que ce soit ni de configurer quoi que ce soit. Sa présence dans le chemin suffit. C'est cette caractéristique qui distingue JavacDoor des modèles de chaîne d'approvisionnement sur lesquels la plupart des outils sont construits.
| Patron de Couture | Gâchette | Visible comme |
|---|---|---|
| npm install hook | npm install | scripts.postinstall dans le manifeste |
| Charge utile d'importation Python | première importation du module | déclaration au niveau du module dans la source |
| JavacDoor | javac sur tout projet en aval | un nom de fichier d'enregistrement de service |
Un hook d'installation est une déclaration dans un manifeste, et le manifeste est la première chose que l'on lit. Une charge utile d'importation se trouve au moins dans un code source lisible. L'enregistrement d'un service n'est ni l'un ni l'autre : il s'agit d'un nom de fichier suivi d'une ligne nommant une classe, et le comportement est implémenté dans le bytecode compilé, situé dans un autre répertoire.
La classe de charge utile effectue elle-même une reconnaissance de l'hôte en interrogeant la base de données. Les pools de constantes des classes compilées contiennent / Bin / sh, whoami, uname -a, pwd sur le chemin Unix et tasklist sur le chemin Windows, ainsi que flux d'erreur de redirection pour fusionner la sortie d'erreur du processus enfant avec le flux capturé. Deux modèles JSON acheminent les résultats depuis l'hôte : une balise d'enregistrement.
{"host":"%s","os":"%s","user":"%s","dir":"%s"}
peuplé de nom d'hôte commande et le nom_du_système_d_exploitation, user.name et répertoire utilisateur propriétés système et fonction de rappel de résultat :
{"version":"%s","host":"%s","time":"%s","output":"%s"}
Les indicateurs de progression affichés dans la sortie du compilateur sont d'une franchise inhabituelle : [C2] Exécution à la compilation terminée, [C2] Rappel envoyé → HTTP , [C2] Shell connecté à .
Neuf versions, trois générations de C2
Ces versions ne sont pas neuf copies d'une même charge utile. Il s'agit d'un journal d'itérations, et leur lecture séquentielle montre que le canal a été reconstruit tandis que le vecteur de distribution est resté fixe.
| Libération | Exécution automatique à la compilation | Développement |
|---|---|---|
1.0.0 | non (aucun fichier de service) | URL de rappel fournie par l'opérateur uniquement |
1.0.1 | oui (vecteur introduit) | URL de rappel fournie par l'opérateur |
1.0.2 | oui | Shell inversé, tunnel TCP ngrok |
1.0.3 | oui | Canal HTTP, /register /cmd /out |
1.0.4 | oui | Canal HTTP, /register /cmd /out |
1.0.5 | oui | Canal HTTP, /register /poll /out |
1.0.6 | oui | Canal HTTP, /register /poll /out |
1.0.7 | oui | Canal HTTP, /register /cmd |
1.0.8 | oui | Canal HTTP + validation TLS désactivée |
Trois détails de ce tableau méritent d'être soulignés, car chacun d'eux modifie la façon dont l'ensemble des artefacts doit être interprété.
Le vecteur arrive à 1.0.1, et non à 1.0.2. La version 1.0.0 conserve la même logique de reconnaissance et de rappel, mais ne comporte ni fichier de service ni importations pour le traitement des annotations ; elle ne s'exécute que si elle est appelée. À partir de la version 1.0.1, le fichier de service est présent et les classes compilées sont importées. javax.annotation.processing.SupportedSourceVersionToute évaluation qui compare les deux versions étiquetées — 1.0.0 et 1.0.2 — conclut à juste titre que quelque chose a changé, mais identifie mal où.
Le shell inversé existe dans une seule version. La version 1.0.2 contient 0.tcp.ngrok[.]io, la ligne de logarithme [C2] shell connecté à 0.tcp.ngrok[.]io:19823, une bannière interactive coquille c2et un terminateur de cadre __FINIR__Les versions 1.0.3 et suivantes ne contiennent aucune de ces fonctionnalités et accèdent à un hôte HTTPS. Une demande de retrait mentionnant uniquement les versions concernées aurait indiqué un point de terminaison TCP inactif, sans faire mention du canal HTTP actif, présent dans six versions ultérieures.
La version finale supprime la vérification du transport. La version 1.0.8 ajoute une classe implémentant javax.net.ssl.X509TrustManager dont les méthodes de vérification de certificats sont inefficaces, un vérificateur de nom d'hôte toujours fiable enregistré via définir le vérificateur de nom d'hôte par défautEt un confiance Cette routine installe les deux certificats comme valeurs par défaut de la JVM. De ce fait, pour le reste de la compilation, la JVM acceptera n'importe quel certificat provenant de n'importe quel hôte, non seulement pour le trafic de la charge utile, mais aussi pour toutes les autres opérations TLS effectuées ultérieurement par la compilation.
Le canal HTTP, dans les six versions qui l'utilisent, est géré par un seul hôte : tableful-fervor-crazed.ngrok-free[.]devChaque requête contient l'en-tête ngrok-skip-browser-warning, ce qui supprime la page interstitielle que les tunnels ngrok gratuits envoient aux navigateurs. Les ensembles de chemins changent d'une version à l'autre. /dehors disparaît à la version 1.0.7, et /cmd alterne avec /sondage — mais l'hôte ne change jamais.
L'auto-exclusion dit
Chaque fichier POM à partir de la version 1.0.1 définit un argument de compilation pour la propre construction de l'artefact :
-proc:aucun
Ce paramètre désactive le traitement des annotations. Son effet ici est précise : lorsque le projet contenant le processeur est lui-même compilé, le processeur ne s'exécute pas.
Ce paramètre a des usages tout à fait courants. Un projet qui intègre un processeur d'annotations doit souvent éviter de l'appliquer à lui-même lors de l'initialisation, et la documentation des outils de compilation le recommande précisément. Pris isolément, il ne prouve rien.
Conjuguée au fonctionnement du processeur, cette information révèle une asymétrie spécifique : le code s’exécute sur les machines de tous ceux qui compilent avec l’artefact, et non sur celle qui génère l’artefact. L’indicateur apparaît dans la version 1.0.1, qui introduit le fichier de service, et dans toutes les versions suivantes. La corrélation entre la version où l’exécution à la compilation est activée et celle où elle est désactivée localement constitue le signal analytique le plus pertinent de l’ensemble d’artefacts ; elle est visible dans un fichier POM en texte brut, sans décompilation.
Nous prenons note de l'effet et nous nous arrêtons là. Rien dans les artefacts n'explique pourquoi ce drapeau a été activé.
Une autre métadonnée mérite d'être mentionnée, principalement pour la supprimer. Le POM nomme le projet « C2 Lab Payload », le décrit comme un « artefact de charge utile du laboratoire C2 » et le licencie sous licence MIT. Ce type d'auto-étiquetage est parfois présenté comme preuve qu'un paquet est un exercice de recherche.cisIl ne s'agit pas d'une menace réelle, et cette interprétation est parfois correcte : un canari déclaré sans infrastructure accessible est différent. Cela ne s'applique pas ici. Un implant fonctionnel publié dans un dépôt public, accessible à tout utilisateur, avec une infrastructure sortante reconstruite trois fois en neuf versions, est une capacité active, indépendamment de sa dénomination dans les métadonnées. Le nom dans le POM ne change rien à ce qui se passe sur une machine qui compile avec ce composant.
Indicateurs pour les machines de construction
Si un hôte de compilation a compilé avec cet artefact, la preuve se trouve dans les journaux de compilation et la télémétrie réseau plutôt que dans un implant persistant sur le disque — la charge utile s'exécute à l'intérieur du processus du compilateur et se termine avec lui.
Dans le cache du fichier JAR ou du dépôt local
META-INF/services/javax.annotation.processing.Processornommageio.github.davidtimur.c2lab.C2Processor- Fichier de service md5
7d2a08a5c8869a47eea9fa62487dfbe4 - Classes de moins de
io/github/davidtimur/c2lab/:C2Processor,C2Task,Task, et dans la version 1.0.8, la classe interneTask$1
Sortie de compilation
[C2] compile-time execution complete[C2] callback sent → HTTP[C2] callback failed:[C2] shell connected to- bannière interactive
c2-shell; terminateur de cadre__END__
Télémétrie en cours
javacen tant que parent de/bin/sh -c(Unix) ou un interpréteur de commandes Windows- Commandes enfant
whoami,uname -a,pwd(Unix) outasklist(Windows) parenté par une étape de compilation
En télémétrie réseau
- TCP sortant vers
0.tcp.ngrok[.]io:19823(version 1.0.2) - HTTPS vers
tableful-fervor-crazed.ngrok-free[.]dev, chemins/register,/cmd,/poll,/out(versions 1.0.3 à 1.0.8) - En-tête de requête
ngrok-skip-browser-warning: true - Demander des corps correspondant
{"host":...,"os":...,"user":...,"dir":...}or{"version":...,"host":...,"time":...,"output":...}
En configuration
- Variables d'environnement
CALLBACK,CALLBACK_URL; propriété du systèmecallback.url
Métadonnées de l'éditeur
- le Groupe
io.github.davidtimur; adresse de l'éditeurdavudboi999@gmail[.]com; clé de signatureE520C345EF94423D
Une compilation effectuée avec la version 1.0.8 nécessite une vérification supplémentaire. Cette version installant un gestionnaire de confiance permissif par défaut pour l'ensemble de la JVM, toute connexion TLS établie ultérieurement dans cette même JVM (résolution de dépendances, chargement d'artefacts, déploiement) s'est déroulée sans validation de certificat. Le trafic issu de cette fenêtre d'exécution ne doit donc pas être considéré comme authentifié.
Pourquoi les artefacts compilés nécessitent une analyse différente
JavacDoor est un cas de test utile car il réfute simultanément deux hypothèses courantes, et aucune de ces défaillances n'est spécifique aux outils d'un fournisseur en particulier.
La première hypothèse est que le code dangereux s'annonce dans un fichier manifeste. Une grande partie des outils de gestion de la chaîne d'approvisionnement est organisée autour du cycle de vie. hooksEn effet, pour npm et PyPI, c'est généralement là que se déroule l'action. JavacDoor ne possède pas de hook. Son déclencheur est un fichier d'enregistrement de service dont le nom est une interface Java et le contenu un nom de classe. Pour intercepter cela statiquement, il faut traiter META-INF/services/javax.annotation.processing.Processor en tant que point d'entrée d'exécution à part entière, au même titre qu'un post-installation Le script s'exécute, puis on remonte la classe nommée jusqu'au bytecode. Les écosystèmes possèdent leurs propres mécanismes de découverte automatique de ce type, et chacun constitue un point d'entrée, qu'il soit ou non répertorié comme tel par les outils.
La seconde hypothèse est que les chaînes de caractères résident dans les fichiers sources. Pour un fichier jar, les points de terminaison, les commandes shell, les modèles JSON et les marqueurs de journalisation se trouvent tous dans les pools de constantes de .classe Les outils de recherche textuelle ne trouvent rien, non pas parce que les chaînes de caractères sont obscurcies, mais parce qu'elles se trouvent dans un conteneur binaire structuré qu'une analyse textuelle ne peut pas interpréter. Tous les indicateurs réseau de cet article proviennent de l'analyse de pools de constantes. L'un d'eux est un fichier complet. https:// L'URL est clairement visible dans un fichier de classe ; une analyse textuelle du contenu lisible du fichier JAR ne la révélerait pas. Il n'y a pas d'encodage à contourner, seulement un format de conteneur à lire.
Les deux problèmes sont similaires : un format d'artefact est traité comme un ensemble de fichiers plutôt que comme une structure dotée d'une sémantique définie. La solution est peu élégante : analyser le conteneur, recenser les points d'entrée de découverte automatique de l'écosystème et les suivre dans le code compilé. Pour les vecteurs de compilation en particulier, une troisième vérification est peu coûteuse et étonnamment révélatrice : comparer le comportement d'un artefact vis-à-vis des consommateurs avec les exceptions dont il s'exclut. Un artefact qui enregistre un processeur de compilation tout en désactivant le traitement de compilation pour sa propre compilation nous en dit long sur lui-même en deux lignes de POM en clair.
Pour les équipes qui utilisent aujourd'hui des artefacts Maven, voici trois mesures pratiques :
- Considérez le chemin du processeur d'annotations comme une limite d'exécution. Les dépendances qui s'y trouvent exécutent du code lors de votre compilation. Lorsqu'une compilation ne nécessite pas de traitement d'annotations, -proc:aucun est aussi utile en matière de défense qu'elle l'était apparemment localement ici ; le cas échéant, il faut spécifier explicitement l'ensemble de processeurs plutôt que de l'hériter du classpath de compilation.
- Sous-processus du compilateur de journalisation. Une étape de compilation qui lance un shell est anormale dans la plupart des projets et peut être facilement détectée par une alerte.
- Ne considérez pas les métadonnées comme un témoignage. Les termes « Lab », « test », « payload » et « PoC » dans le nom ou la description d’un package ne constituent pas des limites de portée. Seules la portée et le comportement le sont.
L'artefact et l'ensemble de son groupe ont été retirés de Maven Suite à notre signalement, le système Central affiche désormais une erreur 404 pour le chemin de l'artefact et celui du groupe, et l'index Central ne signale aucune correspondance. L'artefact est donc fermé. Le vecteur, quant à lui, reste ouvert ; il s'agit d'une fonctionnalité documentée du compilateur Java et accessible à toute personne publiant un fichier JAR.
Références
Cet article ne cite aucune source externe. Toutes les conclusions sont issues d'une analyse statique directe des neuf fichiers JAR publiés, récupérés sur Maven Central avant leur suppression. Aucun code provenant de ces artefacts n'a été exécuté.







