Раздел: Документация
0 ... 40 41 42 43 44 45 46 ... 102 — сЧцталось, что переполнять здесь особенно и нечего. Максимум -Ли-11"1 юцт1> банальный DoS, но целенаправленно захватить управление мо*"0 :.1 .1К)зможно в силу хаотичности распределения динамических блоков .ртвои ga30BbI,", адрес блока р, вообще говоря, случаен, и за его концом мо-яо К!мЯТ"пК.Положено все что угодно, в том числе и невыделенный регион на-)Кст"1ЛтЬ 00рашение к которому приводит к немедленному исключению, ава-.завершающему программу. чрпр все это ие более чем расхожие заблуждения. Сегодня переполнена самом дс-n-г к щнамических буферов никого ие удивишь. Эта технология широко и не- лГ-спеШНО используется в качестве универсального (!) средства захвата управ-ения Нашумевший червь Slapper — один из немногих червей, поражающий UNIX-машины, — распространяется именно так. Как же такое возможно? Попробуем разобраться... Выделение и освобождение динамической памяти действительно происходит деволыто сумбурно — беспорядочно, и за концом нашего блока в произвольный момент времени может быть распложен любой другой блок. Даже при последовательном выделении нескольких блоков памяти, никто не может гарантировать, что при каждом запуске программы они будут выделяться в одном и том же порядке, поскольку это зависит от размера и порядка освобождения предыдущих выделяемых буферов. Тем по менее архитектура служебных структур данных, пронизывающая динамическую память своеобразным несущим каркасом, легко предсказуема, хотя и меняется от одной версии библиотеки компилятора к другой. Существует множество реализаций динамической памяти, и различные производители используют различные алгоритмы. Выделяемые блоки памяти могут >ытъ нанизаны и на дерево, и иа одно-, двухсвязный список, ссылки на кото-тиб М0Г"УТ ыть иРедетавлены как указателями, так и индексами, хранимыми в начале/конце каждого выделяемого блока, либо в отдельной структуре иных. Причем последний способ реализации по ряду причин встречается кРанне редко. выдел°Т1НИ слелУЮцУю организацию динамической памяти, при которой все к°торыеМЫе ЛОКИ сосЛинены посредством двухсвязных списков, указатели иа "амяти а(,ШЮЖенЬ в начале каждого блока (рис. 4.2), причем смежные блоки " "роце язэтельно должны находиться в соседних элементах списка, гак как ных операций выделения/освобождения список пеизбеж-ПсроП()1 Руется, а постоянная дефрагментация обходится довольно дорого. ,>10ка ПамятИС )а "РИводит к затиранию служебных структур следующего НамДает? g" И Как следствие — к возможности их модификации. Но что это "Раще,,,11 л°ступ к ячейкам всякого блока осуществляется по указателю, "*Указате 1I)0rPaMMc в момент его выделения, а отнюдь не но «служебпо-к>Тся Исклю1КОТО1Ы" мы собираемся затереть! Служебные указатели исполь-(."иеука- ЧИТРЛЬИ0 Функциями malloc/free (и другим подобными им). Ис-Уоще,-,, а1еля "а следующий/предыдущий блок позволяет навязать адрес выделяемого блока, например, «наложив» его на доступный нам указатель на следующий блок в цепочке указатель на предыдущий блок в цепочке размер статус (занят/свободен) блок памяти 1 память, выделенная блоку указатель на следующий блок в цепочке указатель на предыдущий блок в цепочке размер статус (занят/свободен) память, выделенная блоку блок памяти 2 Рис. 4.2. Карта приблизительного распределения динамической памяти Освобождение блоков памяти — другое дело! Для уменьшения фрагментации динамической памяти функция free автоматически объединяет текущий освобождаемый блок со следующим, если тот тоже свободен. Поскольку смежи» блоки могут находиться на различных концах связывающего их списка, перс-1 присоединением чужого блока памяти функция free должна «выбросить» е из цепочки. Это осуществляется путем «склейки» предшествующего и поь* дующего указателен, что в псевдокоде выглядит приблизительно так: *УК тель на следующий блок в цепочке = указатель на предыдущий блок в Чеп° Постойте, по ведь это... Да! Это аналог бейсик-функнии РОКЕ, позволяют» модифицировать любую ячейку уязвимой программы! Подробнее об этом можно прочитать в статье «Опсе upon a free()...»> опубл" ванной в ЗЭЬ-номере электронного журнала PHRACK, доступного по < www.phrack.org. Статья перегружена техническими подробностями реали динамической памяти в различных библиотеках и нанисанадоволыютя* языком, но ознакомится с ней, безусловно, стоит. Как правило, возможность записи в память используется для модиф1"" таблицы импорта с целью подмены некоторой API-функции, гарантия0" , вызываемой уязвимой программой вскоре после переполнения («вскор буфер, но никаких гарантий, что это получится, у нас нет. При выделении ка памяти функция malloc ищет наиболее подходящий с ее точки зрецця он свободной памяти (обычно это первый свободный блок в цепочке, сов ющий по размеру с запрошенным), н не факт, что наш регион ей под0« Короче говоря, не воодушевляющая перспектива получается.ет tomV чт0 часы ее уже сочтены — целостность ссылочного каркаса динамиче- Ю» памяти нарушена, и это неустойчивое сооружение в любой момент может хнуть, пустив программу в разнос). К сожалению, передать управление на дсреполняюшийся буфер, скорее всего, не удастся, так как его адрес наперед неизвестен и тут приходится импровизировать. Во-первых, злоумышленник может разместить shell-код в любом другом доступном ему буфере с известным адресом (см. далее раздел «В секции данных...»). Во-вторых, среди функций уязвимой программы могут встретиться и такие, что передают управление на указатель, переданный им с тем или иным аргументом (условимся называть такую функцию функцией f). После чего останется найти API-функцию, принимающую указатель на переполняющийся буфер, и подменить ее адрес адресом функции f. В Си++-нрограммах с их виртуальными функциями и указателями this такая ситуация случается не так уж и редко, хотя и распространенной ее тоже не назовешь. Но при проектировании shell-кода на универсальные решения закладываться, вообще говоря, и не приходится. Проявите инженерную смекалку, удивите мир! Будьте заранее готовы к тому, что в некоторых реализациях кучи вы встретитесь не с указателями, а с индексами, в общем случае представляющими собой относительные адреса, отсчитываемые либо от первого байта кучи, либо от текущей ячейки памяти. Последний случай встречается наиболее часто (в частности, штатная библиотека компилятора MS VC 6.0 построена именно так), поэтому имеет смысл рассмотреть его поподробнее. Как уже говорилось выше, абсолютные адреса переполняющего буфера наперед неизвестны и непредсказуемым образом изменяются под воздействием ряда обстоятельств. Адреса же ячеек, наиболее соблазнительных для модификации, напротив, абсолютны. Что делать? Можно, конечно, исследовать стратегию выделения/освобождения памяти для данного приложения на предмет выявления наиболее вероятных комбинаций — кое-какие закономерности в назначении адресов переполняющимся буферам, безусловно, есть. Методично перебирая все возможные варианты ОДШ за другим, атакующий рано или поздно захватит управление сервером ("равда, перед этим несколько раз его завесит, демаскируя атаку и усиливая бдительность администраторов). 8 СЕКЦИИ ДАННЫХ... Пополняющиеся буферы, расположенные в секции данных (статические бу-Ры) — настоящая золотая жила с точки зрения злоумышленника! Это един-Чнц""1111 ТИ" УФеРов адреса которых явно задаются еще на этане компиля-Дчя Boouie~TO не компиляции, а компоновки, но это уже детали) и постоянны како"1410" конкРет1,°й версии уязвимого приложения, независимо от того, на операционной системе она выполняется. *~амое г1-id чЧн ВНое ~ секция данных содержит огромное количество указателей на -Юв ЧНН//данные- глобальные флаги, дескрипторы файлов и кучи, имена фай-всегеКсТовые СТР0КИ буферы некоторых библиотечных функций... Правда, до *гого богатства еще предстоит «дотянуться», и, если длина переполни- 0 ... 40 41 42 43 44 45 46 ... 102
|