pwnable.kr上的题目tiny,我一直没有能够做出来,所以就决定先看看tiny_easy。tiny_easy与tiny很相似,只是栈变成了可执行的,所以可以将shellcode放在栈上。但是,由于栈的地址被随机化了,我们仍然需通过暴力尝试的方法来克服ASLR。
直到最近,我在网上搜索了解到mmap
地址随机化的一些坑,才明白这两道题目可以通过某些手段来克服ASLR,从而避免暴力尝试。
正常情况下mmap
的随机化
mmap
的方法声明如下:
当第一个参数是0时,会由系统选择一个地址,在这里可以产生随机化。我们编写以下测试代码:
在32位系统和64位系统上编译后运行,发现每次mmap
得到的地址均是不同的,而这正是ASLR所起到的保护作用。
CVE-2016-3672
这个漏洞指出,mmap
在某些情况下不会随机化。具体地,当通过ulimit -s unlimited
将栈的大小设置为无限制后,32位程序(在32位系统和64位系统上)的mmap
不会随机化。再次使用上面的测试代码可以验证这一结论。
这一漏洞的起因,是为了支持旧的32位架构。在那些比较旧的机器上,ASLR只会将栈随机化,而mmap
得到的(如共享库等)地址是固定的。
具体地,阅读内核源码arch/x86/mm/mmap.c
,可以找到以下方法:
可以看到,如果mmap_is_legacy()
返回为真,那么mmap
的基址就是旧的mmap_legacy_base
。而这个mmap_legacy_base
的获取如下:
通过这里的注释也可以看出,如果是32位的程序,那么在32位系统和64位系统上,mmap_legacy_base
均是一个固定的值,不会加上随机的偏移。
综上,我们只要设法使得mmap_is_legacy()
返回真,那么就可以让mmap
不进行随机化。而mmap_is_legacy()
的定义如下:
其中的第二个判断条件,便是检查栈的大小。我们可以通过ulimit
命令来设置这个值。所以,只要将栈设置为unlimited
,那么随后运行的程序的mmap
都会得到固定的地址。进一步来说,这就会使得共享库、vdso等,均处于固定的位置,从而影响ASLR的保护效果。
根据漏洞发现者的介绍,这一问题已经存在了很久,包括最新的Linux仍然受到影响。而根据Redhat的公告,这个漏洞的影响低,近期内应该也不会修复。不过,对于题目tiny和tiny_easy则是非常有价值的,因为可以通过这一手段将vdso的地址固定,从而稳定地进行ROP。
参考文献
- http://hmarco.org/bugs/CVE-2016-3672-Unlimiting-the-stack-not-longer-disables-ASLR.html
- https://access.redhat.com/security/cve/cve-2016-3672