
Una solución basada en agente Java de Byte Buddy para CVE-2021-44228, la vulnerabilidad "JNDI LDAP" de log4j 2.x.
Una corrección basada en agente Java Byte Buddy para CVE-2021-44228, la vulnerabilidad "JNDI LDAP" de log4j 2.x.
Hace tres cosas:
jndi: ("lookups").System.err (es decir, stderr) indicando que se ha realizado un intento JNDI de log4j (incluyendo la cadena de formato intentada, con cualquier carácter ${} saneado para prevenir inyecciones transitivas)."(log4j jndi disabled)" en el mensaje de registro (para prevenir inyecciones transitivas).Añade -javaagent:path/to/log4j-jndi-be-gone-1.0.0-standalone.jar a tus comandos java.
Nota: Si ya tienes Byte Buddy en el classpath, prueba a usar 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
A partir de la versión 1.1.0, log4j-jndi-be-gone intenta por defecto manejar versiones reempaquetadas (también conocidas como "shaded") de log4j que pueden estar incrustadas en un JAR bajo un nombre de paquete alternativo para evitar colisiones entre la versión de una dependencia de una aplicación y la versión de la misma dependencia de otra dependencia. Sin embargo, debe tenerse en cuenta que log4j parece no reempaquetarse fácilmente bajo nombres/prefixos de paquete alternativos debido al uso de reflexión con nombres de clase estáticos y/o nombres de clase de archivos de configuración incrustados.
Este comportamiento se puede deshabilitar colocando =structureMatch=0 después de la ruta del JAR del agente en el argumento -javaagent:, por ejemplo:
-javaagent:path/to/log4j-jndi-be-gone-1.0.0-standalone.jar=structureMatch=0
lo que dará como resultado el mismo comportamiento de coincidencia que 1.0.0: una simple comparación exacta de cadenas contra el nombre de la clase.
Puedes compilar el JAR con ./gradlew (build/libs/log4j-jndi-be-gone-1.0.0(-standalone).jar) u obtenerlo desde la página de lanzamientos.
El JAR del agente log4j-jndi-be-gone es compatible con Java 6-17+.
La implementación comienza buscando coincidencias con clases cuyos sufijos coincidan con los subpaquetes más internos y el nombre de clase de org.apache.logging.log4j.core.lookup.JndiLookup, lookup.JndiLookup, ya que es razonable esperar que org.apache.logging.log4j.core haya sido alterado por reglas de reempaquetado que no buscaban preservar los nombres de paquete. Además, en lugar de simplemente realizar comprobaciones similares para todos los demás tipos esperados de log4j, se asegura de que también existan bajo el mismo paquete base.
Luego, la implementación recorre la estructura de cualquier clase lookup.JndiLookup potencialmente identificada como log4j, intentando validar contra:
org.apache.logging.log4j.core.config.plugins.Plugin esperada en todas las versiones 2.x, incluidos los parámetros de la anotación y sus valoreslookup(), comparando sus modificadores y firma de tipo (e ignorando la versión de 1 argumento de 2.0)convertJndiName(), comparando sus modificadores y firma de tipoCONTAINER_JNDI_RESOURCE_PATH_PREFIX, comparando sus modificadoreslog4j-jndi-be-gone no funcionará si la librería log4j ha sido ofuscada o si sus paquetes/nombres de clase han sido modificados más allá de un reempaquetado básico (es decir, "shading").
log4j-jndi-be-gone-1.0.0-standalone.jar incluye Byte Buddy. Si ya usas Byte Buddy, es posible que tengas problemas con él. Prueba en su lugar A partir de la versión 1.1.0, el JAR independiente de log4j-jndi-be-gone incluye un Byte Buddy reempaquetado bajo su propio prefijo de paquete. Esto debería prevenir cualquier colisión.log4j-jndi-be-gone-1.0.0.jar, aunque ten en cuenta que log4j-jndi-be-gone espera Byte Buddy 1.12.x.
Si has reemplazado tus clases JndiLookup con implementaciones que intentan hacer honeypotting o registrar llamadas a lookup(), log4j-jndi-be-gone potencialmente deshabilitará su método lookup, impidiendo que funcionen.
El directorio tests/jnditest contiene un caso de prueba simple donde una llamada de registro de log4j pasa una cadena de formato JNDI LDAP. También configura su propio listener de puerto para determinar si log4j realizó un intento de conexión y falla la prueba si se recibe una conexión.
$ ./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)
Licenciado bajo la licencia Apache 2.
log4j-jndi-be-gone ha sido probado en OpenJDK 6, 8, 11 y 17, y en las JVM HotSpot y OpenJ9.