Раздел: Документация
0 ... 41 42 43 44 45 46 47 ... 102 140Глава 4. Ошибки переполнения буфера извне и ющегося буфера окажется жестко ограничена сверху (как часто и Cj1v атакующий не получит от последнего никаких преимуществ!" <К!ТсЧ К тому же, если стек и куча гарантированно содержат указатели в о ленных местах и поддерживают более или менее универсальные Механ1 захвата управления, то в случае со статическими буферами атакующему ется надеяться лишь на удачу. А удача, как известно, баба подлая и полнения статических буферов носят единичный характер, всегда рач ющийся по уникальному сценарию, ие допускающему обобщающе классификации." СЕКРЕТЫ ПРОЕКТИРОВАНИЯ SHELL-КОДА Попытка реализовать собственный shell-код неминуемо наталкивает атаку-ющего на многочисленные ограничения, одни из которых обходятся путем хитроумных хаков и извращений, с другими же приходится мириться, воспринимая их как неотъемлемую часть жестоких сил природы. ЗАПРЕЩЕННЫЕ СИМВОЛЫ Строковые переполняющиеся буферы (в особенности те, что относятся к консольному вводу и клавиатуре) налагают жесткие ограничения на ассортимент своего содержимого. Самое неприятное из которых заключается в том, что символ ноля на всем протяжении строки может встречаться лишь однажды и лишь на конце строки (правда, это ограничение ие распространяется па UNICODE-строкн). Это затрудняет подготовку shell-кода шфе иятствует выбору произвольных целевых адресов. Код, пе используют»1 нулевых байт, принято называть Zero Free кодом, и техника его подготов ки — настоящая Кама-Сутра. ИСКУССТВО ЗАТИРАНИЯ АДРЕСОВ Рассмотрим ситуацию, когда следом за переполняющимся буфером lUlC(.v. вимый указатель на вызываемую функцию (или указатель tnisal"nni) Поющая злоумышленника функция root располагается но адресу 0040 скольку только один символ, затирающий указатель, может быть с ноля, то непосредственная запись требуемого значения невозможна дится хитрить.ч.)(-г Начнем с того, что в 32-разрядных операционных системах (к которЫ*1 ности, принадлежит Windows NT и многие клоны LNIXa) стек, gjflOOl11 большинства приложений лежат в узком диапазоне адресов: OOlOOOOOh- то есть как минимум один ноль у нас уже есть — и это старший 0 В зависимости от архитектуры процессора он может располагаться каь ff(f шим, так и по старшим адресам. Семейство х86-цроцессоров держ,Т и* ших адресах, что, с точки зрения атакующего, очень даже хорош0-1 141 ю- jgigHMgjhell-KQfla навязать уязвимому приложению любой XxYyZzh-адрес при ус/ чм M()>lcCvv Vv и Zz не равны нолю. ЧТО " тйте рассуждать творчески: позарез необходимый нам адрес 401000h ГеперьД иед0стижим в принципе. Но, может быть, нас устроит что-ни-, прямом аПример, почему бы не начать выполнение функции не с перво-удьЛр. функЦИИ с классическим прологом (коих вокруг нас большинство) ,„ байта. иНСТрукцин PUSH EBP, сохраняющей значение регистра ЕВР в сте-"есчИ этого не сделать, то при выходе функция непременно грохнется, но... Kl „бучет неважно (свою миссию функция выполнила, и все, что было нужно тотему, она сделала). Хуже, если паразитный символ ноля встречается иередпие адреса или присутствует в нем дважды, например — 50000И. Н некоторых случаях помогает способ коррекции существующих адресов. Допустим, затираемый указатель содержит адрес 5000FAh. Тогда для достижения желаемого результата атакующий должен затереть один лишь младший символ адреса, заменив FAh символом ноля. Как вариант можно попробовать поискать в дизассемблерпом листинге коман-лу перехода (вызова) интересующей нас функции, — существует вероятность, что она булет располагаться по «правильным» адресам. При условии, что целевая функция вызывается не однажды и вызовы следуют из различных мест (аобычно именно так и бывает), вероятность того, что хотя бы один из адресов нам «подойдет», весьма велика. Следует также учитывать, что некоторые функции ввода не вырезают символ перевода каретки из вводимой строки, чем практически полностью обезоруживают атакующих. Непосредственный ввод целевых адресов становится практически невозможным (ну что интересного можно найти по адресу х Ул.), коррекция существующих адресов теоритически вполне осуществн- •• «о на практике встретить подходящий указатель крайне маловероятно :ч1ачсИЧеСКИ МЫ огРаничеиы лишь одним адресом ??000А, где ?? — прежнее ТеРеть Иб ЯЗВИмого указателя). Единственное, что остается, — полностью за-х,Ь1см ВСе аитаУказателя вместе с двумя следующими за ним байтами. Тогда 1*тиоцп/;М НавязатьУЯЗВИм°му приложению любой FfXxYyZz, где Ff > 00h. Этот м,ческим б"г° пжнадлежит К°ДУ операционной системы, прикладным дииа-"айти мащ лиотскам и Драйверам. С ненулевой вероятностью здесь можно "Р«стеЙ1 ИННЮ КОмаиду, передающую управление по целевому адресу. "Чем - call СЛЧае эго СА1-- аДРес/JMP адрес (что маловероятно), а в более (""тветСтвен perMCTP/JMP регистр. Обе — двухбайтовые команды (FF DxhFF Ex ,b Иа Момент И В памяти т;нсих последовательностей сотни! Главное, что-пРавен11}1 Вызова затертого указателя (а значит, и на момент передачи !чГ)Уемый Кома,,Де call регистр/JMP регистр) ныбранный регистр содержал Тат,1ь,еФущГ!0ЙадРеС i.vfЬм°бразоГШИ КОНсольного ьвода интерпретируют некоторые символы г1С"Ром), и 0М Наг,римеР. символ с кодом 008 удаляет символ, стоящий перед "Тсты.м 0тсеиваются еще до попадания в уязвимый буфер. Следует 1 к тому, что атакуемая программа контролирует корректность 142 Глава 4. Ошибки переполнения буфера ИЗв поступающих данных, откидывая все нетекстовые символы или чТо приводит их к верхнему/нижнему регистру. Вероятность успешно» С"1°)Ке только это не DoS-атака) становится исчезающе мала.Kl1 (ее.-. ПОДГОТОВКА SHELL-КОДА В тех случая редачи двоичног аях, когда переполняющийся строковой буфер использует жчного shell-кода (например, головы червя), проблема uvn Д"ЯП1 волов стоит чрезвычайно остро — нулевые символы содержатся как них командах, так и на концах строк, передаваемых системным фу"*""1"1 в качестве основного аргумента (обычно это "cmd.exe" или "/bin/sh") Для «изгнания» нулей из операндов машинных инструкций следует пп к нуть к адресной арифметике. Так, например, MOV EAX.01h(B8 0 0 00 00 01) валентно XOR EAX.EAX/INC ЕАХ (33 СО 40). Последняя запись, кстати, дажекш че. Текстовые строки (вместе с завершающим нолем в конце) также могутбцр сформированы непосредственно на вершине стека (листинг 4.12). Листинг 4.12. Размещение строковых аргументов на стеке с динамической генерацией завершающего символа ноля 00000000: 33C0хог еах.еах 00000002: 50push еах 0000С003: 682Е657865push 06578652Е :"ехе." 000G0C08: 682E636D64push 0646D632E :"dmc." Как вариант, можно воспользоваться командой XOR EAX.EAX/MOV [XXX], ЕАХ,встав ляющей завершающий ноль в позицию XXX, где XXX — адрес конца текстовой строки, вычисленный тем или иным способом (см. далее раздел «В поисках сам» себя»). Радикальным средством предотвращения появления нулей является шифр* ка shell-кода, в подавляющем большинстве случаев сводящаяся ктрнвда му X0R. Основную трудность представляет поиск подходящего ключа шт «И ния — ни один шифруемый байт не должен обращаться всимв Поскольку a XOR а = 0, для шифрования подойдет любой оаИТОВЫ"с.сТВут совпадающий ни с одним байтом shell-кода. Если же в shell-коде при у t полный набор всех возможных значений от 00h до FFh, следует Ув!гаиТНак.1;1 ну ключа до слова и двойного слова, выбирая ее так, чтобы никакой 0 дываемой гаммы не совпадал ни с одним шифруемым байтом. А как такую гамму (метод перебора не предлагать)? Да очень просто $е0сФ ем частоту каждого из символов shell-кода, отбираем 4 символа, к elH0,1 чаются реже всего, выписываем их смешения относительно "учсН1 в столбик и вычисляем остаток от деления на 4. Вновь записываем ц до значения в столбик, отбирая те, которые в нем не встречаются, зсеэ позиции данного байта в ключе. Непонятно? Не волнуйтесь, сеи берем на конкретном примере (листинги 4.13-4.15).щс,,С,1>$> Допустим, в нашем shell-кода наиболее «низкочастотными» окат11нг лы 69h, ABh, CCh, DDh, встречающиеся в следующих позициях (сМ. 0 ... 41 42 43 44 45 46 47 ... 102
|