UAF即为Use After Free。也就是使用了已经被释放的内存,最终导致内存崩溃或任意代码被执行的漏洞。UAF漏洞常见于浏览器中,如IE、Chrome、Firefox等。
当分配有内存的指针被释放后,如果指针没有及时置空。那么此时的指针就被称为“悬挂指针”,也就是俗称的野指针。引用此类指针就会出现各种意外情况。举例以下代码。
#include "stdafx.h"
#include <stdlib.h>
#include <tchar.h>
#include <windows.h>
#pragma runtime_checks( "[runtime_checks]", off )
int main()
{
HANDLE hHeap;
char *heap, *heap1, *heap2;
hHeap = HeapCreate(HEAP_GENERATE_EXCEPTIONS, 0x1000, 0xffff);
getchar();
heap = (char*)HeapAlloc(hHeap, 0, 0x10);
heap1 = (char*)HeapAlloc(hHeap, 0, 0x10);
heap2 = (char*)HeapAlloc(hHeap, 0, 0x10);
printf("heap addr:0x%08X\n", heap);
HeapFree(hHeap, 0, heap);
HeapFree(hHeap, 0, heap2);
HeapFree(hHeap, 0, heap1);
HeapFree(hHeap, 0, heap2); //双重释放
HeapDestroy(hHeap);
return 0;
}
当heap2重复释放时,就会引发程序崩溃。HeapFree(hHeap, 0, heap2);只是其中第一种方式。根本原因在于heap2第一次释放完后就已经变成一个野指针。后面再次使用就会出现意外。
假如heap2是一个类,而其中类中保存着虚表函数。当你释放掉这个类,并再次调用时这个野指针类时,就会导致从虚表中读取错误的虚函数。可能就会引发崩溃。但此时如果有人修改了这个地方的内存,当你调用的时候,可能就不是崩溃,而是被指引到其他的恶意地址。
至于如何修改这个地方的内存,方法就是再重新申请一个相同的堆,程序就会重复利用原来的内存。此时再调用野指针的地方刚好就调用到了你新分配的堆上。而这个堆就是你自己的,想怎么改就怎么改,这样就可以实现控制程序的执行流程。
此类方法是可以在js中进行实现,因为js本身是解释型语言,如果浏览器不提供api,js是不能胡乱执行的。但如果利用UAF漏洞拿到了浏览器的执行权,我们就可以成功执行shellcode,调用自己想要调用的任何系统API,直接借用浏览器对计算机进行控制。
UAF诞生之际,主流浏览器都发现了大量此类bug,而IE浏览器便是首当其冲。而与UAF同时出现的还有Heap Spray(堆喷)技术。而堆喷技术就是无差别攻击,占用大量堆内存,大到将0x0C0C0C0C包含在内,使我们在浏览器内部创造了一个固定地址的可控数据。与任何其他形式的漏洞相结合,都可以极大提高命中shellcode的概率,而且在这大量堆中,还分布了无数的小型shellcode块,这样同样会有意外惊喜。