How the Windows Firewall RPC Filter Works
2021-08-22 14:32:00 Author: www.blogger.com(查看原文) 阅读量:76 收藏

tag:blogger.com,1999:blog-4304739697716191998.post-98609841674191672021-08-21T22:32:00.002-07:002021-08-21T22:44:46.198-07:00How the Windows Firewall RPC Filter Works<p>I did <a href="https://twitter.com/tiraniddo/status/1423082196668096514">promise</a> that I'd put out a blog post on how the Windows RPC filter works. Now that I released my more <a href="https://googleprojectzero.blogspot.com/2021/08/understanding-network-access-windows-app.html">general blog post</a> on the Windows firewall I thought I'd come back to a shorter post about the RPC filter itself. If you don't know the context, the Windows firewall has the ability to restrict access to RPC interfaces. This is interesting due to the renewed interest in all things RPC, especially the <a href="https://github.com/topotam/PetitPotam">PetitPotam</a> trick. For example you can block any access to the <i>EFSRPC</i> interfaces using the following script which you run with the <i>netsh</i> command.</p><div style="text-align: left;"><span style="font-family: courier;">rpc<br />filter<br />add rule layer=um actiontype=block<br />add condition field=if_uuid matchtype=equal data=c681d488-d850-11d0-8c52-00c04fd90f7e<br />add filter<br />add rule layer=um actiontype=block<br />add condition field=if_uuid matchtype=equal data=df1941c5-fe89-4e79-bf10-463657acf44d<br />add filter<br />quit</span></div><div style="text-align: left;"><span style="font-family: courier;"><br /></span></div><div style="text-align: left;">This script adds two rules which will block any calls on the RPC interfaces with UUIDs of&nbsp;<i>c681d488-d850-11d0-8c52-00c04fd90f7e</i> and&nbsp;<i>df1941c5-fe89-4e79-bf10-463657acf44d</i>. These correspond to the two EFSRPC interfaces.</div><div style="text-align: left;"><br /></div><div style="text-align: left;">How does this work within the context of the firewall? Does the kernel components of the Windows Filtering Platform have a builtin RPC protocol parser to block the connection? That'd be far too complex, instead everything is done in user-mode by some special layers. If you use <a href="https://www.powershellgallery.com/packages/NtObjectManager/1.1.32">NtObjectManager</a>'s firewall <i>Get-FwLayer</i> command you can check for layers registered to run in user-mode by filtering on the <i>IsUser</i> property.</div><div style="text-align: left;"><br /></div><div style="text-align: left;"><span style="font-family: courier;">PS&gt; Get-FwLayer | Where-Object IsUser</span></div><div><span style="font-family: courier;">KeyName&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; Name</span></div><div><span style="font-family: courier;">-------&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; ----</span></div><div><span style="font-family: courier;">FWPM_LAYER_RPC_PROXY_CONN&nbsp; &nbsp; RPC Proxy Connect Layer</span></div><div><span style="font-family: courier;">FWPM_LAYER_IPSEC_KM_DEMUX_V4 IPsec KM Demux v4 Layer</span></div><div><span style="font-family: courier;">FWPM_LAYER_RPC_EP_ADD&nbsp; &nbsp; &nbsp; &nbsp; RPC EP ADD Layer</span></div><div><span style="font-family: courier;">FWPM_LAYER_KM_AUTHORIZATION&nbsp; Keying Module Authorization Layer</span></div><div><span style="font-family: courier;">FWPM_LAYER_IKEEXT_V4&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;IKE v4 Layer</span></div><div><span style="font-family: courier;">FWPM_LAYER_IPSEC_V6&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; IPsec v6 Layer</span></div><div><span style="font-family: courier;">FWPM_LAYER_IPSEC_V4&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; IPsec v4 Layer</span></div><div><span style="font-family: courier;">FWPM_LAYER_IKEEXT_V6&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;IKE v6 Layer</span></div><div><span style="font-family: courier;">FWPM_LAYER_RPC_UM&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; RPC UM Layer</span></div><div><span style="font-family: courier;">FWPM_LAYER_RPC_PROXY_IF&nbsp; &nbsp; &nbsp; RPC Proxy Interface Layer</span></div><div><span style="font-family: courier;">FWPM_LAYER_RPC_EPMAP&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;RPC EPMAP Layer</span></div><div><span style="font-family: courier;">FWPM_LAYER_IPSEC_KM_DEMUX_V6 IPsec KM Demux v6 Layer</span></div><div><br /></div><div>In the output we can see 5 layers with RPC in the name of the layer.&nbsp;</div><div><ul style="text-align: left;"><li><i>FWPM_LAYER_RPC_EP_ADD</i> - Filter new endpoints created by a process.</li><li><i>FWPM_LAYER_RPC_EPMAP</i> - Filter access to endpoint mapper information.</li><li><i>FWPM_LAYER_RPC_PROXY_CONN</i> - Filter connections to the RPC proxy.</li><li><i>FWPM_LAYER_RPC_PROXY_IF</i> - Filter interface calls through an RPC proxy.</li><li><i>FWPM_LAYER_RPC_UM</i> - Filter interface calls to an RPC server</li></ul><div>Each of these layers is potentially interesting, and you can add rules through <i>netsh</i> for all of them. But we'll just focus on how the&nbsp;<i>FWPM_LAYER_RPC_UM&nbsp;</i>layer works as that's the one the script introduced at the start works with. If you run the following command after adding the RPC filter rules you can view the newly created rules:</div></div><div><br /></div><div><div><span style="font-family: courier;">PS&gt; Get-FwFilter -LayerKey FWPM_LAYER_RPC_UM -Sorted | Format-FwFilter</span></div><div><span style="font-family: courier;">Name&nbsp; &nbsp; &nbsp; &nbsp;: RPCFilter</span></div><div><span style="font-family: courier;">Action Type: Block</span></div><div><span style="font-family: courier;">Key&nbsp; &nbsp; &nbsp; &nbsp; : d4354417-02fa-11ec-95da-00155d010a06</span></div><div><span style="font-family: courier;">Id&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;: 78253</span></div><div><span style="font-family: courier;">Description: RPC Filter</span></div><div><span style="font-family: courier;">Layer&nbsp; &nbsp; &nbsp; : FWPM_LAYER_RPC_UM</span></div><div><span style="font-family: courier;">Sub Layer&nbsp; : FWPM_SUBLAYER_UNIVERSAL</span></div><div><span style="font-family: courier;">Flags&nbsp; &nbsp; &nbsp; : Persistent</span></div><div><span style="font-family: courier;">Weight&nbsp; &nbsp; &nbsp;: 567453553048682496</span></div><div><span style="font-family: courier;">Conditions :</span></div><div><span style="font-family: courier;">FieldKeyName&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;MatchType Value</span></div><div><span style="font-family: courier;">------------&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;--------- -----</span></div><div><span style="font-family: courier;">FWPM_CONDITION_RPC_IF_UUID Equal&nbsp; &nbsp; &nbsp;df1941c5-fe89-4e79-bf10-463657acf44d</span></div><div><span style="font-family: courier;"><br /></span></div><div><span style="font-family: courier;"><br /></span></div><div><span style="font-family: courier;">Name&nbsp; &nbsp; &nbsp; &nbsp;: RPCFilter</span></div><div><span style="font-family: courier;">Action Type: Block</span></div><div><span style="font-family: courier;">Key&nbsp; &nbsp; &nbsp; &nbsp; : d4354416-02fa-11ec-95da-00155d010a06</span></div><div><span style="font-family: courier;">Id&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;: 78252</span></div><div><span style="font-family: courier;">Description: RPC Filter</span></div><div><span style="font-family: courier;">Layer&nbsp; &nbsp; &nbsp; : FWPM_LAYER_RPC_UM</span></div><div><span style="font-family: courier;">Sub Layer&nbsp; : FWPM_SUBLAYER_UNIVERSAL</span></div><div><span style="font-family: courier;">Flags&nbsp; &nbsp; &nbsp; : Persistent</span></div><div><span style="font-family: courier;">Weight&nbsp; &nbsp; &nbsp;: 567453553048682496</span></div><div><span style="font-family: courier;">Conditions :</span></div><div><span style="font-family: courier;">FieldKeyName&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;MatchType Value</span></div><div><span style="font-family: courier;">------------&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;--------- -----</span></div><div><span style="font-family: courier;">FWPM_CONDITION_RPC_IF_UUID Equal&nbsp; &nbsp; &nbsp;c681d488-d850-11d0-8c52-00c04fd90f7e</span></div></div><div><br /></div><div>If you're read my general blog post the output should made some sense. The&nbsp;<i>FWPM_CONDITION_RPC_IF_UUID</i> condition key is used to specify the UUID for the interface to match on. The&nbsp;<i>FWPM_LAYER_RPC_UM</i> has many possible fields to filter on, which you can query by inspecting the layer object's <i>Fields</i> property.</div><div><br /></div><div><div><span style="font-family: courier;">PS&gt; (Get-FwLayer -Key FWPM_LAYER_RPC_UM).Fields</span></div><div><span style="font-family: courier;"><br /></span></div><div><span style="font-family: courier;">KeyName&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; Type&nbsp; &nbsp; &nbsp; DataType</span></div><div><span style="font-family: courier;">-------&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; ----&nbsp; &nbsp; &nbsp; --------</span></div><div><span style="font-family: courier;">FWPM_CONDITION_REMOTE_USER_TOKEN&nbsp; &nbsp; &nbsp;RawData&nbsp; &nbsp;TokenInformation</span></div><div><span style="font-family: courier;">FWPM_CONDITION_RPC_IF_UUID&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;RawData&nbsp; &nbsp;ByteArray16</span></div><div><span style="font-family: courier;">FWPM_CONDITION_RPC_IF_VERSION&nbsp; &nbsp; &nbsp; &nbsp; RawData&nbsp; &nbsp;UInt16</span></div><div><span style="font-family: courier;">FWPM_CONDITION_RPC_IF_FLAG&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;RawData&nbsp; &nbsp;UInt32</span></div><div><span style="font-family: courier;">FWPM_CONDITION_DCOM_APP_ID&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;RawData&nbsp; &nbsp;ByteArray16</span></div><div><span style="font-family: courier;">FWPM_CONDITION_IMAGE_NAME&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; RawData&nbsp; &nbsp;ByteBlob</span></div><div><span style="font-family: courier;">FWPM_CONDITION_RPC_PROTOCOL&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; RawData&nbsp; &nbsp;UInt8</span></div><div><span style="font-family: courier;">FWPM_CONDITION_RPC_AUTH_TYPE&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;RawData&nbsp; &nbsp;UInt8</span></div><div><span style="font-family: courier;">FWPM_CONDITION_RPC_AUTH_LEVEL&nbsp; &nbsp; &nbsp; &nbsp; RawData&nbsp; &nbsp;UInt8</span></div><div><span style="font-family: courier;">FWPM_CONDITION_SEC_ENCRYPT_ALGORITHM RawData&nbsp; &nbsp;UInt32</span></div><div><span style="font-family: courier;">FWPM_CONDITION_SEC_KEY_SIZE&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; RawData&nbsp; &nbsp;UInt32</span></div><div><span style="font-family: courier;">FWPM_CONDITION_IP_LOCAL_ADDRESS_V4&nbsp; &nbsp;IPAddress UInt32</span></div><div><span style="font-family: courier;">FWPM_CONDITION_IP_LOCAL_ADDRESS_V6&nbsp; &nbsp;IPAddress ByteArray16</span></div><div><span style="font-family: courier;">FWPM_CONDITION_IP_LOCAL_PORT&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;RawData&nbsp; &nbsp;UInt16</span></div><div><span style="font-family: courier;">FWPM_CONDITION_PIPE&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; RawData&nbsp; &nbsp;ByteBlob</span></div><div><span style="font-family: courier;">FWPM_CONDITION_IP_REMOTE_ADDRESS_V4&nbsp; IPAddress UInt32</span></div><div><span style="font-family: courier;">FWPM_CONDITION_IP_REMOTE_ADDRESS_V6&nbsp; IPAddress ByteArray16</span></div></div><div><br /></div><div>There's quite a few potential configuration options for the filter. You can filter based on the remote user token that's authenticated to the interface. Or you can filters based on the authentication level and type. This could allow you to protect an RPC interface so that all callers have to use Kerberos with at&nbsp;<i>RPC_C_AUTHN_LEVEL_PKT_PRIVACY</i> level.&nbsp;</div><div><br /></div><div>Anyway, configuring it is less important to us, you probably want to know how it works, as the first step to trying to find a way to bypass it is to know where this filter layer is processed (note, I've not found a bypass, but you never know).&nbsp;</div><div><br /></div><div>Perhaps unsurprisingly due to the complexity of the RPC protocol the filtering is implemented within the RPC server process through the <i>RpcRtRemote</i> extension DLL. Except for RPCSS this DLL isn't loaded by default. Instead it's only loaded if there exists a value for the&nbsp;<i>WNF_RPCF_FWMAN_RUNNING</i> WNF state. The following shows the state after adding the two RPC filter rules with <i>netsh</i>.</div><div><br /></div><div><div><span style="font-family: courier;">PS&gt; $wnf = Get-NtWnf -Name 'WNF_RPCF_FWMAN_RUNNING'</span></div><div><span style="font-family: courier;">PS&gt; $wnf.QueryStateData()</span></div><div><span style="font-family: courier;"><br /></span></div><div><span style="font-family: courier;">Data ChangeStamp</span></div><div><span style="font-family: courier;">---- -----------</span></div><div><span style="font-family: courier;">{}&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;2</span></div></div><div><br /></div><div>The RPC runtime sets up a subscription to load the DLL if the WNF value is ever changed. Once loaded the RPC runtime will register all current interfaces to check the firewall. The filter rules are checked when a call is made to the interface during the normal processing of the security callback. The runtime will invoke the&nbsp;<i>FwFilter </i>function inside <i>RpcRtRemote</i>, passing all the details about the firewall interface call. The filter call is only made for DCE/RPC protocols, so not ALPC. It also will only be called if the caller is remote. This is always the case if the call comes via TCP, but for named pipes it will only be called if the pipe was opened via SMB.</div><div><br /></div><div>Here's where we can finally determine how the RPC filter is processed. The <i>FwFilter</i> function builds a list of firewall values corresponding to the list of fields for the&nbsp;<i>FWPM_LAYER_RPC_UM</i> layer and passes them to the&nbsp;<a href="https://docs.microsoft.com/en-us/previous-versions/bb309061(v=vs.85)">FwpsClassifyUser0</a> API along with the numeric ID of the layer. This API will enumerate all filters for the layer and apply the condition checks returning the classification, e.g. block or permit. Based on this classification the RPC runtime can permit or refuse the call.&nbsp;</div><div><br /></div><div>In order for a filter to be accessible for classification the RPC server must have&nbsp;<i>FWPM_ACTRL_OPEN</i> access to the engine and&nbsp;<i>FWPM_ACTRL_CLASSIFY</i> access to the filter. By default the <i>Everyone</i> group has these access rights, however AppContainers and potentially other sandboxes do not. However, in general AppContainer processes don't tend to create privileged RPC servers, at least any which a remote attacker would find useful. You can check the access on various firewall objects using the&nbsp;<i>Get-AccessibleFwObject</i> command.</div><div><br /></div><div><span style="font-family: courier;">PS&gt;&nbsp;$token = Get-NtToken -Filtered -Flags LuaToken</span></div><div><div><span style="font-family: courier;">PS&gt; Get-AccessibleFwObject -Token $token | Where-Object Name -eq RPCFilter</span></div><div><span style="font-family: courier;"><br /></span></div><div><span style="font-family: courier;">TokenId Access&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;Name</span></div><div><span style="font-family: courier;">------- ------&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;----</span></div><div><span style="font-family: courier;">4ECF80&nbsp; Classify|Open RPCFilter</span></div><div><span style="font-family: courier;">4ECF80&nbsp; Classify|Open RPCFilter</span></div></div><div><br /></div><div>I hope this gives enough information for someone to dig into it further to see if there's any obvious bypass I missed. I'm sure there's probably some fun trick you could do to circumvent restrictions if you look hard enough :-)</div><div><br /></div>tiraniddo[email protected]

文章来源: https://www.blogger.com/feeds/4304739697716191998/posts/default/9860984167419167
如有侵权请联系:admin#unsafe.sh