Heap spraying

Heap spraying (буквально — «распыление по куче») в информационной безопасности — это хакерская атака, использующая ошибки в работе с памятью приложения. При атаке heap spraying хакер заставляет приложение выделить память под большое количество объектов, содержащих вредоносный код. Таким образом повышается вероятность успеха эксплойта, который переносит поток исполнения на какую-либо позицию внутри кучи. Важно понимать, что без эксплойта, позволяющего изменять поток исполнения, heap spraying не нанесёт какого-либо вреда. Атака опирается на предсказуемость положения кучи в адресном пространстве процесса. Кроме того, выделение памяти в куче — детерминированная операция, что и позволяет успешно применять данную технику. Heap spraying особенно эффективна в браузерах, где хакер может выделять память с помощью нескольких строк JavaScript на веб-странице. Немаловажно и сходство выделения памяти в разных операционных системах, что делает этот вид атаки кроссплатформенным. В результате появляется возможность разместить определённую последовательность байт (например, машинную инструкцию) по заранее предсказанному адресу в памяти целевого процесса[1].

При создании процесса в операционной системе под его нужды выделяется адресное пространство[2][3][4], в котором располагаются пользовательские данные, исполняемый код и некоторая системная информация, зависящая от конкретной операционной системы. Пользовательские данные могут быть размещены в куче или стеке в зависимости от способа выделения памяти[5]. Так, в сегменте стека хранятся переменные с автоматическим классом размещения, а также информация, сохраняемая при каждом вызове функции, например, адрес возврата. Куча — это область динамической памяти, то есть при динамическом выделении памяти пространство выделяется именно в куче. Обычно куча и стек растут навстречу друг другу[2][3][4].

Основная концепция

undefined

Heap spraying сама по себе не является уязвимостью. Однако она может быть использована для доставки вредоносного кода в исполняемую область памяти процесса. Эта техника использует детерминированность операции выделения памяти в системе. При этом большой объём памяти часто располагается с одним и тем же смещением в адресном пространстве процесса. Однако сама по себе эта техника не создаёт брешь в системе безопасности: нужна уязвимость, позволяющая изменить порядок исполнения команд (машинных инструкций)[6].

Использование этой техники затруднено из-за множества факторов, влияющих на исполнение процесса. Однако heap spraying позволяет выполнить множество инструкций, что частично компенсирует эту сложность и увеличивает вероятность успешного взлома.

Heap spraying может реализовываться для большинства операционных систем и архитектур. Основная сложность — найти уязвимость, позволяющую перенаправить поток исполнения. Динамическое выделение большого объёма памяти, как уже упоминалось, — это операция, позволяющая с высокой вероятностью предсказать положение кучи в памяти в момент проецирования виртуальной памяти на физическую память[7]. Каждый раз исполняя одну и ту же последовательность выделения памяти, куча, скорее всего, окажется на том же самом месте[6].

undefined

Для повышения вероятности успешной атаки необходимый размер выделяемого куска памяти должен быть сопоставим с размером сегмента или страницы, в зависимости от организации памяти.

Основная проблема этой атаки — изменение потока исполнения. Без возможности перехвата управления данный тип атаки теряет смысл. Некоторые функции могут сохранять адрес возврата в куче, тогда хакер может попытаться изменить их. В этом случае при возврате из такой функции исполнение произойдёт из удобного для хакера участка памяти, что приведёт к запуску вредоносного кода. Любая функция, считывающая адрес, расположенный в куче, также может быть уязвимостью: хакер подменяет значение на нужный адрес, перенаправляя выполнение на вредоносный участок. Тем не менее, сделать это не так просто[1][7].

Корректность адреса (размер, смещение относительно страницы), используемого при подмене, сильно зависит от архитектуры. Поэтому на практике используют блоки, в которых содержатся в основном NOP-инструкции, а необходимый код размещают в конце. Такой приём позволяет не заботиться о точном попадании по адресу, а направлять уязвимый поток примерно в нужное место в адресном пространстве[1].

Общие шаги выполнения атаки heap spraying:

Этот тип атак особенно эффективен в браузерах. Большинство браузеров поддерживают выполнение скриптов. Хакер может выделять необходимые объёмы памяти, используя несколько строк 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.

Изображения

Встречаются методы, использующие загрузку изображений. Графическое изображение составляется из NOP-инструкций, а затем используется по аналогии с предыдущими случаями[1].

Способы предотвращения

Как и в случае переполнения буфера, есть три основных направления защиты[1]. Часто проще предотвратить изменение потока исполнения, чем само использование буфера. Современные операционные системы реализуют все перечисленные ниже методы:

  1. Запрет исполнения через разделение данных и исполняемого кода, обычно благодаря архитектурным механизмам, например NX bit. В большинстве ОС внедрён DEP[1].
  2. Повышение случайности расположения данных в памяти. Например, чтобы следующий элемент кучи не имел фиксированного смещения относительно предыдущего. Этот механизм — ASLR — также реализован во всех современных ОС.
  3. Увеличение числа проверок корректности работы с буфером в менеджере памяти.

Современная стратегия защиты полагается на комплексные механизмы, встроенные непосредственно в ядро операционных систем и архитектуру веб-браузеров. Специализированные исследовательские проекты прошлых лет, такие как Nozzle[1] и BuBBle[13], утратили актуальность.

Значительную роль в предотвращении атак играют аппаратные технологии, которые блокируют передачу управления на вредоносный код в куче:

  • Intel Control-Flow Enforcement Technology (CET) — использует теневой стек и отслеживание косвенных переходов[14];
  • ARM Memory Tagging Extension (MTE) — использует тегирование памяти[15].

Примечания

  1. 1 2 3 4 5 6 7 8 9 10 11 12 13 Benjamin Livshits, Paruj Ratanaworabhan, Benjamin Zorn (2009). “NOZZLE: A Defense Against Heap-spraying Code Injection Attacks” (PDF). USENIX Security Symposium [англ.]: 38. Архивировано из оригинала (PDF) 2014-08-09. Дата обращения 2026-05-28.
  2. 1 2 Таненбаум Э., Вудхалл А. Операционные системы. Разработка и реализация. — СПб. : Питер, 2007. — P. 78–252. — ISBN 978-5-469-01116-9.
  3. 1 2 Рихтер Дж. Windows для профессионалов: создание эффективных Win32 приложений с учетом специфики 64х разрядной версии Windows. — М. : Русская Редакция, 2008. — P. 68–118, 333–418.
  4. 1 2 У. Ричард Стивенс, Стивен А. Раго. UNIX. Профессиональное программирование. — СПб. : Символ-Плюс, 2014. — P. 288–351.
  5. Брайан Керниган, Деннис Ритчи. Язык программирования Си. — Вильямс, 2015. — P. 93–123.
  6. 1 2 3 4 Thomas Toth, Christopher Kruegel (2002). “Accurate buffer overflow detection via abstract payload execution”. Proceedings of the 5th international conference on Recent advances in intrusion detection (RAID'02) [англ.]: 274—291. ISBN 3-540-00020-8. Дата обращения 2026-05-28.
  7. 1 2 Марк Руссинович, Дэвид Соломон. Внутреннее устройство Windows. — СПб. : Питер, 2013. — P. 104–681.
  8. Heap Spraying in the Era of ASLR and DEP (2010). Дата обращения: 28 мая 2026.
  9. 1 2 JIT-Spray: Bypassing ASLR and DEP. arXiv (2010). Дата обращения: 28 мая 2026.
  10. JIT-Spray: Bypassing ASLR and DEP in Browsers. cs.utexas.edu (2010). Дата обращения: 28 мая 2026.
  11. Автономные эксплойты: Mythos и побег из песочницы. Habr (2026). Дата обращения: 28 мая 2026.
  12. Sandbox Escape в AI-агентах 2026. Codeby (2026). Дата обращения: 28 мая 2026.
  13. Francesco Gadaleta, Yves Younan, Wouter Joosen (2010). “BuBBle: A Javascript Engine Level Countermeasure against Heap-Spraying Attacks” (PDF) [англ.]: 17. Архивировано из оригинала (PDF) 2016-03-03. Дата обращения 2024-06-14. Используется устаревший параметр |url-status= (справка)
  14. Intel Editorial: Intel CET Answers Call to Protect Against Control-Flow Hijacking. Intel. Дата обращения: 28 мая 2026.
  15. Memory safety with Arm Memory Tagging Extension. Arm. Дата обращения: 28 мая 2026.

Литература

  • Рихтер Дж. 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.

Ссылки

Категории