mmap_overlapping
本质上也是劫持size或者prevsize,只是mmap比较特殊,直接走内核进行分配,释放也是直接用munmap。分配的chunk地址也会比较抽象元数据也不一样,看这个poc就都理解了,只是可能会在允许我们分配特别特别大的chunk的时候能用到。不过好处是只要内核mmap和munmap不改,这个overlap是全版本通用的。
1 |
|
这里就是通过修改低地址处mmap chunk的size进行释放后再次分配出来从而获得一个chunk内部可操作指针。
POC/EXP
How2heap上有两个Overlapping的实例,阅读了源码之后发现本质逻辑是相同的,这里先一起贴出来:还有一个是mmap的overlap,只在分配大小大于MMAP_THRESHOLD
的时候用,mmap chunk的元数据以及分配和释放都涉及到系统调用和内核的内存管理并不参与一般的堆分配。单独放在前面
1 | /* |
实例2是这样的:
1 | /* |
原理分析
首先,这个漏洞能实现的原理是因为glibc中关于chunk一些基本操作的宏本质上是通过一个指针+读取当前chunkhead中的信息实现的。例如next_chunk(p)
是这样定义的:
1 | /* Ptr to next physical malloc_chunk. */ |
还有更多的:
-
prev_chunk
1 | /* Size of the chunk below P. Only valid if prev_inuse (P). */ |
-
是否使用:
1 |
|
所以,overlapping或者说extend/shrink本质上是劫持chunkhead信息中的size域或者prevsize域,来达到欺骗程序,从而操作相邻chunk中内容的目的。
在演示复现中,上面两个程序都是使用了UAF覆写了chunk的size域,将其写为一个更大的值(一般是可用空间之和+0x10的headsize),这样在free的时候,就会将原本物理地址相邻的两个不同chunk视为一个大chunk进行释放(放入unsortedbin中)。然后通过malloc一个两chunk可用空间大小之和大小的chunk,就可以得到我们的overlapping大chunk,换言之就是分配得到了一个包含另一个chunk的更大的chunk,从而实现数据的覆盖写入。实际操作和题目中,可以使用堆溢出来试着实现这一点。
另外,时刻脑子里要有一个堆分布的概念,尤其是是否和topchunk相邻。这个漏洞与bin的种类没有太大关系,只是在fastbin_size范围内时由于prev_inuse存在首个chunk不会轻易和top_chunk合并
参考CTFWiki的说法,这种漏洞也被称为chunk_extend/shrink。核心原理既然是覆盖chunkhead,那我们能做的就不止有覆盖size域一种做法了,还可以覆盖prev_size来进行前向的overlapping,下面是一些可能在how2heap基础上进阶的用法。(来自CTF_Wiki)
-
首先这个漏洞可以先修改再释放也可以先释放再修改,最终效果是一样的。
-
前向overlapping:
1 | int main(void) |
这个前向overlapping是利用了unlink机制实现的。先将prev_inuse位置为零,这样在free一个smallbin大小的chunk的时候会检查其前后是否能够合并,prev_inuse被置零就导致其会以prev_size为指导将p和前一个chunk合并。总之在遇到题目的时候再来更新这里吧。
二编:沟槽的自己把自己绕晕了。首先我们知道64位下chunk是16字节对齐的,size域必定是0x_0这样的形式。我们说的NON_MAIN_ARENA, IS_MMAPPED和PREV_INUSE三位“最低位”,由于小端序的关系