Heap spraying
Heap spraying (буквально — «распыление по куче») в информационной безопасности — это хакерская атака, использующая ошибки в работе с памятью приложения. При атаке heap spraying хакер заставляет приложение выделить память под большое количество объектов, содержащих вредоносный код. Таким образом повышается вероятность успеха эксплойта, который переносит поток исполнения на какую-либо позицию внутри кучи. Важно понимать, что без эксплойта, позволяющего изменять поток исполнения, heap spraying не нанесёт какого-либо вреда. Атака опирается на предсказуемость положения кучи в адресном пространстве процесса. Кроме того, выделение памяти в куче — детерминированная операция, что и позволяет успешно применять данную технику. Heap spraying особенно эффективна в браузерах, где хакер может выделять память с помощью нескольких строк JavaScript на веб-странице. Немаловажно и сходство выделения памяти в разных операционных системах, что делает этот вид атаки кроссплатформенным. В результате появляется возможность разместить определённую последовательность байт (например, машинную инструкцию) по заранее предсказанному адресу в памяти целевого процесса[1].
При создании процесса в операционной системе под его нужды выделяется адресное пространство[2][3][4], в котором располагаются пользовательские данные, исполняемый код и некоторая системная информация, зависящая от конкретной операционной системы. Пользовательские данные могут быть размещены в куче или стеке в зависимости от способа выделения памяти[5]. Так, в сегменте стека хранятся переменные с автоматическим классом размещения, а также информация, сохраняемая при каждом вызове функции, например, адрес возврата. Куча — это область динамической памяти, то есть при динамическом выделении памяти пространство выделяется именно в куче. Обычно куча и стек растут навстречу друг другу[2][3][4].
Основная концепция
Heap spraying сама по себе не является уязвимостью. Однако она может быть использована для доставки вредоносного кода в исполняемую область памяти процесса. Эта техника использует детерминированность операции выделения памяти в системе. При этом большой объём памяти часто располагается с одним и тем же смещением в адресном пространстве процесса. Однако сама по себе эта техника не создаёт брешь в системе безопасности: нужна уязвимость, позволяющая изменить порядок исполнения команд (машинных инструкций)[6].
Использование этой техники затруднено из-за множества факторов, влияющих на исполнение процесса. Однако heap spraying позволяет выполнить множество инструкций, что частично компенсирует эту сложность и увеличивает вероятность успешного взлома.
Heap spraying может реализовываться для большинства операционных систем и архитектур. Основная сложность — найти уязвимость, позволяющую перенаправить поток исполнения. Динамическое выделение большого объёма памяти, как уже упоминалось, — это операция, позволяющая с высокой вероятностью предсказать положение кучи в памяти в момент проецирования виртуальной памяти на физическую память[7]. Каждый раз исполняя одну и ту же последовательность выделения памяти, куча, скорее всего, окажется на том же самом месте[6].
Для повышения вероятности успешной атаки необходимый размер выделяемого куска памяти должен быть сопоставим с размером сегмента или страницы, в зависимости от организации памяти.
Основная проблема этой атаки — изменение потока исполнения. Без возможности перехвата управления данный тип атаки теряет смысл. Некоторые функции могут сохранять адрес возврата в куче, тогда хакер может попытаться изменить их. В этом случае при возврате из такой функции исполнение произойдёт из удобного для хакера участка памяти, что приведёт к запуску вредоносного кода. Любая функция, считывающая адрес, расположенный в куче, также может быть уязвимостью: хакер подменяет значение на нужный адрес, перенаправляя выполнение на вредоносный участок. Тем не менее, сделать это не так просто[1][7].
Корректность адреса (размер, смещение относительно страницы), используемого при подмене, сильно зависит от архитектуры. Поэтому на практике используют блоки, в которых содержатся в основном NOP-инструкции, а необходимый код размещают в конце. Такой приём позволяет не заботиться о точном попадании по адресу, а направлять уязвимый поток примерно в нужное место в адресном пространстве[1].
Общие шаги выполнения атаки heap spraying:
- Определение размера выделяемой области памяти так, чтобы каждое выделение соответствовало размеру страницы.
- Выделение нескольких блоков, содержащих NOP-инструкции и шелл-код.
- Использование известной уязвимости для перенаправления счётчика команд на потенциальное расположение блоков (например, через переполнение стека).
- Исполнение выделенной области[1][6].
Этот тип атак особенно эффективен в браузерах. Большинство браузеров поддерживают выполнение скриптов. Хакер может выделять необходимые объёмы памяти, используя несколько строк JavaScript или ActionScript на веб-странице. Значимую роль играет схожий механизм выделения памяти в разных операционных системах, что делает такую атаку кроссплатформенной. Более того, адреса перехода окажутся похожими.
Для точного контроля над памятью и обхода защитных механизмов операционной системы Windows, таких как ASLR и DEP, может использоваться низкоуровневая функция API VirtualAlloc. Она позволяет запрашивать страницы памяти с правами на исполнение (например, с флагом PAGE_EXECUTE_READWRITE), что делает выделенную область кучи исполняемой и нейтрализует DEP. Кроме того, выделение очень больших смежных блоков памяти с помощью этой функции повышает предсказуемость расположения вредоносного кода, способствуя обходу ASLR[8].
История
Впервые heap spraying была использована в 2001 году и получила широкое распространение летом 2005 года. Позже было обнаружено множество уязвимостей в Internet Explorer. Эксплойты были схожи друг с другом: каждый состоял из heap spraying (способ реализации практически не менялся) и переноса счётчика команд на нужный участок памяти. Таким образом, очередной эксплойт представлял собой изменение нескольких строк HTML с переключением на новую уязвимость[1].
В период с 2010 по 2020 год для обхода защитных механизмов, таких как DEP и ASLR, стала применяться техника JIT-spraying, которая активно использовалась в эксплойт-китах против Internet Explorer и Adobe Flash Player[9][10].
В 2020-х годах для автоматизации поиска уязвимостей и генерации сложных эксплойтов, способных обходить «песочницы», стали применяться системы искусственного интеллекта (например, модель Mythos)[11][12].
Реализация
JavaScript
Самый простой способ выделить место в памяти браузера — объявить строковую переменную и инициализировать её[1].
Примеры выделения памяти на JavaScript:
var myvar = "CORELAN!";
var myvar2 = new String("CORELAN!");
var myvar3 = myvar + myvar2;
var myvar4 = myvar3.substring(0,8);
Это простые примеры, поскольку строки малы. Блок с шелл-кодом обычно гораздо больше, но спектр его размера всё равно не достигает полной страницы памяти.
Теоретически можно поместить необходимый шелл-код во все выделяемые блоки, но так злоумышленнику придётся следить, чтобы указатель не указывал на середину исполняемого кода. Обычно поступают иначе — выделяют блоки с множеством NOP-инструкций и размещают команды в конце. Благодаря линейности размещения в куче соблюдается линейность исполнения, и точность попадания на начало кода уже не критична.
При правильном подборе размера выделяемого блока число выделенных блоков должно быть максимально близко к размеру элемента кучи. Если блок меньше, оставшееся место будет не занято: менеджер памяти либо оставит «мусор», либо положит другой объект подходящего размера, что приведёт к ошибке при исполнении сегмента памяти[1].
Типовой скрипт, применяемый злоумышленниками:
<html>
<script>
var shellcode = unescape('%u4141%u4141'); // это строка CORELAN
var bigblock = unescape('%u9090%u9090'); // 90 — инструкция NOP
var headersize = 20;
var slackspace = headersize + shellcode.length; // начальный размер блока: полезный код + размер заголовка
while (bigblock.length < slackspace) bigblock += bigblock; // заполнение NOP-ами
var fillblock = bigblock.substring(0,slackspace); // наполнение полезным кодом
var block = bigblock.substring(0,bigblock.length - slackspace); // только NOP-ы
while (block.length + slackspace < 0x40000) block = block + block + fillblock; // доведение до размера кучи
var memory = new Array();
for (i = 0; i < 500; i++){ memory[i] = block + shellcode } // выделение нескольких блоков.
</script>
</html>
unescape() — функция, позволяющая разместить байты в нужном порядке[1].
Эволюцией классического метода стала техника JIT-Spray, при которой злоумышленники используют скрипты для того, чтобы заставить JIT-компилятор сгенерировать вредоносный машинный код в исполняемой области памяти. Это позволяет обходить защиту DEP и ASLR[9].
VBScript
VBScript используется в Internet Explorer для создания строк посредством string. Идея реализации аналогична JavaScript, но с разным набором функций[6].
ActionScript
В июле 2009 года были обнаружены эксплойты, позволяющие использовать ActionScript для реализации heap spraying в Adobe Flash[1].
HTML5
В сентябре 2012 года на конференции EuSecWest 2012 Федерико Муттис и Анибаль Сакко показали, что heap spraying с высокой детализацией может быть реализована с помощью технологий HTML5. В работе демонстрировался низкоуровневый растровый интерфейс canvas API.
Способы предотвращения
Как и в случае переполнения буфера, есть три основных направления защиты[1]. Часто проще предотвратить изменение потока исполнения, чем само использование буфера. Современные операционные системы реализуют все перечисленные ниже методы:
- Запрет исполнения через разделение данных и исполняемого кода, обычно благодаря архитектурным механизмам, например NX bit. В большинстве ОС внедрён DEP[1].
- Повышение случайности расположения данных в памяти. Например, чтобы следующий элемент кучи не имел фиксированного смещения относительно предыдущего. Этот механизм — ASLR — также реализован во всех современных ОС.
- Увеличение числа проверок корректности работы с буфером в менеджере памяти.
Современная стратегия защиты полагается на комплексные механизмы, встроенные непосредственно в ядро операционных систем и архитектуру веб-браузеров. Специализированные исследовательские проекты прошлых лет, такие как Nozzle[1] и BuBBle[13], утратили актуальность.
Значительную роль в предотвращении атак играют аппаратные технологии, которые блокируют передачу управления на вредоносный код в куче:
Примечания
Литература
- Рихтер Дж. Windows для профессионалов: создание эффективных Win32 приложений с учетом специфики 64х разрядной версии Windows. — М. : Русская Редакция, 2008. — P. 68–118, 333–418.
- У. Ричард Стивенс, Стивен А. Раго. UNIX. Профессиональное программирование. — СПб. : Символ-Плюс, 2014. — P. 288–351.
- Таненбаум Э., Вудхалл А. Операционные системы. Разработка и реализация. — СПб. : Питер, 2007. — P. 78–252. — ISBN 978-5-469-01116-9.
- Брайан Керниган, Деннис Ритчи. Язык программирования Си. — Вильямс, 2015. — P. 93–123.
- Марк Руссинович, Дэвид Соломон. Внутреннее устройство Windows. — СПб. : Питер, 2013. — P. 104–681.
Ссылки
- Exploit Writing Tutorial (англ.). Corelan Team. Дата обращения: 14 июня 2024. Архивировано 25 апреля 2015 года.
- Fundamentals of Garbage Collection (англ.). MSDN. Дата обращения: 14 июня 2024. Архивировано 22 декабря 2015 года.
- Запуск процессов с помощью вызова exec(). OpenNET. Дата обращения: 14 июня 2024. Архивировано 22 декабря 2015 года.
- Hungpages (англ.). Debian. Дата обращения: 14 июня 2024. Архивировано 22 декабря 2015 года.
- HTML5 Heap spray (англ.). EUSecWest 2012. Дата обращения: 14 июня 2024. Архивировано 22 декабря 2015 года.