Shellcode своими руками

Xakep, номер #044, стр. 044-038-4


К несчастью (или к счастью?), это - далеко не выход. Ведь можно положить в стек данные таким образом, что после возврата из процедуры управление будет передано любой функции, принадлежащей основной программе, например, memcpy, и параметры ее вызова тоже будут находиться в стеке на положенных местах. В результате функция скопирует данные из стека в другую область памяти, где исполнение кода разрешено, а после возврата из нее управление будет передано в эту область памяти.

Эксплуатация переполнения "вслепую"

А что, если мы не знаем адрес возврата, длину буфера, расстояние от буфера до адреса возврата и вообще не видели уязвимую программу, а знаем лишь, что она находится на таком-то IP-адресе и таком-то порте? Возможно ли добиться выполнения шеллкода? Ответ - ДА! Некоторыми нетривиальными путями возможно добиться и этого.

Во-первых, нам нужно знать адрес буфера в памяти. Примерный адрес мы знаем - он чуть меньше, чем начальный адрес стека. Например, в linux он будет чуть меньше, чем 0xbfffffff. Насколько "чуть"? На сотню-тысячу байт. Сколько попыток нужно, чтобы угадать его? Нет, не сотня-тысяча. Нам не обязательно знать адрес шеллкода в памяти с точностью до одного байта, ничто не мешает нам разместить перед шеллкодом "посадочную зону" из большого количества NOP-ов, нам достаточно попасть адресом возврата в эту зону. Количество попыток перебора, таким образом, сведется к менее чем десяти.

Во-вторых, нам нужно знать примерную длину буфера. Ее мы подберем методом "деления на два", или "недолет-перелет". Вот псевдоисходник программы, реализующей это:

mpzone=0;slider=0x1000;

do {

mpzone+=slider;

if(!try)mpzone-=slider;

slider=slider/2;

} while(slider!=0);

Функция try - псевдофункция, определяющая, был ли "недолет" или "перелет". В случае "недолета", т.е. допустимого размера данных, удаленная программа продолжает функционировать нормально, а в случае "перелета" - подвисает, обрывает соединение или совсем выпадает. Если выпадает совсем, реализовывать эту технологию долго - надо ждать, пока админ приведет сервис в работоспособное состояние. Работа алгоритма завершится на том, что будет найден максимальный размер данных, при котором удаленный сервис работает нормально. Эту область данных я буду называть MP-зона (от лат. monopenisuale).

Для более корректного нахождения MP-зоны функция try должна проверять способность сервиса принять данные не один раз, а несколько, с различным значением данных. Такая проверка гарантирует, что внутрь MP-зоны можно помещать любые данные, и на работоспособность сервиса это не повлияет.

Что такое MP-зона с точки зрения программы? Понятно, что MP-зона не меньше места, выделенного в стеке под буфер. Но она может быть и больше, вот картинка:

Стек передаваемые данные

...

<буфер - 1024 байта> <MP-зона>

<переменная 1 - 4 байта> <MP-зона>

<адрес возврата - 4 байта> <новый адрес возврата>

...

Если значение переменной 1 не влияет на работоспособность программы, то она также попадает в MP-зону. Однако значение этой переменной может быть критично:

Назад на стр. 044-038-3  Содержание  Вперед на стр. 044-038-5