Разработка эксплоитов для Linux. Часть III - переполнение буфера с помощью ret2libc

Разработка эксплоитов для Linux. Часть III - переполнение буфера с помощью ret2libc

В данном руководстве будет продемонстрирована эксплуатация переполнения буфера с помощью ret2libc.

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

ПРИМЕЧАНИЕ: Перед чтением данного руководства рекомендуем вам просмотреть:

Во второй части данного руководства, при компиляции уязвимого приложения, мы использовали флаг -z execstack для gcc. Таким образом, был получен исполняемый стек. Но проблема в том, что большинство современных операционных систем по умолчанию использует неисполняемый стек.

В предыдущих частях данного руководства эксплоиты разрабатывались на Backtrack 4 R2. В данном руководстве эксплоит будет создан на Debian Squeeze.

Необходимые навыки:

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

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

Неисполняемый стек

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

Более подробно о неисполняемом стеке можно прочитать здесь: http://en.wikipedia.org/wiki/NX_bit

Для того чтобы обойти это ограничение, мы воспользуемся техникой “ret2libc” (возвращение к libc).

Эксплоиты, продемонстрированные в предыдущих статьях данной серии, имеют такую структуру:

##############################
мусорные данные + NOP + шеллкод + EIP (переписанный с помощью инструкции JMP/CALL в регистр, указывающий в область мусорных данных и NOP)
##############################

В случае с неисполняемым стеком, данная техника работать не будет. Выполнение инструкции jmp для стека приведет к ошибке сегментации. Вот здесь нам и пригодиться библиотека libc. Вместо перезаписи EIP с помощью инструкции, мы производим перезапись EIP с помощью функций, которые содержаться в библиотеке libc, после которых укажем аргументы функций.

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

Мы разобрались с тем, для чего нужно использовать libc. Теперь давайте перейдем к практическому применению техники вызова переполнения буфера.

Мы располагаем уязвимым приложением:

##############################
#include <stdio.h>
#include <string.h>
 
void evil(char* input)
{
        char buffer[500];
        strcpy(buffer, input); // Vulnerable function!
        printf("Buffer stored!\n");
        printf("Buffer is: %s\n\n",input);
}
 
int main(int argc, char** argv)
{
        evil(argv[1]);
        return 0;
}
##############################

В предыдущей части данного руководства мы производили компиляцию приложения с помощью флага -z execstack в gcc. На этот раз попробуем произвести компиляцию, используя настройки по умолчанию (используем неисполняемый стек).


Рис. 1

Присоединяем уязвимую программу к gdb и устанавливаем точки остановки на “call evil” и “ret” из функции “evil” для вычисления смещения, в которое будут переданы нужные данные.


Рис 2

Рис. 3
После установки точек остановки, отправляем приложению пакет мусорных данных и смотрим, что из этого получиться.


Рис. 4

Рис. 5
Как было показано, для перезаписи нам дополнительно требуется 8 байтов, что в итоге даст 516 байт (Это означает, что объем мусорных данных должен составлять 512 байтов).
Теперь, давайте проверим, можно ли использовать libc для отправки нижеуказанной команды в GDB.
###########################
maint info sections ALLOBJ
###########################

Рис. 6

Мы не получили никаких нулевых данных, следовательно libc нам подходит.

Требуемое нам смещение равно 516. В начале данного руководства, было сказано о том, что EIP будет переписано с помощью инструкции JMP/CALL, так как это приведет к ошибке сегментации. Вместо этого, мы попытаемся перезаписать EIP с помощью одной из функций libc, а затем продолжим вызывать различные функции и передавать требуемые аргументы.

Мы будем работать с такими функциями:

  • system(): Данная функция выполняет команду или программу, указанную в аргументе.
  • exit(): Функция для выхода из программы.

Нам необходимо отыскать адреса функций system() и exit(), а также адрес “/bin/bash”, который необходимо указать как аргумент для system().
Структура нашего эксплоита, будет выглядеть таким образом:

##############################
мусорные данные * 512 + system() + exit() + адрес /bin/bash
##############################

Давайте найдем все необходимые адреса для формирования работоспособного эксплоита.


Рис. 7

Адрес system(), по всей видимости, действительный.


Рис. 8

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

По адресу 0xb7ebc304 можно найти функцию <exit+4>, которая нам подходит.


Рис. 9
Теперь найдем /bin/bash.


Рис. 10

Давайте проследим за /bin/bash.


Рис. 11

Теперь нам необходимо изменить адрес таким образом, чтобы заменить только “/bin/bash” для получения действительных аргументов для system().


Рис. 12

Все необходимые адреса найдены, перейдем к эксплуатации:

##############################
мусорные данные * 512 + “\x80\x61\xec\xb7” + “\x04\xc3\xeb\xb7” + “\x73\xf7\xff\xbf”
##############################

Посмотрим, что у нас получиться:


Рис. 13

Эксплуатация прошла успешно.

Мы нашли признаки жизни...в вашем смартфоне!

Наш канал — питательная среда для вашего интеллекта

Эволюционируйте вместе с нами — подпишитесь!