0%

awvs+sqlmap傻瓜注入

本文将从一下几点介绍利用S.E.H写shellcode。

  • S.E.H原理
  • 为什么要利用S.E.H
  • SEH_Shellcode构造方法
  • 实际测试

0x01 S.E.H原理

SEH是一个结构体,这个结构体叫做异常处理结构体,每个结构体中有两个指针,第一个指针建立SEH链表,第二个指针指向异常处理函数,换句话说是异常处理函数句柄。

S.E.H的构造如下

1
2
3
4
5
 --------------------------
|DWORD: Next S.E.H recoder |
--------------------------
|DWORD: Exception handler |
--------------------------

  • S.E.H 按照级别可分为:线程的异常处理、进程的异常处理、系统默认的异常处理(U.E.F),其整体的处理流程为:
  • CPU 执行时发生未捕获异常,内核结果进程的控制权,开始内核态的异常处理。
    内核异常结束,将控制权还给 ring3.
  • ring3 中第一个处理异常的函数是 ntdll.dll 中的 KiUserExceptionDispatcher() 函数。
  • KiUserExceptionDispatcher() 首先检查程序是否处于调试状态。如果程序正在被调试,会将异常交给调试器进行处理。
  • 在非调试状态下,KiUserExceptionDispatcher() 调用 RtlDispatchException() 函数对线程的 S.E.H 链表进行遍历,如果找到能够处理异常的回调函数,将再次遍历先前调用过的 S.E.H 句柄,即 unwind 操作,以保证异常处理机制自身的完整性。
  • 如果栈中所有的 S.E.H 都失败了,且用户曾经使用过 SetUnhandledExceptionFilter() 函数设定进程处理异常,则这个异常处理将被调用。
  • 如果用户自定义的进程异常处理失败,或者用户根本没有定义进程异常处理,那么系统默认的异常处理 UnhandledExceptionFilter() 将会被调用。U.E.F 会根据注册表中相关信息决定是默默地关闭程序,还是弹出错误对话框。

利用S.E.H的shellcode的基本想法就是通过控制输入,利用栈溢出来覆盖S.E.H结构体,

0x02 为什么要利用S.E.H

首先,我们要明确的是本文并不能绕过数据执行保护,利用S.E.H绕过的是对于安全cookie的检测。

安全cookie是什么?

所谓安全cookie是在进入函数压栈的时候,在压栈之中加入一个cookie,这个cookie很有可能是个随机的值,因而无法构造,在函数执行返回的时候程序检查这个cookie的值是否正确,如果正确,说明栈没有溢出,不正确,说明发生了栈溢出。这就是利用cookie进行的栈保护。

那么为什么利用S.E.H可以绕过这个保护呢?

答案很明显,当函数产生异常的时候,在函数返回之前,就触发了异常处理,而跳过了检查cookie的这一步。

0x03 SEH_Shellcode 构造方法

既然是覆盖SEH结构体,那就要自己构造一个结构体冒充原来的S.E.H结构。

经典的用法是这样的:

1
2
eb 06 90 90 (jmp 06 nop nop)
xx xx xx xx (ppr_address)

ppr_address的意思是pop pop ret三个命令所在的地址。这个小插件几乎会在每一个程序中出现。

看到这里的时候其实有一点我是没有弄懂的,因为之前以为Next S.E.H recoder这个变量是指针,指针就应该是地址,为什么是一句命令呢?后来我才知道这个DWORD实际上是一段命令