Невозможно отучить людей изучать самые ненужные предметы.
Введение в CSS
Преимущества стилей
Добавления стилей
Типы носителей
Базовый синтаксис
Значения стилевых свойств
Селекторы тегов
Классы
CSS3
Надо знать обо всем понемножку, но все о немногом.
Идентификаторы
Контекстные селекторы
Соседние селекторы
Дочерние селекторы
Селекторы атрибутов
Универсальный селектор
Псевдоклассы
Псевдоэлементы
Кто умеет, тот делает. Кто не умеет, тот учит. Кто не умеет учить - становится деканом. (Т. Мартин)
Группирование
Наследование
Каскадирование
Валидация
Идентификаторы и классы
Написание эффективного кода
Вёрстка
Изображения
Текст
Цвет
Линии и рамки
Углы
Списки
Ссылки
Дизайны сайтов
Формы
Таблицы
CSS3
HTML5
Блог для вебмастеров
Новости мира Интернет
Сайтостроение
Ремонт и советы
Все новости
Справочник от А до Я
HTML, CSS, JavaScript
Афоризмы о учёбе
Статьи об афоризмах
Все Афоризмы
Помогли мы вам |
Заголовки новостей пестрят ужасными сообщениями о том, что проблема охватывает половину компьютерного мира. А взломать через нее якобы можно всё — от сервера Minecraft твоего соседа до крупных корпораций вроде Apple.
На GitHub есть несколько репозиториев, например, Log4jAttackSurface или log4shell со списками уязвимого ПО (с блэкджеком и пруфами, разумеется!). Даже в «Википедии» уже есть статья о Log4Shell!
Давай разбираться так ли страшен черт, как его малюют, с чего все началось и почему баг получил такую огласку.
Начнем с небольшой преамбулы. Баг был обнаружен экспертом Чен Чжаоцзюнь (Zhaojun Chen) из команды Alibaba Cloud Security. Детали уязвимости были отправлены в Apache Foundation 24 ноября 2021 года. В публичный доступ они попали чуть позже — 9 декабря. В твиттере завирусился пост, в котором была пара картинок, изображающих результат успешной эксплуатации — запущенный калькулятор. На первом скрине был затерт пейлоад, но вторая картинка и кусок кода из первой намекали, где и что нужно искать. Помимо этого в посте была ссылка на pull-реквест с фиксом, прямо скажем не слишком удачным! Сейчас пост в твиттере уже удален и посмотреть можно только через Internet Archive.
В этот же день на GitHub появился PoC с деталями эксплуатации. Когда уязвимость обзавелась собственным идентификатором CVE-2021-44228, репозиторий переименовали, а затем и вовсе удалили. Как видишь, увидеть начало истории сейчас можно только благодаря архивам.
К слову, баг получил максимальный балл (10) по стандарту CVSS из‑за его простой эксплуатации, не требующей никаких прав, и серьезности последствий для атакуемой системы.
Итак, эксплоит получил распространение и начал уходить в массы, люди стали тестировать пейлоады повсеместно и обнаруживать уязвимые продукты. Давай в деталях посмотрим, в чем причина уязвимости, какие были обходы и патчи и в каких продуктах.
CVE-2021-44228 — злоумышленник, который может контролировать сообщения журнала или параметры сообщений журнала, может выполнить произвольный код, загруженный с серверов LDAP через JNDI. Проблема затрагивает версии Apache Log4j2 2.0-beta9 до 2.15.0 (за исключением исправлений безопасности 2.12.2, 2.12.3 и 2.3.1) уязвимы к удаленному выполнению произвольного кода через JNDI.
CVE-2021-45046 — злоумышленник, контролирующий через Thread Context Map (MDC) динамические данные в сообщениях журналов событий, может создать пейлоад с использованием JNDILookup, который приведет к утечке информации и удаленному выполнению кода в некоторых конфигурациях Log4j и локальному выполнению кода во всех конфигурациях. Проблема присутствует из‑за не полностью исправленной уязвимости CVE-2021-44228 в Log4j 2.15.0.
CVE-2021-45105 — из‑за проблемы неконтролируемой рекурсии, злоумышленник специально сформированным сообщением журнала событий может вызвать отказ в обслуживании. Проблема затрагивает версии Log4j2 начиная с 2.0-alpha1 и до 2.16.0 (за исключением 2.12.3 and 2.3.1).
CVE-2021-44832 — Злоумышленник, имеющий доступ к изменению настроек логирования, может создать такую конфигурацию, через которую возможно удаленное выполнение кода. Для этого используется JDBC Appender с источником данных, ссылающимся на JNDI URI. Проблема затрагивает все версии Log4j2 начиная с 2.0-beta7 и до 2.17.0.
Театр, как известно, начинается с вешалки, а тестирование уязвимости — со стенда В качестве основной системы я буду использовать Windows и IntelliJ IDEA для компиляции и отладки.
Cоздаем пустой проект на Java с использованием gradle. Добавляем в зависимости уязвимую версию Log4j, например, 2.14.1.
dependencies {
implementation 'org.apache.logging.log4j:log4j-api:2.14.1'
implementation 'org.apache.logging.log4j:log4j-core:2.14.1'
}
Потом создаем класс где аргумент, который мы передадим программе, будет логироваться.
package logger;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
public class Test {
private static final Logger logger = LogManager.getLogger(Test.class);
public static void main(String[] args) {
String msg = (args.length > 0 ? String.join(" ", args) : "");
logger.error(msg);
}
}
Теперь укажем главный класс, который должен вызываться при запуске.
plugins {
id 'java'
id 'application'
}
...
mainClassName = 'logger.Test'
Все готово, можно запускать. Параметры в логгер передаем в качестве аргументов с помощью флага --args
:
gradlew run --args='hello world'
Запуск программы для тестирования уязвимости log4shellТеперь настало время протестировать работу уязвимости, для этого возьмем простой пейлоад ${jndi:ldap://127.0.0.1/a}
и передадим его в качестве параметра. Только не забудь сначала поставить на прослушку 389 порт.
Тестирование уязвимости log4shellКоннект приходит, а это значит, что уязвимость успешно проэксплуатирована. Этого пока достаточно для дальнейшего препарирования.
Попробуем разобраться, почему эта загадочная конструкция вообще выполняется.
По сути, конструкции вида ${}
используются в динамических строках, которые преобразуются разными реализациями класса StringSubstitutor. Да не осудят меня Java сеньоры, я буду считать, что это просто переменные.
Теперь скачаем исходники нашей версии библиотеки Log4j. Интересующая нас обработка логируемого события начинается в методе format
класса MessagePatternConverter
.
... public void format(final LogEvent event, final StringBuilder toAppendTo) {
final Message msg = event.getMessage();
...
if (config != null && !noLookups) {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));}}
}
Этот цикл проверяет наличие конструкции ${
в сообщении. Если она присутствует, управление передается классу StrSubstitutor
для дальнейшей обработки.
... public static final char DEFAULT_ESCAPE = '$'; ... public static final StrMatcher DEFAULT_PREFIX = StrMatcher.stringMatcher(DEFAULT_ESCAPE + "{"); ... public static final StrMatcher DEFAULT_SUFFIX = StrMatcher.stringMatcher("}");
Здесь можно видеть инициализацию дефолтного префикса (${
) и суффикса (}
). Далее по коду видим метод substitute
.
public StrMatcher getVariablePrefixMatcher() {
return prefixMatcher;
}
...
public StrMatcher getVariableSuffixMatcher() {
return suffixMatcher;
}
private int substitute(final LogEvent event, final StringBuilder buf,
final int offset, final int length,
List<String> priorVariables) {
final StrMatcher prefixMatcher = getVariablePrefixMatcher();
final StrMatcher suffixMatcher = getVariableSuffixMatcher();
Он снова выполняет поиск таких конструкций (${ололо}
) по содержимому логируемого события, только в этот раз проверяет наличие суффикса }
, чтобы определить действительно ли нужна дальнейшая обработка.
while (pos < bufEnd) {
final int startMatchLen = prefixMatcher.isMatch(chars, pos, offset, bufEnd);
if (startMatchLen == 0) {
pos++;
} else // found variable start marker
Метод prefixMatcher.
, как видно из названия, находит начало конструкции, символы ${
. Проверка выполняется методом isMatch
.
|
|