PWN-堆基础之LargeBin_Attack
LargeBin_Attack
LargeBins结构:
bins[64] ~ bins[126]
63 个循环双向链表
FIFO
管理大于 504 Bytes 的 free chunks(32位下)
[
堆块结构:
1 | struct malloc_chunk { |
malloc流程:
- 若申请size属于fastbin,查找fastbin,若fastbin中存在符合大小的chunk,取出chunk,返回。
- 若size属于smallbin,查找smallbin,若smallbin中存在符合大小的chunk,取出chunk,返回。
- 若size属于largebin,则调用malloc_consolidate函数对fast bin中所有的堆块进行合并,其过程为将fast bin中的堆块取出,清除下一块的p标志位并进行堆块合并,将最终的堆块放入unsorted bin。之后 便利unsored bin中的chunk,若找到满足的chunk则返回(遍历过程中会将不满足的chunk根据大小分到small bin 和large bin中,且插入large bin的时候会进行排序)然后在small bin和large bin中找到适合size_real大小的块。若找到则分配,并将多余的部分放入unsorted bin。
- 若largebin中也未找到,则调用top chunk扩展brk,若top chunk size不够,则借助系统开辟空间。
Large Bin_Attack利用原理:
当执行malloc流程中的第三步时,假设此时unsorted bin中存在chunk,遍历unsorted bin的过程中会将chunk分到largebin中,实现插入操作,即发生unlink,会导致某些地址被赋值。(malloc之前需要能控制largebin中chunk的bk和bk_nextsize数据和unsorted bin中chunk的bk数据。)
1 | //victim是unsortedbin_chunk、fwd是修改后的largebin_chunk |
对于largbin_chunk的内容赋值我们并不关心,关心的是伪造的target的赋值,其中最重要的是fake_chunk中size的修改。
具体unlink过程:
假设此时unsorted bin中存在一个chunk(size属于large bin且size大于largebin_chunk的size,bk数据可控),largebin中存在一个chunk(bk和bk_nextsize数据可控,可以通过off by null—overlap或uaf实现)
此时分别构造unsortebin_chunk的bk字段和largebin_chunk的bk以及bk_nextsize字段。
构造方法:其中content_addr为要申请到的target地址,chunk_7可控制unsortedbin_chunk,chunk_8可控制largebin_chunk。
构造chunk_7的bk = fake_chunk_addr,构造chunk_8的bk为fake_chunk_addr + 8,bk_nextsize为fake_chunk_addr-0x18-5(bk_nextsize的构造主要是为了给fake_chunk加上size字段,绕过malloc检查)content_addr = ????? fake_chunk = content_addr - 0x20 payload = p64(0)*2 + p64(0) + p64(0x4f1) # size payload += p64(0) + p64(fake_chunk) # bk edit(7,payload) payload2 = p64(0)*4 + p64(0) + p64(0x4e1) # size payload2 += p64(0) + p64(fake_chunk+8) payload2 += p64(0) + p64(fake_chunk-0x18-5) edit(8,payload2)
1
2
3
4
5
6
构造完成后(由于构造了unsortedbin_chunk的bk,此时target被链入了unsortedbin中),此时bins内容:
```python
unsorted bin: target->chunk_7(chunk7_size > chunk8_size)
largebin : chunk_8之后再次malloc(target_size)时,由于此时fastbin和smallbin中均没有符合chunk,因此调用合并遍历unsorted bin。
首先遍历chunk7,发现chunk7不符合,将chunk_7链入largebin,插入largebin时,会根据大小进行排序,大的在前,由于chunk7_size >chunk8_size,因此放在chunk8前边。
此时发生unlink操作,会将target地址附近的内容赋值,此时target被伪造成了合法的fake_chunk,即可绕过检查。
将chunk7链入largebin后,会再次遍历target,发现size符合因此返回target,这样就申请到了指定地址的内存。
例题: 西湖论剑Storm_note
init_proc函数
程序一开始就对进程进行初始化,mallopt(1, 0)
禁用了fastbin,然后通过mmap在0xABCD0000分配了一个页面的可读可写空间,最后往里面写入一个随机数。
alloc_note函数
首先遍历全局变量note,找到一个没有存放内容的地方保存堆指针。然后限定了申请的堆的大小最多为0xFFFFF,调用calloc函数来分配堆空间,因此返回
前会对分配的堆的内容进行清零。
edit_note函数
存在一个off_by_null漏洞,在read后v2保存写入的字节数,最后在该偏移处的字节置为0,形成off_by_null。
delete_note函数
这个函数就是正常free堆指针,并置0。
backdoor函数
程序提供一个可以直接getshell的后门,触发的条件就是输入的数据与mmap映射的空间的前48个字节相同。
利用思路
由于禁用了fastbin申请且存在off by null漏洞,因此考虑首先使用off by null造成堆块重叠,之后利用largebin_attack申请到0xabcd01
00处内存,修改为0后通过输入666调用后门函数getshell。
1.通过off by null造成overlap。
具体步骤:
首先分配7个chunk,chunk1和chunk4是用于放入largebin的大chunk,chunk6防止top chunk合并。Chunk结构如下。
1 | add(0x18) #0 |
构造两个伪造的prev_size,用于绕过malloc检查,保护下一个chunk的prev_size不被修改。如下图所示。
1 | edit(1,'a'*0x4f0+p64(0x500)) #prev_size |
接着利用off by null漏洞改写chunk 1的size为0x500。
1 | free(1) |
然后就可以进行chunk overlap了, 先将0x20的chunk释放掉,然后释放chunk2,这时触发unlink。
1 | add(0x18) #1 |
此时,chunk7指向了unsortedbin_chunk,largebin_chunk构造同理,构造完成后chunk8指向largebin_chunk。
2.修改unsortedbin_chunk和largebin_chunk的指定字段。
1 | content_addr = 0xabcd0100 |
3.将unsortedbin_chunk链入largebin实现fake_chunk的size伪造,申请到fake_chunk
1 | malloc(0x48) |
4.填充0getshell。
1 | payload = p64(0) * 2 + p64(0) * 6 |
完整exp:
1 | from pwn import * |