作者: CSysSec出品
CSysSec注: 本系列文章译自安全自由工作者Sploitfun的漏洞利用系列博客,从经典栈缓冲区漏洞利用堆漏洞利用,循序渐进,是初学者不可多得的好材料,本系列所有文章涉及的源码可以在这里找到。CSysSec计划在原基础上不断添加相关漏洞利用技术以及相应的Mitigation方法,欢迎推荐或自荐文章。
转载本文请务必注明,文章出处:《Linux(X86)漏洞利用系列-Malloc Maleficarum堆溢出)》与作者信息:CSysSec出品
- 0X01 House of Mind
- 0X02 House of Force
- 0x03 House of Spirit
阅读基础:
在2004后期,’glibc malloc’ 被强化. 像unlink类似的一些技术过时后,攻击者变得无所适从。但仅在2005年后期,’Phantasmal Phatasmagoria’又开始提出下面的一系列技术来成功利用堆溢出。
- House of Prime
- House of Mind
- House of Force
- House of Lore
- House of Spirit
0X01 House of Mind
通过这项技术,攻击者利用构造的假arena( Fake Arena)来欺骗’glibc malloc’。通过构造假的arena以让未排序bin的fd含有free的GOT表项地址(12)。 如此一来,漏洞程序中释放的free函数的GOT表项被shellcode地址覆盖。成功覆盖GOT之后,任何时候调用漏洞程序的free函数,shellcode就会执行。
假设条件: 由于不是所有的堆溢出漏洞程序都能被house of mind技术成功利用,以下是成功利用的假设条件:
- 1.调用一系列malloc,直到一个chunk的地址对其多倍的HEAP_MAX_SIZE,进而生成一块内存区域能被攻击者控制。在这块内存区域里,可以发现假的heap_info结构体。假的heap_info的arena指针ar_ptr将会指向假的arena。这样一来,假的arena和heap_info内存区域都会被攻击者控制。
- 2.Size域(其arena指针是prereq 1)被攻击者控制的一个chunk必须要释放。
- 紧邻上述被释放chunk的下一个chunk不能是一个top chunk.
漏洞程序:这个漏洞程序满足上述假设条件
|
|
上述漏洞程序的堆内存如下所示:
注:本系列所有文章中第[N]行代码指的的代码中显示/*[N]*/的位置。
漏洞程序中的第[3]行就是堆溢出发生的地方。用户输入存储在chunk1的内存指针处,共1MB大小。为了成功利用堆溢出,攻击者要按照以下顺序提供用户输入:
- Fake arena
- Junk
- Fake heap_info
- Shellcode
漏洞利用程序:这个程序生成攻击者的数据文件
|
|
攻击者生成数据文件作为用户输入,漏洞程序的堆内存变为如下所示:
攻击者生成数据文件作为用户输入,当漏洞程序的第[4]行执行时,’glibc malloc’会做以下事情:
- 调用arena_for_chunk 宏获取正被释放中的chunk的arena
- arena_for_chunk: 如果NON_MAIN_ARENA (N)位没有被设置,返回主arena(main arena)。如果已经设置,通过将chunk地址对其多倍的HEAP_MAX_SIZE访问相应的heap_info结构体。然后,返回获取heap_info结构体的arena指针。在我们的例子中,ON_MAIN_ARENA (N)被攻击者设置,所以得到了正要被释放的chunk的heap_info结构体(位于地址0x0810000处)。heap_info的ar_ptr = Fake arena的基地址(0x0804a018)。
- 以arena指针和chunk地址作为参数调用_int_free。在我们的例子中,arena指针指向fake arena, 因此fake arena和chun地址作为参数传递到_int_free。
- Fake arena: 以下是fake arena中需要被攻击者覆盖的必要域:
+ [Mutex](https://github.com/sploitfun/lsploits/blob/master/hof/hom/malloc_snip.c#L14)- 必须处于解锁状态(unlocked state). + [Bins](https://github.com/sploitfun/lsploits/blob/master/hof/hom/malloc_snip.c#L29)- 未排序的bin fd必须含有free函数的GOT表项地址-12 + [Top](https://github.com/sploitfun/lsploits/blob/master/hof/hom/malloc_snip.c#L23)- + Top地址必须不等于正被释放的chunk地址 + Top地址必须大于下一个chunk地址 + [System Memory](https://github.com/sploitfun/lsploits/blob/master/hof/hom/malloc_snip.c#L41)- System memory必须大于下一个chunk的size。
- _int_free():
- 如果chunk没有被映射(non mmap’d),获取锁。在我们的例子中,chunk没有被映射并且fake arena的mutex lock也被成功获取。
- 合并:
+ 查找前一个chunk是否空闲,如果空闲,则合并。在我们的例子中,前一个chunk被分配,因此不能向后合并。 + 查找下一个chunk是否空闲,如果空闲,则合并。在我们的例子中,下一个chunk被分配,因此可以向前合并。
- 将当前释放的chunk放入未被排序的bin中。在我们的例子中,fake arena的未排序bin的fs含有free函数的GOT表项地址(12),被拷贝到‘fwd’中。之后,当前被释放的chunk的地址被拷贝到’fwd->bk’中。 bk位于malloc_chunk的12偏移处,因此12加入到’fwd’值中(ie. free- 12+12)。现在free的GOT表项被当前释放的chunk地址修改。由于攻击者已经将shellcode放入到当前被释放的chunk中,从现在开始,只要free被调用,攻击者的shellcode就会执行。
- _int_free():
- Fake arena: 以下是fake arena中需要被攻击者覆盖的必要域:
把攻击者的数据文件当做用户输入,执行上述漏洞代码,就会执行攻击者的shellcode,如下所示
|
|
保护: 现如今,由于’glibc malloc’ 被强化(got hardened), house of mind技术不再有效。为阻止house of mind带来的堆溢出,添加了一下检查:
- 损坏的chunks: 未排序的bin的第一个chunk的bk指针必须执行未排序的bin,否则,’glibc malloc’就会抛出一个损坏的chunk错误。
|
|
0X02 House of Force
通过这项技术,攻击者滥用top chunk的size,利用top chunk欺骗’glibc malloc’来服务大量的内存请求(比堆的系统内存大小要打)。如此一来,当发出一个新的malloc请求,free的GOT表项就会被shellcode地址覆盖。从现在开始,只要free被调用,shellcode就会执行。
假设条件: 需要三个malloc调用才能成功利用house of force 技术:
- Malloc 1: 攻击者必须能控制top chunk的大小。 这堆样就能在这个分配的chunk(物理上在top chunk前面)中溢出。
- Malloc 2: 攻击者必须能控制这个malloc请求的大小
- Malloc 3: 用户输入必须拷贝到这个已经分配的chunk中。
漏洞程序:这个漏洞程序满足上述假设条件.
|
|
漏洞程序的堆内存如下所示:
漏洞程序的第[2]行就是堆溢出发生的地方。为了能成功利用堆溢出,攻击者要提供下面的命令行参数:
- argv[1] - Shellcode + Pad + Top chunk size被拷贝到第一个malloc chunk
- argv[2]- 传递给第一个malloc chunk的size参数
- argv[3]- 用户输入被拷贝到第三个malloc chunk
漏洞利用程序:
|
|
一旦攻击者的命令行参数被拷贝到堆中,漏洞程序的堆内存变为下图所示:
有了攻击者这些参数,就会发生下面的事情:
第[2]行覆盖top chunk的size域:
- 攻击者参数(argv[1] – Shellcode + Pad + 0xFFFFFFFF)拷贝到堆缓冲区'buf1'。 由于argv[1]大于256,top chunk的size域被“0xFFFFFFFF"覆盖
第[3]行通过top chunk代码分配一个非常大的内存块
- 请求一个大内存块的目的是在分配后top chunk必须位于free的GOT表项的前8个字节。因此,只要再多一个内存分配请求而(第[4]行)就可以帮助我们覆盖free的GOT表项了。
- 攻击者参数(argv[2] – 0xFFFFF744)作为参数传递给第二个malloc调用(第[3]行)。size参数可以用下面的方式来计算
- size = ((free-8)-top)
- 在这里
- free指的是可执行文件’vuln’中free的GOT表项,ie)free = 0x08049858
- top指的是当前top chunk(在第一次malloc之后,第[1]行) ie)top = 0x0804a108.
- 因此size = ((0x8049858-0x8)-0x804a108) = -8B8 = 0xFFFFF748
- 当size = 0xFFFFF748时,我们的目标是将新的tip chunk 的8个字节放到free的GOT表项前面,可以通过这样做到
+ (0xFFFFF748+0x804a108) = 0x08049850 = (0x08049858-0x8)
- 当攻击者传递size参数(0xFFFFF748)时,’glibc malloc’将size 转化为 可用的size(usable size)0xfffff750。因此新的top chunk size将会位于0x8049858而不是0x8049850. 攻击者将传递0xFFFFF744(而不是0xFFFFF748)作为size参数,得到转化后的参数是‘0xFFFFF748’,这正是我们需要的。
在第[4]行:
- 由于第三行中的top chunk指向0x8049850,一个256字节的新的内存分配请求会让’glibc malloc’返回0x8049858 ,其会被拷贝到buf3中
在第[5]行:
- 将buf1的地址拷贝到buf3中,导致GOT覆盖。因此对free的调用(第[6]行)就会导致shellcode的执行。
用攻击者的命令行参数再执行上述漏洞程序就会导致shellcode执行,如下所示:
|
|
保护: 至今为止,还没有针对这项技术的保护措施。所以就算用最新的glibc编译,这项技术也可以帮助我们成功利用堆溢出。
0X03 House of Spirit
通过这项技术,攻击者欺骗’glibc malloc’返回一个在栈(不是堆)中的chunk。这就允许攻击者覆盖存储在栈中的返回地址。
假设条件: 由于不是所有的堆溢出漏洞程序都能被house of spirit技术成功利用,以下是成功利用的假设条件
- 缓冲区溢出覆盖一个变量,变量中含有’glibc malloc’返回的chunk地址
- 上述的chunk必须要被释放。攻击者必须控制被释放chunk的size。将chunk的size等于下一个分配的chunk的size。
- 分配一个chunk
- 用户输入必须拷贝到上述分配的chunk中
漏洞程序: 漏洞程序满足上述假设条件
|
|
漏洞程序的栈布局如下所示:
程序的第[3]行是缓冲区溢出发生的地方。为了能成功利用漏洞程序,攻击者提供下面的命令行参数:
- argv[1] = Shell Code + Stack Address + Chunk size
|
|
攻击者提供参数后,漏洞程序的栈布局如下所示:
攻击者提供参数后,我们来看看返回地址是如何被覆盖的
第[3]行:缓冲区溢出
- 这里,攻击者的输入’argv[1]’拷贝到字符缓冲区’name’中。由于攻击者的输入大于44,ptr1变量和local_age分别被栈地址和chunk size覆盖。
- 栈地址-0xbffffdf0- 当第[5]行被执行时,攻击者欺骗’glibc malloc’返回这个地址
- Chunk size - 0x30- 当第[4]行执行时,这个chunk size被用来欺骗’glibc malloc’ ,往下看。
第[4]行:将栈区域加入到’glibc malloc’的 fast bin
- free()调用_int_free)_。 缓冲区溢出后,ptr1 = 0xbffffdf0(而不是0x804aa08)。被覆盖的ptr1作为参数传递非free()。这样一来,可以欺骗’glibc malloc’来释放位于栈中的内存区域。正要被释放的栈区域位于ptr1-8+4 处,而这已经被攻击者用0x30覆盖,因此’glibc malloc’把这个chunk当做fast chunk(因为48<64),然后将被释放的chunk插入到位于索引4处的fast binlist的前端。
第[5]行:获取栈区域(第[4]行中被添加)
- 对40的分配请求被checked_request2size转化为对48的请求。由于可用大小(usable size) ‘48’属于fast chunk中,可以获取它对应的fast bin(位于索引4处)。Fast bin的第一个chunk被移除并返回为用户。第一个chunk什么都不是,但在第[4]行执行期间,栈区域被添加进去了。
第[6]行:覆盖返回地址
- 将攻击者的’argv[1]’拷贝到栈区域(被’glibc malloc’返回),其起始地址是是0xbffffdf0. argv[1]的前16个字节是:
- xeb\x0e - Jmp by 14字节
- \x41\x41\x41\x41\x41\x41\x41\x41\x41\x41 – Pad
- \xb8\xfd\xff\xbf - 存储在栈中的返回地址被此值覆盖。因此,在fvuln执行之后EIP变为0xbffffdb8- 这块区域俺有jmp指令,随后就是触发shell的shellcode!
利用攻击者的参数执行上述漏洞程序,将会执行shellcode,如下所示:
|
|
保护:至今为止,还没有针对这项技术的保护措施。所以就算用最新的glibc编译,这项技术也可以帮助我们成功利用堆溢出。
###House of Prime 待更新 House of Lore** 待更新
注意:出于演示目的,上述漏洞程序编译时关闭以下保护机制:
参考
转载本文请务必注明,文章出处:《Linux(X86)漏洞利用系列-Malloc Maleficarum堆溢出)》与作者信息:CSysSec出品