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