Я слышал о переполнении буфера, и я хотел бы знать, как его вызвать.
Может ли кто-нибудь показать мне небольшой пример переполнения буфера? Новый (и для чего они используются?)
Я слышал о переполнении буфера, и я хотел бы знать, как его вызвать.
Может ли кто-нибудь показать мне небольшой пример переполнения буфера? Новый (и для чего они используются?)
Переполнение буфера происходит в основном, когда созданный раздел (или буфер) памяти записывается за пределы его предполагаемых границ. Если злоумышленнику удастся сделать это из-за пределов программы, это может вызвать проблемы с безопасностью, поскольку это потенциально может позволить им манипулировать произвольными ячейками памяти, хотя многие современные операционные системы защищают от худших случаев этого.
Хотя чтение и запись вне предполагаемых границ обычно считаются плохими идеями, термин "переполнение буфера" обычно зарезервирован для записи за пределами границ, поскольку это может заставить злоумышленника легко изменить способ запуска вашего кода. В Википедии есть хорошая статья о переполнении буфера и о различных способах их использования для эксплойтов.
С точки зрения того, как вы могли программировать один самостоятельно, это было бы просто:
char a[4];
strcpy(a,"a string longer than 4 characters"); // write past end of buffer (buffer overflow)
printf("%s\n",a[6]); // read past end of buffer (also not a good idea)
Независимо от того, компилирует ли он и что происходит при его запуске, вероятно, будет зависеть ваша операционная система и компилятор.
Классический пример переполнения буфера:
// noone will ever have the time to type more than 64 characters...
char buf[64];
gets(buf); // let user put his name
Только переполнение буфера чаще всего не происходит специально. Это происходит чаще всего из-за так называемой ошибки "за один". Это означает, что вы неправильно рассчитали размер массива на единицу - возможно, потому, что вы забыли учесть завершающий нулевой символ или потому, что некоторые другие вещи.
Но он также может быть использован для некоторых злых вещей. В самом деле, пользователь долго знал эту дыру, а затем вставки сказал 70 символов, причем последние содержат некоторые специальные байты, которые перезаписывают некоторый стек-слот - если пользователь действительно запутан, он/она попадет в слот обратного адреса в стеке, и перезаписывает его так, что он перескакивает вперед в этот только вставленный буфер: поскольку то, что вводил пользователь, было не его именем, а его shell-кодом, который он ранее компилировал и выгружал. Затем он будет выполнен. Есть некоторые проблемы. Например, вам нужно организовать, чтобы в этом двоичном коде не было "\n" (потому что gets перестает читать там). Для других способов, которые беспорядочны с опасными строковыми функциями, двоичный ноль проблематичен, потому что строковые функции прекращают копирование туда в буфер. Люди использовали xor
с двумя значениями одного и того же значения для получения нулевого значения, без явного написания нулевого байта.
Это классический способ сделать это. Но есть некоторые блоки безопасности, которые могут сказать, что такие вещи произошли, и другие вещи, которые делают стек неисполняемым. Но я думаю, что есть способы улучшить трюки, чем я только что объяснил. Некоторый ассемблер, возможно, теперь расскажет вам долгие истории об этом:)
Всегда использовать функции, которые также принимают аргумент максимальной длины, если вы не 100% уверены, что буфер действительно достаточно большой. Не играйте в такие игры, как "о, число не должно превышать 5 символов" - в какой-то день это провалится. Помните, что одна ракета, на которой ученые сказали, что число не будет превышать некоторую величину, потому что ракета никогда не будет так быстро. Но в какой-то день это было быстрее, и в результате получилось целочисленное переполнение и ракета разбилась (об ошибке в Ariane 5, одна из самые дорогие компьютерные ошибки в истории).
Например, вместо использования fgets
. Вместо sprintf
используйте snprintf
, где подходящий и доступный (или просто стиль С++, например, istream и прочее)
В современной операционной системе Linux вы не можете использовать переполнение буфера без какого-либо эксперимента EXTRA. Зачем? потому что в этом современном компиляторе GNU C вы будете заблокированы ASLR (ранжирование слоя стека пакетов) и стековый защитник. вы не сможете легко найти память, потому что память попадет в случайную память, вызванную ASLR. и вы будете заблокированы защитой стека, если вы попытаетесь переполнить программу.
Для начала вам нужно поставить ASLR в 0 значение по умолчанию равно 2
[email protected]:~# cat /proc/sys/kernel/randomize_va_space
2
[email protected]:~# echo 0 > /proc/sys/kernel/randomize_va_space
[email protected]:~# cat /proc/sys/kernel/randomize_va_space
0
[email protected]:~#
в этом случае не о учебнике переполнения буфера OLD STYLE, который вы можете получить из Интернета. или aleph один учебник больше не будет работать в вашей системе.
теперь позволяет сделать уязвимость программы для сценария переполнения буфера
---------------------bof.c--------------------------
#include <stdio.h>
#include <string.h>
int main(int argc, char** argv)
{
char buffer[400];
strcpy(buffer, argv[1]);
return 0;
}
---------------------EOF-----------------------------
просмотр функции strcpy опасен без стекового защитника, потому что функция не проверяет, сколько байтов мы будем вводить. компиляция с дополнительной опцией -fno-stack-protector dan -mpreferred-stack-border = 2 для снятия стекового защитника в вашей программе на C
[email protected]:~# gcc -g -o bof -fno-stack-protector -mpreferred-stack-boundary=2 bof.c
[email protected]:~# chown root:root bof
[email protected]:~# chmod 4755 bof
Программа переполнения буфера C с использованием сценария доступа SUID root теперь мы это сделали. теперь позволяет искать, сколько байтов нам нужно поместить в буфер, чтобы сделать ошибку сегментации программы.
[email protected]:~# ./bof `perl -e 'print "A" x 400'`
[email protected]:~# ./bof `perl -e 'print "A" x 403'`
[email protected]:~# ./bof `perl -e 'print "A" x 404'`
Segmentation fault
[email protected]:~#
вы видите, что нам нужно 404 байта, чтобы сделать ошибку сегментации программы (crash), сколько байтов необходимо перезаписать EIP? EIP - инструкция будет выполнена после. поэтому хакер переписывает EIP на злую инструкцию, что они хотят в двоичном SUID в программе. если программа в корне SUID, команда будет запущена в корневом доступе.
[email protected]:~# gdb -q bof
(gdb) list
1 #include <stdio.h>
2 #include <string.h>
3
4 int main(int argc, char** argv)
5 {
6 char buffer[400];
7 strcpy(buffer, argv[1]);
8
9 return 0;
10 }
(gdb) run `perl -e 'print "A" x 404'`
Starting program: /root/bof `perl -e 'print "A" x 404'`
Program received signal SIGSEGV, Segmentation fault.
0xb7e86606 in __libc_start_main () from /lib/tls/i686/cmov/libc.so.6
(gdb) run `perl -e 'print "A" x 405'`
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /root/bof `perl -e 'print "A" x 405'`
Program received signal SIGSEGV, Segmentation fault.
0xb7e800a9 in ?? () from /lib/tls/i686/cmov/libc.so.6
(gdb)
программа GOT код возврата ошибки сегментации. пусть вводит больше байтов и просматривает регистр EIP.
(gdb) run `perl -e 'print "A" x 406'`
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /root/bof `perl -e 'print "A" x 406'`
Program received signal SIGSEGV, Segmentation fault.
0xb7004141 in ?? ()
(gdb)
(gdb) run `perl -e 'print "A" x 407'`
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /root/bof `perl -e 'print "A" x 407'`
Program received signal SIGSEGV, Segmentation fault.
0x00414141 in ?? ()
(gdb)
немного больше
(gdb) run `perl -e 'print "A" x 408'`
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /root/bof `perl -e 'print "A" x 408'`
Program received signal SIGSEGV, Segmentation fault.
0x41414141 in ?? ()
(gdb)
(gdb) i r
eax 0x0 0
ecx 0xbffff0b7 -1073745737
edx 0x199 409
ebx 0xb7fc9ff4 -1208180748
esp 0xbffff250 0xbffff250
ebp 0x41414141 0x41414141
esi 0x8048400 134513664
edi 0x8048310 134513424
eip 0x41414141 0x41414141 <-- overwriten !!
eflags 0x210246 [ PF ZF IF RF ID ]
cs 0x73 115
ss 0x7b 123
ds 0x7b 123
es 0x7b 123
fs 0x0 0
gs 0x33 51
(gdb)
теперь вы можете сделать следующий шаг...
Переполнение буфера просто записывается за конец буфера:
int main(int argc, const char* argv[])
{
char buf[10];
memset(buf, 0, 11);
return 0;
}
В дополнение к тому, что уже было сказано, имейте в виду, что ваша программа может или не может "сбой" при переполнении буфера. Он должен разбиваться, и вы должны надеяться, что это произойдет, но если переполнение буфера переполнится на другой адрес, который также было выделено вашим приложением, ваше приложение может работать нормально в течение более длительного периода времени.
Если вы используете более позднюю версию Microsoft Visual Studio - я бы предложил использовать новые защищенные копии в stdlib, например sprintf_s insted из sprintf, ect...
Этого должно быть достаточно, чтобы воспроизвести его:
void buffer_overflow()
{
char * foo = "foo";
char buffer[10];
for(int it = 0; it < 1000; it++) {
buffer[it] = '*';
}
char accessViolation = foo[0];
}
"Классический" пример переполнения буфера:
int main(int argc, char *argv[])
{
char buffer[10];
strcpy(buffer, argv[1]);
}
Это позволяет вам играть с параметрами переполнения буфера и подстраивать их под ваши сердца. В книге " Взлом - искусство эксплуатации " (ссылка идет на Amazon) подробно рассказывается о том, как поиграть с переполнением буфера (очевидно, чисто как интеллектуальное упражнение).
Если вы хотите проверить программу переполнения буфера, вы можете запустить ее с помощью таких инструментов, как Valgrind. Они найдут для вас некоторые ошибки управления памятью.
Это общий комментарий о полученных вами ответах. Например:
int main(int argc, char *argv[]) { char buffer[10]; strcpy(buffer, argv[1]); }
и
int main(int argc, const char* argv[]) { char buf[10]; memset(buf, 0, 11); return 0; }
На современных платформах Linux это может работать не так, как ожидалось или предназначалось. Он может не работать из-за функции безопасности FORTIFY_SOURCE.
FORTIFY_SOURCE использует "более безопасные" варианты функций высокого риска, таких как memcpy
и strcpy
. Компилятор использует более безопасные варианты, когда он может определить размер буфера назначения. Если копия превысит размер буфера назначения, программа вызывает abort()
.
Чтобы отключить FORTIFY_SOURCE для вашего тестирования, вы должны скомпилировать программу с помощью -U_FORTIFY_SOURCE
или -D_FORTIFY_SOURCE=0
.
В этом контексте буфер представляет собой часть памяти, выделенную для определенной цели, а переполнение буфера - это то, что происходит, когда операция записи в буфер продолжает проходить мимо конца (запись в память, которая имеет другую цель), Это всегда ошибка.
Атака переполнения буфера - это та, которая использует эту ошибку для выполнения чего-то, что автор программы не собирался делать возможным.
С правильными ответами: Чтобы узнать больше об этом, вы можете прослушать Подкастную безопасность сейчас. В Episode 39 (некоторое время назад) они подробно обсудили это. Это быстрый способ получить более глубокое понимание, не требуя переваривать целую книгу.
(По ссылке вы найдете архив с несколькими версиями размера, а также стенограмму, если вы скорее ориентированы на визуальную ориентацию). Звук - не идеальная среда для этой темы, но Стив творит чудеса, чтобы справиться с этим.
Переполнение буфера - это вставка символов, превышающая то, что может содержать выделенная память.