
Исправление на основе Java-агента Byte Buddy для CVE-2021-44228, уязвимости log4j 2.x «JNDI LDAP».
Исправление на основе Java-агента Byte Buddy для CVE-2021-44228, уязвимости log4j 2.x «JNDI LDAP».
Оно делает три вещи:
jndi: («lookups»).System.err (т.е. stderr), указывающее, что была предпринята попытка log4j JNDI (включая строку формата, с санитизацией всех символов ${} для предотвращения транзитивных инъекций)."(log4j jndi disabled)" в сообщении журнала (для предотвращения транзитивных инъекций).Добавьте -javaagent:path/to/log4j-jndi-be-gone-1.0.0-standalone.jar в ваши команды java.
Примечание: Если у вас уже есть Byte Buddy в classpath, попробуйте использовать
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
Начиная с версии 1.1.0, log4j-jndi-be-gone по умолчанию пытается обрабатывать переупакованные (так называемые «shaded») версии log4j, которые могут быть встроены в JAR под альтернативным именем пакета, чтобы предотвратить конфликты между версией зависимости в приложении и версией той же зависимости в другой зависимости. Однако следует отметить, что log4j, похоже, нелегко переупаковывается под альтернативными именами/префиксами пакетов из-за использования рефлексии со статическими именами классов и/или именами классов из встроенных файлов конфигурации.
Это поведение можно отключить, указав =structureMatch=0 после пути к JAR-файлу агента
в аргументе -javaagent:, например:
-javaagent:path/to/log4j-jndi-be-gone-1.0.0-standalone.jar=structureMatch=0
что приведёт к тому же поведению сопоставления, что и в 1.0.0 — простому точному сравнению строк с именем класса.
Вы можете собрать JAR с помощью ./gradlew (build/libs/log4j-jndi-be-gone-1.0.0(-standalone).jar)
или скачать его со страницы релизов.
JAR-файл агента log4j-jndi-be-gone поддерживает Java 6-17+.
Реализация начинает с сопоставления классов, чьи суффиксы совпадают с самыми внутренними
подпакетами и именем класса org.apache.logging.log4j.core.lookup.JndiLookup,
lookup.JndiLookup, поскольку можно обоснованно ожидать, что org.apache.logging.log4j.core
был изменён правилами переупаковки, которые не стремились сохранить имена пакетов. Кроме того,
вместо простого выполнения аналогичных проверок для всех остальных ожидаемых типов log4j,
она гарантирует, что они также находятся в том же базовом пакете.
Затем реализация проходит по структуре всех идентифицированных потенциальных классов log4j
lookup.JndiLookup, пытаясь проверить их на соответствие:
org.apache.logging.log4j.core.config.plugins.Plugin, ожидаемой во всех версиях 2.x, включая параметры аннотации и их значенияlookup(), сопоставляя его модификаторы и сигнатуру типа (и игнорируя версию с 1 аргументом из 2.0)convertJndiName(), сопоставляя его модификаторы и сигнатуру типаCONTAINER_JNDI_RESOURCE_PATH_PREFIX, сопоставляя его модификаторыlog4j-jndi-be-gone не будет работать, если библиотека log4j была обфусцирована или если её пакеты/имена классов были изменены иначе, чем базовая переупаковка (т.е. «shading»).
log4j-jndi-be-gone-1.0.0-standalone.jar включает в себя Byte Buddy. Если вы
уже используете Byte Buddy, у вас могут возникнуть с ним проблемы. Вместо этого
попробуйте Начиная с версии 1.1.0, автономный JAR
log4j-jndi-be-gone включает переупакованный Byte Buddy под собственным префиксом пакета.
Это должно предотвратить любые конфликты.log4j-jndi-be-gone-1.0.0.jar, хотя учтите, что log4j-jndi-be-gone
ожидает Byte Buddy 1.12.x.
Если вы заменили свои классы JndiLookup реализациями, которые пытаются
создавать ловушки (honeypotting) или логировать вызовы lookup(), log4j-jndi-be-gone
потенциально отключит их метод lookup, не давая им работать.
В каталоге tests/jnditest есть простой тестовый пример, где вызов логирования log4j
передаёт строку формата JNDI LDAP. Он также настраивает собственный прослушиватель порта,
чтобы определить, была ли предпринята попытка подключения со стороны log4j, и завершает
тест ошибкой, если подключение было получено.
$ ./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