Категория > Новости > Жидкий хром. Как работает баг use after free в движке Blink - «Новости»
Жидкий хром. Как работает баг use after free в движке Blink - «Новости»23-06-2023, 00:00. Автор: Bailey |
релиз браузера Chrome. В нем исправили 16 уязвимостей. Одну из них мы с тобой сегодня разберем, чтобы понять механизм возникновения таких багов и способы эксплуатации, с помощью которых злоумышленник может атаковать машину, оставшуюся без обновлений. Версия Chrome, о которой пойдет речь, — 87.0.4280.141. А интересующая нас запатченная уязвимость — CVE-2021-21112. Она касается компонента потоков компрессии в браузерном движке Blink и работает по принципу use after free. О баге сообщил исследователь YoungJoo Lee (@ashuu_lee) из компании Raon Whitehat в ноябре 2020 года через сайт bugs.chromium.org, номер отчета 1151298. Blink — это браузерный движок, на основе которого работает Chrome. А потоки сжатия — это те же веб‑потоки (web streams), но для удобства веб‑разработчиков передающиеся со сжатием. Чтобы не приходилось тянуть за проектом зависимости типа zlib, создатели Chrome решили интегрировать форматы сжатия gzip и deflate в движок Blink. По сути, это удобная обертка, трансформирующий поток с алгоритмом трансформации данных по умолчанию (или gzip, или deflate). Трансформирующий поток — это объект, содержащий два потока: читаемый (readable) и записываемый (writable). А между ними находится трансформер, который применяет заданный алгоритм к проходящим между ними данным. В статье я буду ссылаться на старые версии спецификации потоков и исходного кода. По понятным причинам исходный код с тех пор изменился. Да и спецификация тоже. СтендДля воспроизведения уязвимости понадобится стенд, состоящий из виртуальной машины и уязвимой версии Chrome. Готовую виртуальную машину можно загрузить с сайта osboxes.org. Сайт предоставляет образы виртуальных машин как для VirtualBox, так и для VMware. Я буду использовать образ Xubuntu 20 для VirtualBox. Читатель волен выбирать любой дистрибутив. Запускаем машину, обновляемся:
sudo apt update && sudo apt upgrade -y
Теперь нам нужна уязвимая версия браузера. Уязвимую версию Chrome, скомпилированную с ASan (AddressSanitizer), можно скачать с googleapis.com. В отчете об уязвимости указано название нужной сборки, а именно билд asan-linux-release-812852. Распаковываем архив:
unzip asan-linux-release-812852.zip
Готовый билд сэкономит кучу времени, так как сборка браузера требует времени, особенно если машина не очень мощная. AddressSanitizer — это детектор ошибок памяти. Он предоставляет инструментацию во время компиляции кода и библиотеку времени выполнения (runtime). Подробнее о нем можно почитать на сайте Clang. Теперь у нас готова виртуальная машина и скачан необходимый билд Chrome. Помимо них, нам понадобится Python 3 и LLVM. Обычно лог санитайзера ASan выглядит нечитаемо, поскольку там указаны только адреса и смещения. Разобраться поможет утилита llvm-symbolizer, которая устанавливается вместе с LLVM. Она читает эти адреса и смещения и выводит соответствующие места в исходном коде. Лог ASan будет выглядеть намного понятнее. Ну а Python поможет нам готовить данные для сжатия. Все установлено, теперь в бой! ТеорияПрежде чем разбираться в деталях уязвимости, нам нужно немного понимать предметную область. Предыстория всего этого такова. В конце 2019 года команда разработчиков Chromium реализовала новый jаvascript API, который называется Compression Streams. Детали реализации приведены в отчете. Этот API основан на спецификации потоков (спецификация от 30 января 2020 года). Подробно с его концепцией можешь ознакомиться в дизайн‑документе, дополнительные пояснения смотри на GitHub. Я привожу более старые версии, так как уязвимость касалась именно их реализации. В дальнейшем спецификация потоков и их реализация в Chromium изменилась. Теперь разберемся в потоках преобразования, потоках сжатия, объектах promise и методе Потоки сжатияПотоки сжатия основаны на концепции и реализации веб‑потоков. Отличие в том, что потоки компрессии могут сжимать и распаковывать данные. На выбор — алгоритмы gzip и deflate, широко применяемые в веб‑технологиях. Потоки компрессии удовлетворяют спецификации transform stream. Ниже приведена схема алгоритма. Грубо говоря, если данные не кончились (считан чанк), то вызывается метод То есть обрабатываем куски данных и кладем их в очередь. Promisejаvascript часто описывают как язык прототипного наследования. Каждый объект имеет объект‑прототип — шаблон методов и свойств. Все объекты имеют общий прототип Поэтому при изменении каких‑то свойств или методов прототипа новые объекты будут обладать измененными свойствами или методами. Далее нас интересуют асинхронное программирование и «обещания» (promise). Раньше jаvascript исполнялся синхронно, но это мешало веб‑страницам быстро загружаться и плавно работать. Асинхронное программирование позволяет обойти эту проблему. При ожидании какой‑то операции (загрузки данных по сети, чтения с диска и тому подобных) основной поток приложения не блокируется, и оно не подвисает. Сначала в jаvascript внедрили асинхронные колбэки (вызовы функций по завершении операции). Позднее придумали новый стиль написания асинхронного кода — «обещания». Promise — это объект, представляющий асинхронную операцию, выполненную удачно или неудачно. На картинке это наглядно изображено. Источник — jаvascript.ru Промис — это как бы промежуточное состояние: «Я обещаю вернуться к вам с результатом как можно скорее». У объекта promise есть метод Особенность jаvascript в том, что в этом языке все является объектом. По сути метод или функция — это тоже объект. И доступ к нему — это вызов объектов Особенность объекта promise в том, что при его разрешении (resolve) необходимо вызвать
Object.defineProperty(Object.prototype, "then", {
get() {
console.log("then getter executed");
}
});
postMessageКак мы можем узнать из MDN Web Docs, этот метод позволяет обмениваться данными между объектами типа
postMessage(message, targetOrigin, transfer);
После вызова функции владение Если вкратце, суть уязвимости в том, что обработка большого массива данных происходит в цикле и при добавлении обработанных чанков в очередь есть возможность вызвать пользовательский код на JS. Это обеспечено тем, что объекту promise дано разрешение на чтение из потока. Пользовательский код через Для более детального понимания всех концепций можно обратиться к спецификации. Мы же переходим к практике. Запуск PoCПервым делом нужно LLVM, так как с ним поставляется симболизатор. Без него стек вызовов будет выглядеть непонятно, поскольку не будет названий методов и имен файлов. Перейти обратно к новости |