Windows防火墙RPC过滤器工作原理深度解析

本文详细解析了Windows防火墙中RPC过滤器的工作机制,包括如何通过UUID阻止特定RPC接口访问、用户模式过滤层的运作原理,以及RPC运行时如何通过RpcRtRemote扩展DLL实现安全回调检查。

Windows防火墙RPC过滤器工作原理

我曾经承诺过会发布一篇关于Windows RPC过滤器如何工作的博客文章。既然我已经发布了关于Windows防火墙的更通用的博客文章,我想我应该回来写一篇关于RPC过滤器本身的较短文章。

如果你不了解背景,Windows防火墙能够限制对RPC接口的访问。由于人们对所有RPC相关事物的重新关注,尤其是PetitPotam技巧,这一点很有趣。例如,你可以使用以下通过netsh命令运行的脚本来阻止对EFSRPC接口的任何访问。

1
2
3
4
5
6
7
8
rpcfilter
add rule layer=um actiontype=block
add condition field=if_uuid matchtype=equal data=c681d488-d850-11d0-8c52-00c04fd90f7e
add filter
add rule layer=um actiontype=block
add condition field=if_uuid matchtype=equal data=df1941c5-fe89-4e79-bf10-463657acf44d
add filter
quit

这个脚本添加了两条规则,将阻止对UUID为c681d488-d850-11d0-8c52-00c04fd90f7edf1941c5-fe89-4e79-bf10-463657acf44d的RPC接口的任何调用。这些对应于两个EFSRPC接口。

这在防火墙的上下文中是如何工作的?Windows过滤平台的内核组件是否有一个内置的RPC协议解析器来阻止连接?那将过于复杂,相反,所有事情都是在用户模式下由一些特殊的层完成的。如果你使用NtObjectManager的防火墙Get-FwLayer命令,你可以通过过滤IsUser属性来检查注册为在用户模式下运行的层。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
PS> Get-FwLayer | Where-Object IsUser

KeyName                      Name
-------                      ----
FWPM_LAYER_RPC_PROXY_CONN    RPC Proxy Connect Layer
FWPM_LAYER_IPSEC_KM_DEMUX_V4 IPsec KM Demux v4 Layer
FWPM_LAYER_RPC_EP_ADD        RPC EP ADD Layer
FWPM_LAYER_KM_AUTHORIZATION  Keying Module Authorization Layer
FWPM_LAYER_IKEEXT_V4         IKE v4 Layer
FWPM_LAYER_IPSEC_V6          IPsec v6 Layer
FWPM_LAYER_IPSEC_V4          IPsec v4 Layer
FWPM_LAYER_IKEEXT_V6         IKE v6 Layer
FWPM_LAYER_RPC_UM            RPC UM Layer
FWPM_LAYER_RPC_PROXY_IF      RPC Proxy Interface Layer
FWPM_LAYER_RPC_EPMAP         RPC EPMAP Layer
FWPM_LAYER_IPSEC_KM_DEMUX_V6 IPsec KM Demux v6 Layer

在输出中,我们可以看到5个层名中包含RPC的层。

  • FWPM_LAYER_RPC_EP_ADD - 过滤进程创建的新端点。
  • FWPM_LAYER_RPC_EPMAP - 过滤对端点映射器信息的访问。
  • FWPM_LAYER_RPC_PROXY_CONN - 过滤到RPC代理的连接。
  • FWPM_LAYER_RPC_PROXY_IF - 过滤通过RPC代理的接口调用。
  • FWPM_LAYER_RPC_UM - 过滤到RPC服务器的接口调用。

这些层都可能很有趣,你可以通过netsh为它们所有添加规则。但我们将只关注FWPM_LAYER_RPC_UM层的工作原理,因为这是开头脚本所使用的层。如果在添加RPC过滤器规则后运行以下命令,你可以查看新创建的规则:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
PS> Get-FwFilter -LayerKey FWPM_LAYER_RPC_UM -Sorted | Format-FwFilter

Name        : RPCFilter
Action Type : Block
Key         : d4354417-02fa-11ec-95da-00155d010a06
Id          : 78253
Description : RPC Filter
Layer       : FWPM_LAYER_RPC_UM
Sub Layer   : FWPM_SUBLAYER_UNIVERSAL
Flags       : Persistent
Weight      : 567453553048682496
Conditions  :
    FieldKeyName               MatchType Value
    ------------               --------- -----
    FWPM_CONDITION_RPC_IF_UUID Equal     df1941c5-fe89-4e79-bf10-463657acf44d

Name        : RPCFilter
Action Type : Block
Key         : d4354416-02fa-11ec-95da-00155d010a06
Id          : 78252
Description : RPC Filter
Layer       : FWPM_LAYER_RPC_UM
Sub Layer   : FWPM_SUBLAYER_UNIVERSAL
Flags       : Persistent
Weight      : 567453553048682496
Conditions  :
    FieldKeyName               MatchType Value
    ------------               --------- -----
    FWPM_CONDITION_RPC_IF_UUID Equal     c681d488-d850-11d0-8c52-00c04fd90f7e

如果你读过我通用的博客文章,输出应该有些意义。FWPM_CONDITION_RPC_IF_UUID条件键用于指定要匹配的接口UUID。FWPM_LAYER_RPC_UM有许多可能的字段可以过滤,你可以通过检查层对象的Fields属性来查询。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
PS> (Get-FwLayer -Key FWPM_LAYER_RPC_UM).Fields

KeyName                              Type      DataType
-------                              ----      --------
FWPM_CONDITION_REMOTE_USER_TOKEN     RawData   TokenInformation
FWPM_CONDITION_RPC_IF_UUID           RawData   ByteArray16
FWPM_CONDITION_RPC_IF_VERSION        RawData   UInt16
FWPM_CONDITION_RPC_IF_FLAG           RawData   UInt32
FWPM_CONDITION_DCOM_APP_ID           RawData   ByteArray16
FWPM_CONDITION_IMAGE_NAME            RawData   ByteBlob
FWPM_CONDITION_RPC_PROTOCOL          RawData   UInt8
FWPM_CONDITION_RPC_AUTH_TYPE         RawData   UInt8
FWPM_CONDITION_RPC_AUTH_LEVEL        RawData   UInt8
FWPM_CONDITION_SEC_ENCRYPT_ALGORITHM RawData   UInt32
FWPM_CONDITION_SEC_KEY_SIZE          RawData   UInt32
FWPM_CONDITION_IP_LOCAL_ADDRESS_V4   IPAddress UInt32
FWPM_CONDITION_IP_LOCAL_ADDRESS_V6   IPAddress ByteArray16
FWPM_CONDITION_IP_LOCAL_PORT         RawData   UInt16
FWPM_CONDITION_PIPE                  RawData   ByteBlob
FWPM_CONDITION_IP_REMOTE_ADDRESS_V4  IPAddress UInt32
FWPM_CONDITION_IP_REMOTE_ADDRESS_V6  IPAddress ByteArray16

过滤器有相当多的潜在配置选项。你可以基于已验证到接口的远程用户令牌进行过滤。或者你可以基于身份验证级别和类型进行过滤。这可以让你保护一个RPC接口,以便所有调用者必须使用Kerberos并至少达到RPC_C_AUTHN_LEVEL_PKT_PRIVACY级别。

无论如何,配置对我们来说不太重要,你可能想知道它是如何工作的,因为尝试找到绕过方法的第一步是了解这个过滤层在哪里处理(注意,我还没有找到绕过方法,但你永远不知道)。

也许并不奇怪,由于RPC协议的复杂性,过滤是在RPC服务器进程内部通过RpcRtRemote扩展DLL实现的。除了RPCSS,这个DLL默认不加载。相反,只有在存在WNF_RPCF_FWMAN_RUNNING WNF状态的值时才会加载。以下显示了使用netsh添加两个RPC过滤器规则后的状态。

1
2
3
4
5
6
PS> $wnf = Get-NtWnf -Name 'WNF_RPCF_FWMAN_RUNNING'
PS> $wnf.QueryStateData()

Data ChangeStamp
---- -----------
{}             2

RPC运行时设置了一个订阅,以便在WNF值发生更改时加载DLL。一旦加载,RPC运行时将注册所有当前接口以检查防火墙。在安全回调的正常处理过程中,当对接口进行调用时,会检查过滤器规则。运行时将调用RpcRtRemote内部的FwFilter函数,传递有关防火墙接口调用的所有细节。过滤调用仅针对DCE/RPC协议进行,所以不包括ALPC。并且只有在调用者是远程的情况下才会被调用。如果调用通过TCP进来,情况总是如此,但对于命名管道,只有当管道是通过SMB打开时才会被调用。

在这里,我们最终可以确定RPC过滤器是如何处理的。FwFilter函数构建一个对应于FWPM_LAYER_RPC_UM层字段列表的防火墙值列表,并将它们与层的数字ID一起传递给FwpsClassifyUser0 API。这个API将枚举该层的所有过滤器并应用条件检查,返回分类,例如阻止或允许。基于这个分类,RPC运行时可以允许或拒绝调用。

为了使过滤器可访问以进行分类,RPC服务器必须对引擎具有FWPM_ACTRL_OPEN访问权限,并对过滤器具有FWPM_ACTRL_CLASSIFY访问权限。默认情况下,Everyone组拥有这些访问权限,但是AppContainers和潜在的其它沙箱没有。然而,一般来说,AppContainer进程不会创建特权RPC服务器,至少是任何远程攻击者会觉得有用的。你可以使用Get-AccessibleFwObject命令检查各种防火墙对象的访问权限。

1
2
3
4
5
6
7
PS> $token = Get-NtToken -Filtered -Flags LuaToken
PS> Get-AccessibleFwObject -Token $token | Where-Object Name -eq RPCFilter

TokenId Access            Name
------- ------            ----
4ECF80  Classify|Open RPCFilter
4ECF80  Classify|Open RPCFilter

我希望这能提供足够的信息,让有人可以进一步深入研究,看看是否有任何我遗漏的明显绕过方法。我相信如果你仔细寻找,可能会发现一些有趣的技巧来规避限制 :-)

comments powered by Disqus
使用 Hugo 构建
主题 StackJimmy 设计