0x00 前言
本文是根据CTF-WIKI
上Unsorted bin attack
相关内容来写的,用来巩固一下相关知识,并以一道例题练手。
题目是HITCON Training lab14 magic heap:下载地址
参考链接:
0x01 知识回顾
利用前提
:能够控制 Unsorted Bin Chunk 的 bk 指针。
利用效果
:可以达到的效果是实现修改任意地址值为一个较大的数值。
Unsorted bin 基本来源
- 当一个较大的 chunk 被分割成两半后,如果剩下的部分大于 MINSIZE,就会被放到 unsorted bin 中。
- 释放一个不属于 fast bin 的 chunk,并且该 chunk 不和 top chunk 紧邻时,该 chunk 会被首先放到 unsorted bin 中。
- 当进行 malloc_consolidate 时,可能会把合并后的 chunk 放到 unsorted bin 中,如果不是和 top chunk 近邻的话。
Unsorted bin 使用情况
- Unsorted Bin 在使用的过程中,采用的遍历顺序是 FIFO,即插入的时候插入到 unsorted bin 的头部,取出的时候从链表尾获取。
- 在程序 malloc 时,如果在 fastbin,small bin 中找不到对应大小的 chunk,就会尝试从 Unsorted Bin 中寻找 chunk。如果取出来的 chunk 大小刚好满足,就会直接返回给用户,否则就会把这些 chunk 分别插入到对应的 bin 中。具体可以看下面的补充
在malloc的时候,如果是在libc版本<2.26的情况下,如果在fastbin,small bin中找不到对应大小的chunk时,会尝试在Unsorted Bin中遍历,在这个过程中,如果找到对应大小的chunk,则会直接返回给用户。否则会将这些chunk插入对应的bin中;在libc2.26之后,引入tcache内存管理机制,如果申请的chunk的大小不在fastbin范围内,并且在tcache中找不到对应大小的堆块,会尝试在Unsorted Bin中寻找,在遍历的过程中,就算找到符合大小的堆块也不会立即返回。会将找到的符合的chunk放入对应tcache bin中直到bin中chunk的个数达到上限7,或者Unsorted Bin遍历结束。在Unsorted Bin遍历结束之后会,若之前找到至少一个符合大小的chunk,返回tcache bin中的最后一个,否则返回null。
注意这里,是插入到unsorted bin的头部,然后取的时候从链表尾取出,这里可以联系一下数据结构中带头节点的链表,应该对理解这一块有帮助。头结点其实就相当于unsorted_chunks(av)
原理
原理部分在CTF-WIKI里有相当详细的讲解。
最核心的部分应该是这四行以及修改p的bk指针为目的地址target-0x10(64位是-0x10,32位-0x08)
- victim = unsorted_chunks(av)->bk=p
- bck = victim->bk=p->bk = target addr-16
- unsorted_chunks(av)->bk = bck=target addr-16
- bck->fd = *(target addr -16+16) = unsorted_chunks(av);
这样也就将*target 的值修改为了unsorted bin 链表的头结点的地址了,这个地址往往是非常大的一个正数。
利用场景
- 我们通过修改循环的次数来使得程序可以执行多次循环。
- 我们可以修改 heap 中的 global_max_fast 来使得更大的 chunk 可以被视为 fast bin,这样我们就可以去执行一些 fast bin attack 了。
0x02 题目分析
这是一道很简单很适合练习Unsorted bin attack的题目
1 | Arch: amd64-64-little |
题目提供了四个功能
1 | 1. Create a Heap |
漏洞点在edit_heap
里面,这个函数能够实现任意字节的堆溢出。
1 | if ( choice == 4869 ) |
1 | int l33t() |
可以看到,如果我们的choice输入4869,程序会判断magic的大小是否小于等于0x1305,如果不小于,则执行l33t函数,这个函数能够cat flag。
思路
程序特别简单,我们只需要让magic大于0x1305就好,而我们的unsorted bin attack就能实现这个目的。
创建3个chunk (其中chunk2是为了让chunk1与top chunk隔开),delete掉chunk1,然后edit chunk0,通过堆溢出覆盖掉chunk1的bk,让bk为magic的地址-0x10,然后重新创建一个和chunk1大小一样的chunk。然后向程序输入4869、交互即可得到flag。
0x03 完整EXP
1 | from pwn import * |