
Un correctif basé sur un agent Java Byte Buddy pour CVE-2021-44228, la vulnérabilité « JNDI LDAP » de log4j 2.x.
Un correctif basé sur un agent Java Byte Buddy pour CVE-2021-44228, la vulnérabilité « JNDI LDAP » de log4j 2.x.
Il fait trois choses :
jndi: (« lookups »).System.err (c'est-à-dire stderr) indiquant qu'une tentative JNDI log4j
a été effectuée (y compris la chaîne de format tentée, avec tout caractère ${}
assaini pour empêcher les injections transitives)."(log4j jndi disabled)" dans le message de journal
(pour empêcher les injections transitives).Ajoutez -javaagent:path/to/log4j-jndi-be-gone-1.0.0-standalone.jar à vos commandes java.
Remarque : Si vous avez déjà Byte Buddy dans le classpath, essayez d'utiliser
log4j-jndi-be-gone-1.0.0.jar.
$ java -javaagent:path/to/log4j-jndi-be-gone-1.0.0-standalone.jar -jar path/to/some.jar
À partir de la version 1.1.0, log4j-jndi-be-gone tente par défaut de gérer les versions reconditionnées (ou « shaded ») de log4j qui peuvent être intégrées dans un JAR sous un nom de package alternatif afin d'éviter les collisions entre la version d'une dépendance d'une application et la version de la même dépendance d'une dépendance. Cependant, il convient de noter que log4j ne semble pas facile à reconditionner sous des noms/préfixes de packages alternatifs en raison de l'utilisation de la réflexion avec des noms de classes statiques et/ou des noms de classes issus de fichiers de configuration intégrés.
Ce comportement peut être désactivé en plaçant =structureMatch=0 après le chemin du JAR de l'agent
dans l'argument -javaagent:, par exemple :
-javaagent:path/to/log4j-jndi-be-gone-1.0.0-standalone.jar=structureMatch=0
ce qui donnera le même comportement de correspondance que la version 1.0.0, une simple comparaison de chaîne exacte avec le nom de la classe.
Vous pouvez construire le JAR avec ./gradlew (build/libs/log4j-jndi-be-gone-1.0.0(-standalone).jar)
ou le télécharger depuis la page des versions.
Le JAR de l'agent log4j-jndi-be-gone prend en charge Java 6-17+.
L'implémentation commence par rechercher les classes dont les suffixes correspondent
aux sous-packages les plus internes et au nom de classe de
org.apache.logging.log4j.core.lookup.JndiLookup,
lookup.JndiLookup, car il est raisonnable de s'attendre à ce que
org.apache.logging.log4j.core ait été modifié par des règles de reconditionnement qui ne cherchaient pas à
préserver les noms de packages. De plus, au lieu de simplement effectuer des vérifications similaires
pour tous les autres types log4j attendus, elle garantit qu'ils existent également sous le même
package de base.
L'implémentation parcourt ensuite la structure de toute classe log4j lookup.JndiLookup
potentiellement identifiée, en essayant de valider :
org.apache.logging.log4j.core.config.plugins.Plugin attendue dans toutes les versions 2.x, y compris les paramètres de l'annotation et leurs valeurslookup(), en vérifiant ses modificateurs et sa signature de type (et en ignorant la version à 1 argument de la 2.0)convertJndiName(), en vérifiant ses modificateurs et sa signature de typeCONTAINER_JNDI_RESOURCE_PATH_PREFIX, en vérifiant ses modificateurslog4j-jndi-be-gone ne fonctionnera pas si la bibliothèque log4j a été obfusquée ou si ses packages/noms de classes ont été modifiés autrement que par un reconditionnement de base (c'est-à-dire « shading »).
log4j-jndi-be-gone-1.0.0-standalone.jar inclut Byte Buddy. Si vous
utilisez déjà Byte Buddy, vous pourriez rencontrer des problèmes avec. Essayez
plutôt À partir de la version 1.1.0, le JAR
standalone log4j-jndi-be-gone inclut un Byte Buddy reconditionné sous son propre préfixe de package.
Cela devrait éviter toute collision.log4j-jndi-be-gone-1.0.0.jar, mais notez que log4j-jndi-be-gone
attend Byte Buddy 1.12.x.
Si vous avez remplacé vos classes JndiLookup par des implémentations qui
tentent de faire du honeypotting ou de journaliser les appels lookup(), log4j-jndi-be-gone
désactivera potentiellement leur méthode lookup, les empêchant de fonctionner.
Le répertoire tests/jnditest contient un cas de test simple où un appel de journalisation log4j
passe une chaîne de format JNDI LDAP. Il configure également son propre
écouteur de port pour déterminer si une tentative de connexion a été faite par log4j et fait échouer
le test si une connexion est reçue.
$ ./tests/jnditest/test-uninstrumented.sh
BUILD SUCCESSFUL in 1s
6 actionable tasks: 5 executed, 1 up-to-date
BUILD SUCCESSFUL in 1s
3 actionable tasks: 3 up-to-date
JUnit version 4.12
.16:08:49.547 [main] ERROR trust.nccgroup.jnditest.test.JndiTest - Hello, _${jndi:ldap://127.0.0.1:8899/evil}_!
E
Time: 0.929
There was 1 failure:
1) logging(trust.nccgroup.jnditest.test.JndiTest)
java.lang.AssertionError: jndi ldap connection received
at org.junit.Assert.fail(Assert.java:88)
at trust.nccgroup.jnditest.test.JndiTest.logging(JndiTest.java:55)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:568)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
at org.junit.runners.Suite.runChild(Suite.java:128)
at org.junit.runners.Suite.runChild(Suite.java:27)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
at org.junit.runners.Suite.runChild(Suite.java:128)
at org.junit.runners.Suite.runChild(Suite.java:27)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
at org.junit.runner.JUnitCore.run(JUnitCore.java:115)
at org.junit.runner.JUnitCore.runMain(JUnitCore.java:77)
at org.junit.runner.JUnitCore.main(JUnitCore.java:36)
at trust.nccgroup.jnditest.Main.main(Main.java:24)
FAILURES!!!
Tests run: 1, Failures: 1
$ ./tests/jnditest/test-instrumented.sh
BUILD SUCCESSFUL in 1s
6 actionable tasks: 5 executed, 1 up-to-date
BUILD SUCCESSFUL in 1s
3 actionable tasks: 3 up-to-date
JUnit version 4.12
.log4j jndi lookup attempted: (sanitized) ldap://127.0.0.1:8899/evil
16:09:06.064 [main] ERROR trust.nccgroup.jnditest.test.JndiTest - Hello, _(log4j jndi disabled)_!
Time: 1.362
OK (1 test)
Sous licence Apache 2.
log4j-jndi-be-gone a été testé sur OpenJDK 6, 8, 11 et 17, ainsi que sur les JVM HotSpot et OpenJ9.