mmap的随机化
Posted rtoax
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了mmap的随机化相关的知识,希望对你有一定的参考价值。
http://rk700.github.io/2016/11/22/mmap-aslr/
目录
pwnable.kr上的题目tiny,我一直没有能够做出来,所以就决定先看看tiny_easy。tiny_easy与tiny很相似,只是栈变成了可执行的,所以可以将shellcode放在栈上。但是,由于栈的地址被随机化了,我们仍然需通过暴力尝试的方法来克服ASLR。
直到最近,我在网上搜索了解到mmap
地址随机化的一些坑,才明白这两道题目可以通过某些手段来克服ASLR,从而避免暴力尝试。
正常情况下mmap
的随机化
mmap
的方法声明如下:
void *mmap(void *addr, size_t length,
int prot, int flags,
int fd, off_t offset);
当第一个参数是0时,会由系统选择一个地址,在这里可以产生随机化。我们编写以下测试代码:
#include <sys/mman.h>
#include <stdio.h>
int main(int argc, char *argv[]) {
char mapsPath[256];
void *addr = mmap(0, 4096, PROT_READ|PROT_WRITE, MAP_ANONYMOUS|MAP_PRIVATE, -1, 0);
printf("mmap at %p\\n", addr);
return 0;
}
在32位系统和64位系统上编译后运行,发现每次mmap
得到的地址均是不同的,而这正是ASLR所起到的保护作用。
CVE-2016-3672
这个漏洞指出,mmap
在某些情况下不会随机化。具体地,当通过ulimit -s unlimited
将栈的大小设置为无限制后,32位程序(在32位系统和64位系统上)的mmap
不会随机化。再次使用上面的测试代码可以验证这一结论。
这一漏洞的起因,是为了支持旧的32位架构。在那些比较旧的机器上,ASLR只会将栈随机化,而mmap
得到的(如共享库等)地址是固定的。
具体地,阅读内核源码arch/x86/mm/mmap.c
,可以找到以下方法:
/* * This function, called very early during the creation of a new * process VM image, sets up which VM layout function to use: */
void arch_pick_mmap_layout(struct mm_struct *mm)
{
mm->mmap_legacy_base = mmap_legacy_base();
mm->mmap_base = mmap_base();
if (mmap_is_legacy()) {
mm->mmap_base = mm->mmap_legacy_base;
mm->get_unmapped_area = arch_get_unmapped_area;
mm->unmap_area = arch_unmap_area;
} else {
mm->get_unmapped_area = arch_get_unmapped_area_topdown;
mm->unmap_area = arch_unmap_area_topdown;
}
}
可以看到,如果mmap_is_legacy()
返回为真,那么mmap
的基址就是旧的mmap_legacy_base
。而这个mmap_legacy_base
的获取如下:
/* * Bottom-up (legacy) layout on X86_32 did not support randomization, X86_64 * does, but not when emulating X86_32 */
static unsigned long mmap_legacy_base(void)
{
if (mmap_is_ia32())
return TASK_UNMAPPED_BASE;
else
return TASK_UNMAPPED_BASE + mmap_rnd();
}
通过这里的注释也可以看出,如果是32位的程序,那么在32位系统和64位系统上,mmap_legacy_base
均是一个固定的值,不会加上随机的偏移。
在5.10.13中已经修复了:
static unsigned long mmap_legacy_base(unsigned long rnd,
unsigned long task_size)
{
return __TASK_UNMAPPED_BASE(task_size) + rnd;
}
综上,我们只要设法使得mmap_is_legacy()
返回真,那么就可以让mmap
不进行随机化。而mmap_is_legacy()
的定义如下:
static int mmap_is_legacy(void)
{
if (current->personality & ADDR_COMPAT_LAYOUT)
return 1;
if (rlimit(RLIMIT_STACK) == RLIM_INFINITY)
return 1;
return sysctl_legacy_va_layout;
}
其中的第二个判断条件,便是检查栈的大小。我们可以通过ulimit
命令来设置这个值。所以,只要将栈设置为unlimited
,那么随后运行的程序的mmap
都会得到固定的地址。进一步来说,这就会使得共享库、vdso等,均处于固定的位置,从而影响ASLR的保护效果。
根据漏洞发现者的介绍,这一问题已经存在了很久,包括最新的Linux仍然受到影响。而根据Redhat的公告,这个漏洞的影响低,近期内应该也不会修复。不过,对于题目tiny和tiny_easy则是非常有价值的,因为可以通过这一手段将vdso的地址固定,从而稳定地进行ROP。
参考文献
以上是关于mmap的随机化的主要内容,如果未能解决你的问题,请参考以下文章