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

本文共 [328] 位读者顶过

文章说明

此文是翻译文章。另外加上了自己学习过程中的一些笔记,方便自己以后查阅。[出自:jiwo.org]
原文地址:https://csandker.io/2021/02/21/Offensive-Windows-IPC-2-RPC.html

介绍和历史

微软根据命名管道上的RPC通讯对DCE模型进行了调整,以适应其编程方案,并使其在Windows 95中的实现日趋成熟。
过去,您可能想知道为什么他们要基于命名管道进行通信,因为Microsoft于1994年才提出了一种称为本地过程调用(LPC)的新技术。

过程调用上叫做“本地过程调用”的方法,对吗?……嗯,是的,LPC是一个合理的选择(我想他们最初是使用LPC的),但是LPC有一个关键缺陷:它不支持(现在仍然不支持异步调用,当我最终完成我的LPC / ALPC帖子时,会进行更多讨论……),这就是Microsoft基于命名管道的原因。 …

远程过程调用(RPC)是一种使客户端和服务器之间跨进程和机器边界的数据通信(网络通信)成为一项技术。因此,RPC是一种进程间通信(IPC)技术。此类别中的其他技术例如LPC,ALPC或命名管道。

顾名思义,RPC用于调用远程服务器以交换/传递数据或触发远程例程。在这种情况下,术语“远程”不描述对通信的要求。 RPC服务器不必位于远程计算机上,并且理论上甚至不必处于不同的进程中(尽管这是有道理的)。

从理论上讲,您可以在DLL中实现RPC服务器和客户端,将它们加载到同一进程中并交换消息,但是由于消息仍会通过进程外的其他组件例如内核, (稍后会详细介绍)此外,RPC服务器不必位于远程计算机上,但也可以从本地客户端调用。

在此博客文章中,您可以与我一起探索RPC的内部,它的工作方式和操作以及如何实现和攻击RPC客户端和服务器。

这篇文章是从进攻角度出发的,旨在从攻击者的角度介绍RPC攻击面的最相关方面。可以在 乔纳森·约翰逊(Jonathan Johnson)的文章上https://ipc-research.readthedocs.io/en/latest/subpages/RPC.html上找到有关RPC的更具防御性的观点

下面的帖子将包含我的示例实现中对代码的一些引用,所有这些代码都可以在这里找到:
https://github.com/csandker/InterProcessCommunication-Samples/tree/master/RPC/CPP-RPC-Client-Server

微软的RPC实施基于开放软件基金会(OSF)于1993年开发的分布式计算环境(DCE)标准的RPC实施。

无论如何,这里的教训是(我想)有时候最好停下来做一件事,理清头绪,在别的事情上取得进展,然后再迷失在尚未准备向您揭示它的奥秘的事情上。

RPC 消息传递

RPC 是一种客户端服务器技术,其消息传递架构类似于 COM(组件对象模型),在较高级别上由以下三个组件组成:

负责注册 RPC 接口和相关绑定信息的服务器和客户端流程(稍后将对此进行更多了解)
负责整理传入和传出数据的服务器和客户端存根
服务器和客户端的 RPC 运行时库 (rpcrt4.dll),它使用指定的协议将存根数据通过网络端口发送。

此消息架构的视觉概述可在https://docs.microsoft.com/en-us/windows/win32/rpc/how-rpc-works找到,如下所示:

在这里插入图片描述
稍后,在“ RPC通信流”部分中,我将提供从创建RPC服务器到发送消息所涉及的步骤的概述,但是在进行深入研究之前,我们需要澄清一些RPC术语。

当我们深入研究RPC的内部时, 为了与RPC相处,必须了解以下几点。

如果您迷上了新术语和API调用,而您又无法顺其自然,则可以随时跳到RPC 通讯流部分,以了解这些东西在通信链中的位置。

RPC 协议序列

RPC 协议序列是一个恒定的字符串,用于定义 RPC 运行时间应使用哪些协议来传输消息。

此字符串定义应使用哪些 RPC 协议、传输和网络协议。

微软支持以下三个 RPC 协议:

网络计算架构连接导向协议 (NCACN)
网络计算架构数据图协议 (NCADG)
网络计算架构本地远程程序呼叫 (NCALRPC)

在大多数情况下,跨系统边界进行连接时,您会发现 NCACN,而 NCALRPC 建议用于本地 RPC 通信。

协议序列是从上述部分组装的定义恒定字符串,例如基于 TCP 数据包的面向连接通信的ncacn_ip_tcp。

RPC 协议序列常数的完整列表可在:https://docs.microsoft.com/en-us/windows/win32/rpc/protocol-sequence-constants找到。

相关的协议序列如下所示:

ncacn_ip_tcp 面向连接的传输控制协议/互联网协议(TCP/IP)
ncacn_http 以连接为导向的 TCP/IP 使用微软互联网信息服务器作为 HTTP 代理
ncacn_np 以连接为导向的命名管道(通过 SMB.)
ncadg_ip_udp 数据报(无连接)用户数据图协议/互联网协议(UDP/IP)
ncalrpc 本地过程调用 (通过Windows ALPC)
.

RPC接口

为了建立通信通道,RPC运行时需要知道服务器提供的方法(又称“功能”)和参数以及客户端要发送的数据。 这些信息在所谓的“接口”中定义。

旁注:如果您熟悉COM中的接口,那是同一回事。

为了了解如何定义接口,让我们以示例代码中的示例为例: https://github.com/csandker/InterProcessCommunication-Samples/blob/master/RPC/CPP-RPC-Client-Server/RPC-Interface1/Interface1-Implicit.idl

首先要注意的是,接口是在接口定义语言(IDL)文件中定义的。 稍后,Microsoft IDL编译器(midl.exe)将其中的定义编译为服务器和客户端可以使用的头文件和源代码文件。

接口标头对于给定的注释相当不言自明-现在暂时忽略implicit_handle指令,我们很快就会进入隐式和显式句柄。

接口的主体描述了该接口公开的方法,它们的返回值和它们的参数。 Output函数的参数定义中的[in,string]语句不是强制性的,但有助于理解此参数的用途。

旁注:您还可以在应用程序配置文件(ACF)中指定各种接口属性。 其中一些诸如绑定类型(显式与隐式)可以放置在IDL文件中,但是对于更复杂的接口,您可能希望为每个接口添加一个额外的ACF文件。

RPC绑定

一旦您的客户端连接到RPC服务器(稍后我们将介绍如何完成此操作),您就可以创建Microsoft所谓的“绑定”。 或用微软的话来说:

绑定是在客户端程序和服务器程序之间创建逻辑连接的过程。 构成客户端和服务器之间的绑定的信息由称为绑定句柄的结构表示。

一旦我们对上下文进行了绑定,绑定句柄的术语就会变得更加清晰。 从技术上讲,存在三种类型的绑定句柄: 隐式,显示,自动·

旁注:您可以按照此处的描述实现自定义绑定句柄,但由于本文的情况很少见,并且默认类型很好,因此我们在本文中将其忽略。

隐式绑定句柄允许您的客户端连接到特定的RPC服务器并与之通信(由IDL文件中的UUID指定)。 缺点是隐式绑定不是线程安全的,因此多线程应用程序应使用显式绑定。 隐式绑定句柄在IDL文件中定义,如上面的示例IDL代码或我的示例隐式接口中所示。

注意:如果您使用的是知名端点,您也可以通过RpcServerInqBindings 和RpcEpRegister 注册,在您当地的 RPC 端点 映射器上注册您的 RPC 服务器。如果您愿意的话。您不需要这样做,您的客户端就可以连接,但您可以。

如果您想了解更多有关此内容,请在此处找到有关此主题的 Microsoft 文
档:https://docs.microsoft.com/en-us/windows/win32/rpc/specifying-endpoints

RPC 通信流

综上所述,可将通信流量概括为:

1.服务器注册界面,例如使用RpcServerRegisterIf2,这里可以设置或者不设置安全回调

2.服务器使用 RpcServerUseProtseq & RpcServerInqBindings 创建绑定信息(知名端点可选)

3.服务器使用RpcEpRegister 注册端点(知名端点可选)

4.服务器可以使用 RpcServerRegisterAuthInfo注册认证信息(可选)

5.服务器使用RpcServerListen监听客户端连接。

6.客户端使用 RpcStringBindingCompose & RpcBindingFromStringBinding创建一个绑定句柄。

7.客户端RPC运行时库通过查询服务器主机系统上的端点映射器来查找服务器进程(仅对于动态端点是必需的)

8.客户端可以使用 RpcBindingSetAuthInfo来验证绑定句柄(可选)。

9.客户端通过调用所用接口中定义的功能之一进行RPC调用 。

10.客户端RPC运行时库在NDR运行时的帮助下以NDR格式封送参数,并将其发送到服务器,

11.服务器的RPC运行时库将经过编组的参数提供给存根,存根将对它们进行封送处理,然后将其传递给服务器例程。

12.当Server例程返回时,存根获取[out]和[in,out]参数(在接口IDL文件中定义)和返回值,将它们编组,然后将经过封送处理的数据发送到Server的RPC运行时库, 将它们转移回客户端。
在这里插入图片描述

示例实现

如开头所述,以上示例摘自我的示例实现,可从以下网站获取:
https://github.com/csandker/InterProcessCommunication-Samples/tree/master/RPC/CPP-RPC-Client-Server。
在此版本库中,您将找到以下示例实现:

基本的未经身份验证的服务器,支持未经身份验证的隐式绑定
基本的未经身份验证的客户端,支持未经身份验证的隐式绑定
基本服务器支持未经身份验证的显式绑定
支持身份验证的显式绑定的基本服务器
基本客户端支持不带QOS的经过身份验证的显式绑定
基本客户端支持通过QOS进行身份验证的显式绑定

下面是一个例子
在这里插入图片描述

访问矩阵

好吧,如果您理解了以上所有术语,那么这里的访问矩阵就可以直观地看到哪个客户端可以连接到哪个服务器。
在这里插入图片描述

注意:您只能将隐式客户端连接到隐式服务器,将显式客户端连接到显式服务器。 否则,您会收到错误1717(RPC_S_UNKNOWN_IF)

攻击面

最后,在讨论完RPC内部原理之后,让我们谈谈RPC的攻击面。

显然,RPC通讯链中的任何地方都可能存在错误和0day漏洞,这始终取决于具体案例分析以了解其利用潜力,但是一般RPC设计概念也有一些利用潜力,我将在下面突出显示。

寻找有趣的目标

好吧,在我们开始思考可以使用RPC进行哪些进攻性游戏之前,我们需要首先找到合适的目标。

让我们深入研究如何在您的系统上找到RPC服务器和客户端。

PRC服务器

要重述服务器是通过指定所需的信息(协议序列和端点地址)并调用Windows API来构建必要的内部对象并启动服务器来构建的。 考虑到这一点,在本地系统上查找RPC服务器的最简单方法是查找导入这些RPC Windows API的程序。

一种简单的方法是使用Visual Studio现在附带的DumpBin实用程序。
https://docs.microsoft.com/en-us/cpp/build/reference/dumpbin-reference?view=msvc-160

以下是在最近的Windows10上通过C:\ Windows \ System32 \搜索的示例Powershell片段:

Get-ChildItem -Path "C:\Windows\System32\" -Filter "*.exe" -Recurse -ErrorAction SilentlyContinue | % { $out=$(E:\vs2017\VC\Tools\MSVC\14.16.27023\bin\Hostx64\x64\dumpbin.exe /IMPORTS:rpcrt4.dll $_.VersionInfo.FileName); If($out -like "*RpcServerListen*"){ Write-Host "[+] Exe starting RPC Server: $($_.VersionInfo.FileName)"; Write-Output "[+] $($_.VersionInfo.FileName)`n`n $($out|%{"$_`n"})" | Out-File -FilePath EXEs_RpcServerListen.txt -Append } } 

此代码段将要控制台管理的可执行文件的名称以及整个DumpBin的输出打印到文件EXEs_RpcServerListen.txt中(以便您可以查看DumpBin实际为您提供的内容)。

在这里插入图片描述

查找有趣的RPC服务器的另一种方法是通过在本地或任何远程系统上查询RPC端点映射器。

Microsoft有一个称为PortQry的测试实用程序(可以使用该工具的GUI版本)来进行操作,您可以像这样使用它:

C:\PortQryV2\PortQry.exe -n 127.0.0.1 -e 135 

在这里插入图片描述
该工具为您提供了Endpoint Mapper知道的有关远程RPC接口的一些信息(请记住,知名端点不必将其接口通知给端点映射器)。

另一种选择是直接调用Endpoint Manager,方法是调用RpcMgmtEpEltInqBegin并通过RpcMgmtEpEltInqNext在接口上进行迭代。

我已经将这个很酷的工具移植到了VC ++上,并做了一些轻微的可用性更改。

我已经在https://github.com/csandker/RPCDump发布了我的叉子。

如图所示,该工具还列出了找到的RPC端点的接口,以及一些其他信息。 我不会详细介绍所有这些字段,但如果您有兴趣,请查看https://github.com/csandker/RPCDump并阅读Windows API文档。 例如,通过调用RpcMgmtInqStats
https://docs.microsoft.com/en-us/windows/win32/api/rpcdce/nf-rpcdce-rpcmgmtinqstats可以检索这些统计信息,其中在“备注”https://docs.microsoft.com/en-us/windows/win32/api/rpcdce/nf-rpcdce-rpcmgmtinqstats#remarks部分中引用了返回的值。

再次记住,只有RPC接口已注册到目标的端点映射器。

RPC 客户端

查找连接到远程或本地 RPC 服务器的客户端可能也是一个有趣的目标。

DumpBin:

Get-ChildItem -Path "C:\Windows\System32\" -Filter "*.exe" -Recurse -ErrorAction SilentlyContinue | % { $out=$(C:\"Program Files (x86)"\"Microsoft Visual Studio 14.0"\VC\bin\dumpbin.exe /IMPORTS:rpcrt4.dll $_.VersionInfo.FileName); If($out -like "*RpcStringBindingCompose*"){ Write-Host "[+] Exe starting RPC Server: $($_.VersionInfo.FileName)"; Write-Output "[+] $($_.VersionInfo.FileName)`n`n $($out|%{"$_`n"})" | Out-File -FilePath EXEs_RpcClients.txt -Append } } 

在这里插入图片描述

找到 RPC 客户端的另一种选择是,在客户与其目标连接时发现他们。如何发现客户端的一个例子是检查通过两个系统之间发送的流量。

Wireshark 有一个"DCERPC"过滤器,可用于发现连接。客户端连接到服务器的示例如下:
在这里插入图片描述

绑定请求是我们可以用来识别客户端的内容之一。 在包中,我们可以看到客户端尝试绑定到UUID为“ d6b1ad2b-b550-4729-b6c2-1651f58480c3”的服务器接口。

结尾

后文作者提到了RPC服务端和客户端模拟的攻击面以及未授权访问等,由于笔者只关心提权和原理部分所以在这停下,感兴趣的读者可以继续阅读。

评论

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