[04] HEVD 内核漏洞之IntegerOverflow
作者:huity
出处:https://www.cnblogs.com/huity35/p/11252574.html
版权:本文版权归作者所有。文章在博客园、看雪、个人博客同时发布。
转载:欢迎转载,但未经作者同意,必须保留此段声明;必须在文章中给出原文连接;否则必究法律责任。
0x00 前言
0x01 漏洞原理
整数溢出
Demo(基于栈的整数溢出)
#include "stdio.h" #include "string.h" int main() { int i; char buf[8]; unsigned short int size; char overflow[65550]; memset(overflow,65,sizeof(overflow)); printf("Input the num:\n"); scanf("%d",&i); size=i; printf("size:%d\n",size); printf("i:%d\n",i); if(size>8) return -1; memcpy(buf,overflow,i);//栈溢出 return 0; }
上面的demo中,size为无符号短整数(0-65535),当我们输入大于65535是就会造成溢出,例如我们输入65536,最终得到的size为0,从而绕过边界检查,但是在memcpy函数复制数据时,使用的是int类型的i参数,导致栈溢出。
Demo(基于堆的整数溢出)
#include "stdio.h" #include "string.h" #include "Windows.h" int main() { int heap; unsigned short int size; char* v1,*v2; HANDLE HeapHandle; printf("int put size: \n"); scanf("%d",&size); HeapHandle = HeapCreate(HEAP_GENERATE_EXCEPTIONS,0x100,0xfff); if(size <=0x50) { size-=5; printf("size:%d\n",size); v1=(char*)HeapAlloc(HeapHandle,0,size); v2=(char*)HeapAlloc(HeapHandle,0,0x50); } HeapFree(HeapHandle,0,v1); HeapFree(HeapHandle,0,v2); return 0; }
上述demo中size为unsigned short int,小于5时,例如,当size=2时,size减去5则得到负数,但size取值范围导致无法识别负数,而得到正数65533,而分配得到大的堆块,从而溢出导致覆盖到后面的堆管理结构。
分析
__declspec(safebuffers) NTSTATUS TriggerIntegerOverflow( _In_ PVOID UserBuffer, _In_ SIZE_T Size) { ULONG Count = 0; NTSTATUS Status = STATUS_SUCCESS; ULONG BufferTerminator = 0xBAD0B0B0; ULONG KernelBuffer[BUFFER_SIZE] = { 0 };//512*4=2048 SIZE_T TerminatorSize = sizeof(BufferTerminator);//4 PAGED_CODE(); __try { // // UserBuffer 为Ring3地址 其中前面均用A填充,倒数8字节开始的4字节为Payload地址 最后四字节为0xBAD0B0B0 ProbeForRead(UserBuffer, sizeof(KernelBuffer), (ULONG)__alignof(UCHAR)); DbgPrint("[+] UserBuffer: 0x%p\n", UserBuffer); DbgPrint("[+] UserBuffer Size: 0x%X\n", Size); DbgPrint("[+] KernelBuffer: 0x%p\n", &KernelBuffer); DbgPrint("[+] KernelBuffer Size: 0x%X\n", sizeof(KernelBuffer)); #ifdef SECURE // // 安全注意:这是安全的,因为开发人员没有对用户提供的值进行任何算术运算。 //相反,开发人员从KernelBuffer的大小减去ULONG的大小,即x86上的4。 因此,不会发生整数溢出,并且此检查不会失败 if (Size > (sizeof(KernelBuffer) - TerminatorSize)) { DbgPrint("[-] Invalid UserBuffer Size: 0x%X\n", Size); Status = STATUS_INVALID_BUFFER_SIZE; return Status; } #else DbgPrint("[+] Triggering Integer Overflow (Arithmetic Overflow)\n"); // 注意这里是有漏洞的版本 if ((Size + TerminatorSize) > sizeof(KernelBuffer))//FFFFFFFF+4 = 00000003 { DbgPrint("[-] Invalid UserBuffer Size: 0x%X\n", Size); Status = STATUS_INVALID_BUFFER_SIZE; return Status; } #endif //实现拷贝操作 while (Count < (Size / sizeof(ULONG))) { if (*(PULONG)UserBuffer != BufferTerminator) { KernelBuffer[Count] = *(PULONG)UserBuffer; UserBuffer = (PULONG)UserBuffer + 1; Count++; } else { break; } } } __except (EXCEPTION_EXECUTE_HANDLER) { Status = GetExceptionCode(); DbgPrint("[-] Exception Code: 0x%X\n", Status); } return Status; }
函数中比较了用户提交缓冲区长度和内核缓冲区长度,在有漏洞的版本中,这一比较采用了:
if ((Size + TerminatorSize) > sizeof(KernelBuffer))
我们在Windbg中同样可以看到这一问题:
97c4ba9f 8b450c mov eax,dword ptr [ebp+0Ch] 97c4baa2 0345d4 add eax,dword ptr [ebp-2Ch] 97c4baa5 3d00080000 cmp eax,800h kd> r eax eax=00000003
kd> r eax=00000003 ebx=00000000 ecx=ffffffff edx=0000004d esi=86e61528 edi=860fff80 eip=97c80adb esp=9398f268 ebp=9398fab0 iopl=0 nv up ei ng nz na pe cy cs=0008 ss=0010 ds=0023 es=0023 fs=0030 gs=0000 efl=00000287 HEVD!TriggerIntegerOverflow+0x16b: 97c80adb 8b450c mov eax,dword ptr [ebp+0Ch] ss:0010:9398fabc=ffffffff kd> dd 9398fab0 9398fab0 9398fad4 97c80956 00408028 ffffffff 9398fac0 00000001 c0000001 ffffffff 00000000 9398fad0 00408028 9398fafc 97c800ae 86e6a870 9398fae0 86e6a8e0 00000001 00000000 00222027 9398faf0 00000024 c00000bb 86e6a8e0 9398fb14 9398fb00 83e7f593 86e61528 86e6a870 86e6a870 9398fb10 86e61528 9398fb34 8407399f 860fff80 9398fb20 86e6a870 86e6a8e0 00000094 0498fbac
0x02 漏洞利用
0x03 漏洞反思
0x04 链接