Подтвердить что ты не робот

Что такое переполнение буфера и как его вызвать?

Я слышал о переполнении буфера, и я хотел бы знать, как его вызвать.

Может ли кто-нибудь показать мне небольшой пример переполнения буфера? Новый (и для чего они используются?)

4b9b3361

Ответ 1

Переполнение буфера происходит в основном, когда созданный раздел (или буфер) памяти записывается за пределы его предполагаемых границ. Если злоумышленнику удастся сделать это из-за пределов программы, это может вызвать проблемы с безопасностью, поскольку это потенциально может позволить им манипулировать произвольными ячейками памяти, хотя многие современные операционные системы защищают от худших случаев этого.

Хотя чтение и запись вне предполагаемых границ обычно считаются плохими идеями, термин "переполнение буфера" обычно зарезервирован для записи за пределами границ, поскольку это может заставить злоумышленника легко изменить способ запуска вашего кода. В Википедии есть хорошая статья о переполнении буфера и о различных способах их использования для эксплойтов.

С точки зрения того, как вы могли программировать один самостоятельно, это было бы просто:

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)

Независимо от того, компилирует ли он и что происходит при его запуске, вероятно, будет зависеть ваша операционная система и компилятор.

Ответ 2

Классический пример переполнения буфера:

// 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 и прочее)

Ответ 3

В современной операционной системе 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)

теперь вы можете сделать следующий шаг...

Ответ 4

Переполнение буфера просто записывается за конец буфера:

int main(int argc, const char* argv[])
{
    char buf[10];
    memset(buf, 0, 11);
    return 0;
}

Ответ 5

В дополнение к тому, что уже было сказано, имейте в виду, что ваша программа может или не может "сбой" при переполнении буфера. Он должен разбиваться, и вы должны надеяться, что это произойдет, но если переполнение буфера переполнится на другой адрес, который также было выделено вашим приложением, ваше приложение может работать нормально в течение более длительного периода времени.

Если вы используете более позднюю версию Microsoft Visual Studio - я бы предложил использовать новые защищенные копии в stdlib, например sprintf_s insted из sprintf, ect...

Ответ 6

Этого должно быть достаточно, чтобы воспроизвести его:

void buffer_overflow() 
{
    char * foo = "foo";
    char buffer[10];

    for(int it = 0; it < 1000; it++) {
        buffer[it] = '*';
    }

    char accessViolation = foo[0];
}

Ответ 7

"Классический" пример переполнения буфера:

int main(int argc, char *argv[])
{
    char buffer[10];
    strcpy(buffer, argv[1]);
}

Это позволяет вам играть с параметрами переполнения буфера и подстраивать их под ваши сердца. В книге " Взлом - искусство эксплуатации " (ссылка идет на Amazon) подробно рассказывается о том, как поиграть с переполнением буфера (очевидно, чисто как интеллектуальное упражнение).

Ответ 8

Если вы хотите проверить программу переполнения буфера, вы можете запустить ее с помощью таких инструментов, как Valgrind. Они найдут для вас некоторые ошибки управления памятью.

Ответ 9

Это общий комментарий о полученных вами ответах. Например:

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.

Ответ 10

В этом контексте буфер представляет собой часть памяти, выделенную для определенной цели, а переполнение буфера - это то, что происходит, когда операция записи в буфер продолжает проходить мимо конца (запись в память, которая имеет другую цель), Это всегда ошибка.

Атака переполнения буфера - это та, которая использует эту ошибку для выполнения чего-то, что автор программы не собирался делать возможным.

Ответ 11

С правильными ответами: Чтобы узнать больше об этом, вы можете прослушать Подкастную безопасность сейчас. В Episode 39 (некоторое время назад) они подробно обсудили это. Это быстрый способ получить более глубокое понимание, не требуя переваривать целую книгу.

(По ссылке вы найдете архив с несколькими версиями размера, а также стенограмму, если вы скорее ориентированы на визуальную ориентацию). Звук - не идеальная среда для этой темы, но Стив творит чудеса, чтобы справиться с этим.

Ответ 12

Переполнение буфера - это вставка символов, превышающая то, что может содержать выделенная память.