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

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

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


person H4cKL0rD    schedule 22.02.2009    source источник
comment
Почему вы пытаетесь вызвать переполнение буфера?   -  person Sophie Alpert    schedule 22.02.2009
comment
см.: входит в стандартную библиотеку C.   -  person derobert    schedule 22.02.2009
comment
см.: попадает в стандартную библиотеку C — это шутка?   -  person NTDLS    schedule 22.02.2009
comment
@NTDLS: это язвительно, но совершенно серьезно ... становится опасным и совершенно незащищенным в производственном коде.   -  person dmckee --- ex-moderator kitten    schedule 22.02.2009


Ответы (12)


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

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

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

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)

Компилируется ли это и что происходит при запуске, вероятно, зависит от вашей операционной системы и компилятора.

person David Dean    schedule 22.02.2009
comment
Переполнение буфера обычно более разрушительно, когда вы пишете за пределы буфера, а не читаете [например, char x[2]; strcpy (x,hello);] - это потому, что он часто заполняет многие другие переменные и/или кадр стека. - person paxdiablo; 22.02.2009
comment
Не нужно писать за пределами массива, чтобы это считалось переполнением буфера? В этом случае я думаю, что пример, который меняет содержимое в [200], был бы лучше. - person MahlerFive; 22.02.2009
comment
@david, если вы прочитали первый абзац статьи из Википедии, которую вы цитируете, переполнение буфера происходит только тогда, когда вы «пишите» за пределами буфера, ваш образец не является правильным переполнением буфера. - person Ismael; 22.02.2009
comment
Это все еще не переполнение буфера. a — это указатель на строку, а вторая строка просто меняет эту ссылку. Кроме того, a - это массив, поэтому он даже не является допустимым l-значением, и ваш код не будет компилироваться. Надежным примером может быть strcpy(a, строка длиннее 4 символов); - person wj32; 22.02.2009

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

// noone will ever have the time to type more than 64 characters...
char buf[64];
gets(buf); // let user put his name

Само по себе переполнение буфера чаще всего не происходит намеренно. Чаще всего это происходит из-за так называемой ошибки "off-by-one". Это означает, что вы неправильно рассчитали размер массива на единицу - возможно, потому, что вы забыли учесть завершающий нулевой символ или из-за чего-то еще.

Но это также может быть использовано для некоторых злых вещей. Действительно, пользователь давно знает эту дыру, а затем вставляет, скажем, 70 символов, причем последние содержат какие-то специальные байты, которые перезаписывают какой-то слот стека - если пользователь действительно хитрый, он / она попадет в слот обратного адреса в стеке. , и перезаписывает его, так что он переходит в только что вставленный буфер: потому что то, что ввел пользователь, было не его именем, а его шелл-кодом, который он ранее скомпилировал и выгрузил. Затем этот человек будет просто казнен. Есть некоторые проблемы. Например, вы должны сделать так, чтобы в этом двоичном коде не было "\n" (потому что gets перестанет там читать). Для других способов, которые связываются с опасными строковыми функциями, двоичный ноль проблематичен, потому что строковые функции перестают копировать его в буфер. Люди использовали xor с двумя одинаковыми значениями для получения нуля, без явной записи нулевого байта.

Это классический способ сделать это. Но есть некоторые блоки безопасности, которые могут сказать, что такие вещи произошли, и другие вещи, которые делают стек неисполняемым. Но я думаю, что есть трюки намного лучше, чем я только что объяснил. Какой-нибудь ассемблер, наверное, мог бы сейчас рассказать вам об этом длинные истории :)

Как этого избежать

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

Например, вместо gets используйте fgets. И вместо sprintf используйте snprintf там, где это подходит и доступно (или просто вещи в стиле C++, такие как istream и прочее)

person Johannes Schaub - litb    schedule 22.02.2009
comment
Переполнение буфера = переполнение буфера? - person Ahmed Said; 22.02.2009
comment
я не знаю последнего термина. Википедия, кажется, говорит, что они означают одно и то же. - person Johannes Schaub - litb; 22.02.2009

В современной ОС Linux вы не можете использовать переполнение буфера без какого-либо ДОПОЛНИТЕЛЬНОГО эксперимента. Зачем ? потому что вы будете заблокированы ASLR (рандомизация уровня стека адресов) и защитой стека в этом современном компиляторе GNU C. вам будет нелегко найти память, потому что память попадет в случайную память, вызванную ASLR. и вы будете заблокированы защитой стека, если попытаетесь переполнить программу.

Для начала вам нужно установить ASLR равным 0, значение по умолчанию равно 2.

root@bt:~# cat /proc/sys/kernel/randomize_va_space
2
root@bt:~# echo 0 > /proc/sys/kernel/randomize_va_space
root@bt:~# cat /proc/sys/kernel/randomize_va_space
0
root@bt:~#

в этом случае речь не идет об учебнике по переполнению буфера СТАРОГО СТИЛЯ, который вы можете получить из Интернета. или Aleph One Tutorial больше не будет работать в вашей системе.

теперь давайте сделаем уязвимость программы к сценарию переполнения буфера

---------------------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-boundary=2 для снятия защиты стека в вашей программе C

root@bt:~# gcc -g -o bof -fno-stack-protector -mpreferred-stack-boundary=2 bof.c
root@bt:~# chown root:root bof
root@bt:~# chmod 4755 bof

Программа C с переполнением буфера со сценарием корневого доступа SUID теперь у нас есть это сделать. теперь давайте посмотрим, сколько байт нам нужно поместить в буфер, чтобы сделать ошибку сегментации программы

root@bt:~# ./bof `perl -e 'print "A" x 400'`
root@bt:~# ./bof `perl -e 'print "A" x 403'`
root@bt:~# ./bof `perl -e 'print "A" x 404'`
Segmentation fault
root@bt:~#

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

root@bt:~# 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)

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

person Community    schedule 07.12.2010

Переполнение буфера — это просто запись за конец буфера:

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

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

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

person NTDLS    schedule 22.02.2009
comment
Существует также snprintf, преимущество которого заключается в том, что он стандартизирован (ISO C 99). Также есть asprintf (GNU и BSD libc), g_strdup_printf (Glib). - person sleske; 07.07.2010

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

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

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

    char accessViolation = foo[0];
}
person alex2k8    schedule 22.02.2009

Пример «классического» переполнения буфера:

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

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

person ReinstateMonica Larry Osterman    schedule 22.02.2009

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

person John Smith    schedule 22.02.2009

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

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.

person jww    schedule 12.09.2014

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

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

person dmckee --- ex-moderator kitten    schedule 22.02.2009

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

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

person Olaf Kock    schedule 22.02.2009

Переполнение буфера — это вставка символов сверх того, что может вместить выделенная память.

person Jcbn    schedule 05.05.2019
comment
Добавляет ли этот ответ что-то новое по сравнению с уже существующим? Пожалуйста, не повторяйтесь, проголосуйте за хорошие ответы - person slfan; 05.05.2019