Эта статья входит в число добротных статей

Heap spraying

Перейти к навигацииПерейти к поиску
Альтернативный текст
Расположение стека и кучи в адресном пространстве

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

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

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

Память "До" и "После" heap spraying

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

Использовать эту технику трудно, поскольку очень велико число факторов, влияющих на исполнение процесса (с точки зрения хакера). Тем не менее, с помощью heap spraying можно выполнить большое число инструкций, что частично компенсирует эту трудность и позволяет повысить вероятность успешного взлома[7].

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

Характерный вид блоков

Однако, чтобы повысить эту вероятность, необходимо чтобы размер куска выделяемой памяти был сопоставим с размером сегмента или страницы, в зависимости от способа организации памяти[7].

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

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

Шаги, направленные на реализацию heap spraying:

  • Определение размера куска выделяемой памяти таким образом, чтобы одно выделение соответствовало размеру страницы.
  • Выделение нескольких кусков, в которых располагаются NOP и shell-код.
  • Использование известной уязвимости для перенаправления счётчика команд на предполагаемое положение кусков, например, при помощи переполнения стека.
  • Исполнение выделенной области[1][6].

Этот тип атак очень эффективен в браузерах. Большинство браузеров поддерживают исполнение скриптов. Хакер может выделить необходимую память, используя несколько строк JavaScript или ActionScript на веб-странице. Важную роль играет схожесть выделения памяти в различных операционных системах, что делает эту атаку кроссплатформенной. Более того, адреса на которые необходимо совершить переход, будут похожи[9].

История

Первый раз heap spraying использовалась в 2001 году и стала широко распространённой летом 2005 года. После было найдено большое количество уязвимостей в Internet Explorer[10][11]. Эксплойты были очень похожи между собой. Каждый такой эксплойт состоял из heap spraying, метод реализации которой не изменялся, и переноса счётчика команд на необходимое место в памяти. Потому новый эксплойт получался изменением нескольких строк HTML и переключением на новую уязвимость[1].

Реализация

JavaScript

Самый простой способ выделить место в памяти браузера — декларировать строчную переменную и инициализировать её[1].

Примеры выделения памяти на JavaScript[9]:

 
		var myvar = "CORELAN!";
		var myvar2 = new String("CORELAN!");
		var myvar3 = myvar + myvar2;
		var myvar4 = myvar3.substring(0,8);

Это очень простые примеры, так как выделенные строки небольшие. Кусок shell-кода значительно больше, но всё же меньше целой страницы памяти.

Гипотетически можно записать необходимый shell-код много раз в каждый выделяемый нами блок, но тогда злоумышленнику придётся следить, на какой конкретно адрес попадает указатель, так как он не должен попасть на середину исполняемого кода. Обычно поступают по-другому — выделяют куски, содержащие множество NOPов, и в конце прописывают необходимые команды. Тогда в силу линейности расположения блоков в куче проще соблюсти линейность исполнения кода и не нужно заботиться о точности попадания на начало куска памяти[9].

При правильном выборе размера выделяемые куски памяти должны быть очень близки к размеру элемента кучи. Если выделяемый кусок памяти будет меньше, то оставшееся место будет свободно. Менеджер памяти, в лучшем случае, оставит системный мусор в этом «незанятом пространстве», а в худшем положит объект, подходящий по размеру. В любом случае это приведёт к ошибке при попытке исполнить данный участок памяти[1][9].

Таким образом, используемый злоумышленниками скрипт выглядит так[9]:

		<html>
		<script >
		var shellcode = unescape('%u\4141%u\4141'); // это надпись CORELAN
		var bigblock = unescape('%u\9090%u\9090'); //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;
        //заполнение до размера элемента кучи- в данном случае- это 0x40000
		var memory = new Array();
		for (i = 0; i < 500; i++){ memory[i] = block + shellcode }// выделение нескольких таких элементов.
		</script>
		</html>

unescape()- это функция, позволяющая положить байты именно в том порядке, в котором указано в аргументе[1].

VBScript

VBScript используют в Internet Explorer для создания строк с помощью string. Концептуально не отличается от реализации на JavaScript, только названия функций изменяются[6].

ActionScript

В июле 2009 были найдены эксплойты, позволяющие использовать ActionScript для реализации heap spraying в Adobe Flash[1].

HTML5

В сентябре 2012 на EuSecWest 2012[12] была представлена новая реализация. Федерико Муттис (Federico Muttis) и Анибаль Сакко (Anibal Sacco) показали, что heap spraying с высокой степенью гранулярности может быть реализован при помощи технологий HTML5. Они использовали низкоуровневый растровый интерфейс, предоставляемый canvas API.

Изображения

Существуют методы, использующие загрузку изображений. Изображение составляют из NOPов и далее действуют, как и в предыдущих случаях[1][9].

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

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

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

Проекты, связанные с данным типом атаки:

  • проект Nozzle от Microsoft Research ставит своей целью предотвращения heap spraying[1].
  • BuBBle — это другой проект, целью которого является минимизация урона от данной атаки[13].

См. также

Примечания

  1. 1 2 3 4 5 6 7 8 9 10 11 12 13 Benjamin Livshits, Paruj Ratanaworabhan, Benjamin Zorn. NOZZLE: A Defense Against Heap-spraying Code Injection Attacks. (англ.) : Inproceedings. — 2009. — С. 38. Архивировано 9 августа 2014 года.
  2. 1 2 Таненбаум Э., Вудхалл А. Операционные системы. Разработка и реализация. — СПб.: Питер, 2007. — С. 78-252. — 704 с.
  3. 1 2 Рихтер Дж. Windows для профессионалов: создание эффективных Win32 приложений с учетом специфики 64х разрядной версии Windows. — М.: Русская Редакция, 2008. — С. 68-118,333-418. — 720 с.
  4. 1 2 У. Ричард Стивенс, Стивен А. Раго. UNIX. Профессиональное программирование. — СПб.: Символ-Плюс, 2014. — С. 288-351. — 1104 с.
  5. Брайан Керниган, Деннис Ритчи. Язык программирования Си. — Вильямс, 2015. — С. 93-123. — 304 с.
  6. 1 2 3 4 Thomas Toth, Christopher Kruegel. Accurate buffer overflow detection via abstract payload execution (англ.) // RAID'02 Proceedings of the 5th international conference on Recent advances in intrusion detection : Proceeding. — 2002. — 16 октября. — С. 274-291. — ISBN 3-540-00020-8.
  7. 1 2 3 4 Tilo Muller. ASLR Smack & Laugh Reference (англ.). — 2008. — 17 декабря. — С. 21. Архивировано 28 сентября 2015 года.
  8. 1 2 Марк Руссинович, Дэвид Соломон. Внутреннее устройство Windows. — СПб.: Питер, 2013. — С. 104-681. — 800 с.
  9. 1 2 3 4 5 6 Sotirov A. Heap feng shui in javascript (англ.). — 2007. — С. 55. Архивировано 4 декабря 2015 года.
  10. HwaiGeeng, Chew. Security Holes in ISAPI Extensions (англ.) : Inproceedings. — 2001. — P. 12. Архивировано 22 декабря 2015 года.
  11. Black Hat. Security Holes in ISAPI Extensions (англ.) : Inproceedings. — 2010. — С. 35. Архивировано 4 марта 2016 года.
  12. Anibal Sacco, Federico Muttis. HTML5 Heap Sprays, Pwn All The Things (англ.). — 2012. Архивировано 23 декабря 2015 года.
  13. Francesco Gadaleta, Yves Younan, Wouter Joosen. BuBBle: A Javascript Engine Level Countermeasure against Heap-Spraying Attacks (англ.). — 2010. — С. 17. Архивировано 3 марта 2016 года.

Литература

  • Рихтер Дж. Windows для профессионалов: создание эффективных Win32 приложений с учетом специфики 64х разрядной версии Windows. — М.: Русская Редакция, 2008. — С. 68-118,333-418. — 720 с.
  • У. Ричард Стивенс, Стивен А. Раго. UNIX. Профессиональное программирование. — СПб.: Символ-Плюс, 2014. — С. 288-351. — 1104 с.
  • Таненбаум Э., Вудхалл А. Операционные системы. Разработка и реализация. — СПб.: Питер, 2007. — С. 78-252. — 704 с.
  • Брайан Керниган, Деннис Ритчи. Язык программирования Си. — Вильямс, 2015. — С. 93-123. — 304 с.
  • Марк Руссинович, Дэвид Соломон. Внутреннее устройство Windows. — СПб.: Питер, 2013. — С. 104-681. — 800 с.

Ссылки