RPC安全体系化建设-学习探索Window RPC安全机制

admin 2024年1月7日22:06:44评论14 views字数 6123阅读20分24秒阅读模式

互联网公司里大型项目基本都是各种RPC实现,刚毕业的时候看到RPC,仅属于看得懂文字描述,最近针对RPC做过一些鉴权管控及审计工作有了更完善的了解,暂时不唠这个,唠点普众知名windows 系统的rpc安全机制,来了解下更大更完善的rpc机制及其做的安全性,可将其当作优秀实践来指导对rpc安全体系的建设

RPC基础介绍

远程过程调用 ( RPC ) 是进程间通信 (IPC) 的一种形式。它允许客户端调用由 RPC 服务器公开的过程。客户端调用该函数,就好像它是正常的过程调用一样,无需(几乎)对远程交互的详细信息进行编码。服务器可以托管在同一台计算机或远程计算机上的不同进程中。

在本文中将研究 Microsoft 的 RPC (MS-RPC) 实现。MS-RPC 派生自分布式计算环境核心的 RPC 协议的参考实现 。

Windows 将 RPC 大量用于许多不同的服务,例如任务计划、服务创建、打印机和共享设置,以及远程存储的加密数据的管理。由于 RPC 作为远程向量的性质,从安全角度来看,它引起了很多关注。这篇博文将尝试介绍 MS-RPC 工作原理的基础知识,并重点介绍其合并的安全机制。

Windows RPC 工作原理

这些过程及其参数是用一种称为接口定义语言 (IDL) 的描述性语言定义的。为每个接口分配一个唯一标识符,称为 UUID,服务器和客户端都使用该标识符来寻址服务器公开的确切接口。

然后,使用 Microsoft IDL 编译器将 IDL 文件编译为包含运行时功能的头文件和源代码文件。从技术上讲,这些是服务器和客户端使用的存根,它们将控制权传递给在 rpcrt4.dll 中实现的 RPC 运行时。客户端的 RPC 运行时封送数据,并将其传递给另一端的 RPC 运行时。

              

RPC安全体系化建设-学习探索Window RPC安全机制

          

您可能想知道服务器和客户端如何从网络(或本地)的角度进行通信。服务器通过注册协议序列和终结点的组合来侦听传入的 RPC 连接。例如,协议序列可以是 ncacn_ip_tcp (TCP)、 ncacn_np 命名管道)或 ncalrpc (LPC)。终结点可以是端口,例如 TCP 5555 或 \pipe\example(如果使用命名管道)。命名管道使用隐藏的 IPC$ 共享通过 TCP 端口 445 的 SMB 传输进行传输。协议序列的完整列表可在 Microsoft 网站上 找到 

因此,服务器侦听某些端点上的连接。客户端如何知道在哪里连接?答案取决于端点的类型——动态端点或已知端点(又称静态端点)。

动态端点是通过服务器端的端点映射器注册的端点。终结点映射器(又名 epmapper)是一种 RPC 服务,用于将服务映射到实际终结点。epmapper 使用 TCP 端口 135 和 593 进行 RPC over HTTP。因此,客户端可以使用 epmapper 枚举(使用 指定的 API )远程计算机上所有动态注册的 RPC 服务器。

已知端点是未通过 epmapper 注册的端点。客户端应知道服务器预先注册的终结点。如果端点在客户端和服务器的代码中都进行了硬编码,或者端点存在于 IDL 文件中,则可以执行此操作。下面是打印后台处理程序服务 IDL 中的示例。    

Python                  
[                  
uuid(12345678-1234-ABCD-EF00-0123456789AB),                  
version(1.0),                  
ms_union,                  
endpoint("ncacn_np:[\pipe\spoolss]"),                  
pointer_default(unique)                  
]

RPC 表示客户端和服务器之间通过绑定句柄的逻辑连接。服务器和客户端都使用函数来操作绑定数据,例如,在设置身份验证信息时。当客户端在绑定上设置身份验证时,绑定将被视为经过身份验证。当客户端程序调用 RPC 函数时,将发生网络绑定。从网络流量的角度来看,RPC 客户端通过发送数据包中包含身份验证信息的绑定请求来开始 RPC 交互。服务器可以使用bind_ack(已确认)或bind_nak(发生错误)进行响应。

RPC安全体系化建设-学习探索Window RPC安全机制

在Wireshark 中,我们可以看到 Task Scheduler 接口的动态端点解析。客户端获得任务计划程序的终结点信息后,将创建一个新连接。然后,我们可以看到与另一个绑定进程的新连接,包括作为绑定身份验证一部分的 AUTH3 数据包。

与endpoint一样,绑定也有类型:自动绑定、隐式绑定和显式绑定。它们在应用程序对绑定过程的控制量方面有所不同。

1自动绑定( 已过时 )— 客户端和服务器应用程序不处理绑定过程,而是让 RPC 运行时完全控制此过程。

1隐式绑定 — 客户端可以选择在绑定发生之前配置绑定句柄。客户端建立绑定后,RPC 运行时库将处理其余部分。

1显式绑定 — 客户端需要配置绑定句柄。然后,RPC 运行时只需将其传递给服务器。

              

RPC安全体系化建设-学习探索Window RPC安全机制

          

          

远程请求的 RPC 安全性

现在我们知道了 RPC 工作原理的基础知识,我们想要了解哪些操作、策略和机制可以阻止客户端访问函数。从进攻和防守的角度来看,这都很有趣。

传输身份验证

某些传输(如命名管道或 HTTP)将身份验证作为其协议的一部分。例如,命名管道通过具有身份验证的 SMB 进行传输。这基本上意味着,当服务器注册命名管道端点时,只有具有有效用户凭据的客户端才能连接到该端点。在域环境中,在同一域中拥有域用户就足以通过身份验证检查。如果计算机不是域的一部分,则客户端需要在远程服务器上拥有本地用户的凭据。

RPC安全体系化建设-学习探索Window RPC安全机制

          

绑定身份验证    

通过使用绑定身份验证,服务器可以具有身份验证机制。为此,服务器需要通过调用 RpcServerRegisterAuthInfo 来注册身份验证信息。客户端必须使用服务器使用的正确服务主体名称和安全支持提供程序方法,否则它将从服务器接收“RPC_S_UNKNOWN_AUTHN_SERVICE”。

客户端可以通过使用 RpcBindingSetAuthInfo 和 RpcBindingSetAuthInfoEx 等 API 在绑定上设置身份验证和授权数据来向服务器进行身份验证。客户端指定身份验证方法(例如 NTLM、Kerberos、Negotiate、SCHANNEL 等)和服务主体名称。

需要了解的两件重要事情:

1如果客户端在绑定上设置了身份验证,并且服务器尚未注册任何身份验证信息,则服务器将返回“RPC_S_UNKNOWN_AUTHN_SERVICE”。

1仅仅因为服务器注册了身份验证绑定,并不意味着客户端必须使用经过身份验证的绑定。此外,RPC 运行时不会使用无效凭据调度客户端身份验证绑定。在本文的后面,我们将讨论服务器如何防止访问未经身份验证的绑定。

值得注意的是,RPC 运行时全局保存身份验证信息。这意味着,如果两个 RPC 服务器共享相同的进程,并且其中一个服务器注册了身份验证信息,则另一个服务器也将具有身份验证信息。客户端现在可以在访问每个服务器时对绑定进行身份验证,将此行为称为 SSPI 多路复用 。

端点安全

服务器可以在端点上设置安全描述符。安全描述符是 Windows 中的常规访问检查安全机制。它允许创建“规则”,规定谁可以访问对象,谁被拒绝访问。当尝试访问对象时,操作系统会将调用方的访问令牌与安全描述符进行比较,以查看是否允许访问。在这种情况下,对象是终结点,访问令牌是从传输协议身份验证派生的客户端访问令牌。此检查仅适用于经过身份验证的传输,如命名管道、ALPC 和 HTTP(使用身份验证时)。TCP 是未经身份验证的传输协议,因此不会进行此访问检查。

与 SSPI 多路复用类似,端点也是多路复用的,接口和端点不绑定。服务器分别注册接口和端点。如果进程具有多个已注册的端点,则可以通过这些端点中的每一个访问此进程中注册的每个接口。

例如,假设您注册了接口并创建了一个侦听 \pipemypipe 的终结点。在同一进程中承载的另一个 RPC 服务器在 TCP 7777 上注册了自己的终结点。您的接口也可以通过TCP访问。这将绕过对端点施加的安全限制,例如安全描述符。因此, 微软官方建议 不要依赖端点安全性,也不要验证客户端是否通过预期的传输协议。    

RPC安全体系化建设-学习探索Window RPC安全机制

          

          

接口安全

查看接口安全性时,有三种方法可以保护接口:设置安全回调、在接口上设置安全描述符以及使用接口标志。

设置安全回调

第一种接口安全机制是安全回调。安全回调是由服务器开发人员实现的自定义回调。安全回调中的逻辑由开发人员决定,它允许服务器限制对接口的访问。如果回调返回 RPC_S_OK ,则允许客户端。

如果注册了安全回调,则 RPC 运行时将自动拒绝未经身份验证的客户端,除非设置了标志 RPC_IF_ALLOW_CALLBACKS_WITH_NO_AUTH

设置安全回调并设置 RPC_IF_ALLOW_SECURE_ONLY 标志并不意味着客户端具有高权限。因此,安全回调是服务器查询客户端权限级别的地方。这是通过调用 RpcBindingInqAuthClient 完成的。另一个常见的检查是客户端用于通过经过身份验证的安全传输(如命名管道)强制连接的传输方法。这是通过调用 RpcBindingServerFromClient → RpcBindingToStringBinding → RpcStringBindingParse 并比较 Protseq (协议序列) 参数来完成的。这还可以防止端点多路复用滥用。

安全回调缓存    

如果注册了安全回调,则会在安全检查期间调用该回调。如果安全回调通过(即返回 RPC_S_OK),则如果使用缓存,则结果将被缓存。下次同一客户端调用接口上的函数时,由于缓存了安全回调结果,因此不会再次调用安全回调,而是使用缓存的条目。

缓存的实现很简单,但取决于几个因素。

1缓存绑定到客户端的安全上下文,该上下文取自绑定。因此,如果服务器(或进程中的任何其他服务器)未注册任何身份验证绑定,或者客户端未设置身份验证绑定,则将禁用此调用的缓存。

1如果服务器使用标志RPC_IF_SEC_NO_CACHE注册接口,则 RPC 运行时会强制调用每个调用的安全回调,从而禁用缓存机制。

1未记录的接口标志RPC_IF_SEC_CACHE_PER_PROC也会影响缓存机制。如果服务器指定了此标志,则缓存将基于 调用 ,而不是基于 接口 。这意味着,如果缓存在接口 TEST 上保留了函数 X 的成功值,则对接口 TEST 上函数 Y 的调用将再次触发安全回调。

缓存机制可能会导致依赖于客户端调用的函数的任何安全回调的逻辑漏洞。作为 RPC 开发人员或审核员,您应该了解缓存机制。我们已经发表了关于缓存实现的深入研究,包括我们发现的漏洞

设置安全描述符

保护接口的第二种机制是在接口上设置安全描述符。只有函数 RpcServerRegisterIf3 具有将安全描述符注册到接口的选项。如果未设置安全描述符,则 默认安全描述符为

1NT 权限匿名登录

1每个人 都

1NT 权限受限

1内置管理员

1自我

使用接口标志

保护接口的第三种方法是使用接口标志,这些标志是在通过 RpcServerRegisterIf* 函数创建接口时设置的。从安全角度来看,有趣的标志是:    

RPC_IF_ALLOW_CALLBACKS_WITH_NO_AUTH — 每次调用都会调用安全回调,无论使用何种传输方法或身份验证级别。

RPC_IF_ALLOW_LOCAL_ONLY — 仅允许本地请求。

RPC_IF_ALLOW_SECURE_ONLY — 连接仅限于授权级别高于RPC_C_AUTHN_LEVEL_NONE的客户端。指定此标志将拒绝通过 NULL 会话的客户端。此标志不保证调用用户的权限级别,它仅保证客户端具有有效的凭据。

RPC_IF_SEC_NO_CACHE — 此标志将完全禁用接口安全回调的缓存。

RPC_IF_SEC_CACHE_PER_PROC — 此标志不会禁用整个缓存机制,而是将默认行为更改为基于调用而不是基于接口。

          

          

程序安全检查

          

服务器程序员可以将安全检查作为接口上公开的函数的一部分来实现,因此,可以选择更改或自定义每个调用的函数的检查逻辑。常见的安全检查是访问检查,如图所示(取自 srvsvc):

RPC安全体系化建设-学习探索Window RPC安全机制

在此示例中,LocalrSessionGetInfo 调用 SsAccessCheck SsAccessCheck 基本上通过调用 RpcImpersonateClient 来模拟客户端,然后调用执行访问检查的函数 NtAccessCheckAndAuditAlarm 。然后进行 RpcRevertToSelf 调用以还原到服务器的令牌。请务必记住检查 RpcImpersonateClient 的返回值,因为如果失败,服务器将继续在服务器进程的安全令牌中运行,而不是在客户端的进程中运行。    

          

系统策略

根据计算机类型设置了多个系统范围的策略:客户端、服务器或域控制器 (DC)。

与终结点安全性相关的一个有趣的系统策略是“限制未经身份验证的 RPC 客户端策略”。可以设置三个值。

1“Authenticated” — 如果 接口上未设置RPC_IF_ALLOW_CALLBACKS_WITH_NO_AUTH标志 ,RPC 运行时将阻止对未提前进行身份验证的 TCP 客户端的访问。

1“已通过身份验证,无异常” — 阻止所有未经身份验证的连接。

1“None” — 允许所有RPC客户端连接到计算机上运行的RPC服务器。

因此,未经身份验证的客户端的一个有趣的攻击面是通过 TCP 访问并注册 RPC_IF_ALLOW_CALLBACKS_WITH_NO_AUTH 标志的接口。显然,客户端需要通过安全回调检查才能调用接口的函数。

如前所述,通过命名管道的连接(通过 SMB 传输)具有自己的身份验证,作为 SMB 协议的一部分。客户端在通过 SMB 连接时可以使用“匿名登录”(也称为 NULL 会话)。两个相关的系统策略是“限制对命名管道和共享的匿名访问”和“网络访问:可以匿名访问的命名管道”。如果启用了第一个,则只能匿名连接在第二个管道中定义的命名管道。对于工作站,第二个策略为空,这基本上意味着不能对域中的工作站使用 NULL 会话。对于 DC 计算机,策略中的命名管道列表包括“pipenetlogon”、“pipesamr”和“pipelsarpc”。从攻击者的角度来看,这很有趣,因为这些是域外部的计算机可以连接到的端点。

最后,关于终结点安全描述符检查,有一个默认禁用的系统范围策略:“网络访问:让所有人权限应用于匿名用户。启用后,everyone 安全标识符将添加到为匿名连接创建的令牌中。    

总结

一个好的RPC在安全方面的建设,很明显需要具备最基础的身份验证、权限控制,确保由指定的上游来调用,而非被滥用;其次,本身RPC实现的相关功能具备基础安全,确保自身不存在明确安全风险;然后,在其他服务作为当前RPC调用上游时,对该服务做好安全检测,明确调用方不存在安全风险;最后之间的可能会产生的逻辑风险需通过建立完善的日志审计及人工服务来持续化挖掘

原文始发于微信公众号(暴暴的皮卡丘):RPC安全体系化建设-学习探索Window RPC安全机制

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2024年1月7日22:06:44
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   RPC安全体系化建设-学习探索Window RPC安全机制http://cn-sec.com/archives/2372972.html

发表评论

匿名网友 填写信息