by Mitja Kolsek, the 0patch Team
"DFSCoerce" is another forced authentication issue in Windows that can be used by a low-privileged domain user to take over a Windows server, potentially becoming a domain admin within minutes. The issue was discovered by security researcher Filip Dragovic, who also published a POC.
Filip's tweet indicated this issue can be used even if you have disabled or filtered services that other currently known forced authentication issues such as PrinterBug/SpoolSample, PetitPotam, ShadowCoerce and RemotePotato0 are exploiting: "
does not fix forced authentication issues unless an attack can be mounted anonymously. Our customers unfortunately can't all disable relevant services or implement mitigations without breaking production, so it is on us to provide them with such patches.
The Vulnerability
The vulnerability lies in the Distributed File System (DFS) service. Any authenticated user can make a remote procedure call to this service and execute functions NetrDfsAddStdRoot or NetrDfsremoveStdRoot, providing them with host name or IP address of attacker's computer. These functions both properly perform a permissions check using a call to AccessImpersonateCheckRpcClient, which returns error code 5 ("access denied") for users who aren't allowed to do any changes to DFS. If access is denied, they block the adding or removing of a stand-alone namespace - but they both still perform a credentials-leaking request to the specified host name or IP address.
Such leaked credentials - belonging to server's computer account - can be relayed to some other service in the network such as LDAP or Certificate Service to perform privileged operations leading to further unauthorized access. Unsurprisingly, attackers and red teams like such things.
Our Micropatch
Since a proper access check was already in place, just not reacted to entirely properly, we decided to use its result and correct the logic in both vulnerable functions.
The image below shows our patch (green code blocks) injected in function NetrDfsremoveStdRoot. As you can see, a call to AccessImpersonateCheckRpcClient is made in the original code, which returns 5 ("access denied") when the caller has insufficient permissions. This information is then stored as one bit into register r8b, and copied to local variable arg_18 (sounds like an argument, but compilers use so-called "home space" for local variables when it suits them). Our patch code takes the return value of AccessImpersonateCheckRpcClient and compares it to 5; if equal, we sabotage attacker's attempts by placing a zero at the beginning of their ServerName string pointed to by rcx, effectively turning it into an empty string. This approach allows us to minimize the amount of code and complexity of the patch, which is always our goal. Function DfsDeleteStandaloneRoot, which causes the forced authentication to attacker's host, is then called from the original code (moved to a blue trampoline code block) but it gets an empty string for the host name - and returns an error. A blocked attack therefore behaves as if a request was made by an unprivileged user with an illegal ServerName. We decided not to log this as an attempted exploit to avoid possible false positives in case a regular user without malicious intent might somehow trigger this code via Windows user interface.
Source code of the micropatch shows two identical patchlets, one for function NetrDfsAddStdRoot and one for NetrDfsremoveStdRoot:
MODULE_PATH "..\Affected_Modules\dfssvc.exe_10.0.17763.2028_Srv2019_64-bit_u202206\dfssvc.exe"
PATCH_ID 952
PATCH_FORMAT_VER 2
VULN_ID 7442
PLATFORM win64
patchlet_start
PATCHLET_ID 1 ; NetrDfsAddStdRoot
PATCHLET_TYPE 2
PATCHLET_OFFSET 0x183e
N_ORIGINALBYTES 5
JUMPOVERBYTES 0
code_start
neg eax ; get original return value from AccessImpersonateCheckRpcClient
cmp eax, 5 ; check if access denied(5) was returned
jne CONTINUE_1 ; return value is not 5, continue with
; normal code execution
mov word[rcx], 0 ; else set ServerHost to NULL. Result: DFSNM
; SessionError: code: 0x57 - ERROR_INVALID_PARAMETER
; continue with original code
CONTINUE_1:
code_end
patchlet_end
patchlet_start
PATCHLET_ID 2 ; NetrDfsremoveStdRoot
PATCHLET_TYPE 2
PATCHLET_OFFSET 0x1c96
N_ORIGINALBYTES 5
JUMPOVERBYTES 0
code_start
neg eax ; get original return value from AccessImpersonateCheckRpcClient
cmp eax, 5 ; check if access denied(5) was returned
jne CONTINUE_2 ; return value is not 5, continue with
; normal code execution
mov word[rcx], 0 ; else set ServerHost to NULL. Result: DFSNM
; SessionError: code: 0x57 -
ERROR_INVALID_PARAMETER
; continue with original code
CONTINUE_2:
code_end
patchlet_end
Micropatch Availability
While
this vulnerability has no official patch and could be considered a
"0day", Microsoft seems determined not to fix relaying issues such as
this one; therefore, this micropatch is not provided in the FREE plan
but requires a PRO or Enterprise license.
The micropatch was written for the following Versions of Windows with all available Windows Updates installed:
- Windows Server 2008 R2
- Windows Server 2012
- Windows Server 2012 R2
- Windows Server 2016
- Windows Server 2019
- Windows Server 2022
This micropatch has already been distributed to, and applied on, all online 0patch Agents in PRO or Enterprise accounts (unless Enterprise group settings prevent that).
If you're new to 0patch, create a free account in 0patch Central, then install and register 0patch Agent from 0patch.com, and email sales@0patch.com for a trial. Everything else will happen automatically. No computer reboot will be needed.
We'd like to thank Filip Dragovic for sharing details about this vulnerability, which allowed us to create a micropatch and protect our users. We also encourage security researchers to privately share their analyses with us for micropatching.
No comments:
Post a Comment