Издательский дом ООО "Гейм Лэнд"СПЕЦВЫПУСК ЖУРНАЛА ХАКЕР #45, АВГУСТ 2004 г.

Зоопарк переполняющихся буферов 

Крис Касперски aka мыщъх

Спецвыпуск Xakep, номер #045, стр. 045-008-7


Задержав дыхание, вновь запускаем программу и вводим "qwertyuiopasdfghP^Q@", пароль можно пропустить. Собственно говоря, символы "qwertyuiopasdfgh" могут быть любыми, главное, чтобы "P^Q@" располагались в 17-й, 18-й и 19-й позициях. Нуль, завершающий строку, вводить не надо: функция gets засунет его самостоятельно.

Если все сделано правильно, то программа победоносно выведет на экран "Your have root", подтверждая, что атака сработала. Правда, по выходе из root'а программа немедленно грохнется, так как в стеке находится мусор, но это уже неважно, ведь функция root отработала и стала не нужна.

Просто передавать управление на готовую функцию неинтересно (тем более что такой функции в атакуемой программе может и не быть). Намного более действенно заслать на удаленную машину свой собственный shell-код и там его исполнить.

Вообще говоря, организовать удаленный shell не так-то просто: необходимо, как минимум, установить TCP/UDP-соединение, попутно обманув доверчивый firewall, создать пайпы, связать их дескрипторами ввода/вывода терминальной программы, а самому работать диспетчером, гоняя данные между сокетами и пайпами. Некоторые пытаются поступить проще, пробуя унаследовать дескрипторы, но на этом пути их ждет жестокий облом, так как дескрипторы не наследуются и такие эксплоиты не работают. Даже и не пытайся их оживить – все равно не получится. Если среди читателей наберется кворум, эту тему можно будет осветить во всех подробностях, пока же ограничится локальным shell'ом, но и он для некоторых из вас будет своеобразных хакерским подвигом!

Вновь запускаем нашу демонстрационную программу, срываем буфер, вводя строку "AAA...", но вместо того чтобы нажать "ОК" в диалоге критической ошибки приложения, давим "отмену", запускающую отладчик (для этого он должен быть установлен). Конкретно нас будет интересовать содержимое регистра ESP в момент сбоя. На моей машине он равен 0012FF94h, у тебя это значение может отличаться. Вводим этот адрес в окне дампа и, прокручивая его вверх/вниз, находим нашу строку "ААААА". В моем случае она расположена по адресу 0012FF80h.

Теперь мы можем изменить адрес возврата на 12FF94h, и тогда управление будет передано на первый байт переполняющегося буфера. Остается лишь подготовить shell-код. Чтобы вызвать командный интерпретатор в осях семейства NT, необходимо дать команду WinExec("CMD", x). В 9x такого файла нет, но зато есть command.com, который есть анахронизм. На языке ассемблера этот вызов может выглядеть так (код можно набить прямо в HIEW'е):

Код #10. Подготовка shell-кода

00000000: 33C0 xor eax,eax

00000002: 50 push eax

00000003: 68434D4420 push 020444D43 ;" DMC"

00000008: 54 push esp

00000009: B8CA73E977 mov eax,077E973CA ;"wesE"

0000000E: FFD0 call eax

00000010: EBFE jmps 000000010

Здесь мы используем целый ряд хитростей и допущений, подробный разбор которых требует целой книги. Если говорить кратко, то 77E973CAh – это адрес API-функции WinExec, жестко прописанный в программу и добытый путем анализа экспорта файла KERNEL32.DLL утилитой DUMPBIN. Это грязный и ненадежный прием, так как в каждой версии оси адрес функции свой и правильнее было бы добавить в shell-код процедуру обработки экспорта, описанную в следующей статье. Почему вызываемый адрес предварительно загружается в регистр EAX? Потому что call 077E973CAh на самом деле ассемблируется в относительный вызов, чувствительный к местоположению call'а, что делает shell-код крайне немобильным.

Назад на стр. 045-008-6  Содержание  Вперед на стр. 045-008-8