npx skills add mukul975/Anthropic-Cybersecurity-SkillsMITRE ATT&CK
NIST CSF 2.0
MITRE D3FEND
Overview
Active Directory (AD) compromise investigation is a critical incident response capability that focuses on identifying how attackers gained access to domain services, what persistence mechanisms they established, and the scope of credential compromise. Since 88% of breaches involve compromised credentials (Verizon 2025 DBIR), AD is the primary target for enterprise-wide attacks. Investigators must analyze NTDS.dit database integrity, Kerberos ticket-granting activity, Group Policy modifications, replication metadata, and privileged group membership changes to reconstruct the attack chain and determine full compromise scope.
When to Use
- When conducting security assessments that involve performing active directory compromise investigation
- When following incident response procedures for related security events
- When performing scheduled security testing or auditing activities
- When validating security controls through hands-on testing
Prerequisites
- Familiarity with incident response concepts and tools
- Access to a test or lab environment for safe execution
- Python 3.8+ with required dependencies installed
- Appropriate authorization for any testing activities
Key Investigation Areas
1. NTDS.dit Database Analysis
The NTDS.dit file is the core Active Directory credential database containing all password hashes for domain accounts. Attackers commonly exfiltrate this file using tools like ntdsutil, secretsdump.py, or DCSync attacks via Mimikatz.
Detection indicators:
- Event ID 4662: Access to directory service objects with replication permissions
- Event ID 4742: Computer account modifications on domain controllers
- Volume Shadow Copy creation on domain controllers (Event ID 8222)
- Unusual ntdsutil.exe or vssadmin.exe execution
- Replication traffic from non-DC sources (DCSync detection)
2. Kerberos Attack Detection
Golden Ticket indicators:
- TGT tickets with abnormally long lifetimes (default is 10 hours)
- Event ID 4769 with encryption type 0x17 (RC4) instead of AES
- TGT issued without corresponding Event ID 4768 (AS-REQ)
- Kerberos tickets referencing non-existent or disabled accounts
Silver Ticket indicators:
- Service tickets without corresponding TGT requests
- Event ID 4769 with unusual service names
- Tickets with forged PAC data
Kerberoasting indicators:
- High volume of Event ID 4769 for service accounts
- RC4 encryption requests for accounts that support AES
- Requests from workstations not normally accessing those services
3. Group Policy Abuse
- GPO modifications granting new privileges (Event ID 5136)
- Scheduled task deployment via GPO
- Software installation policies added to domain
- Login script modifications
- Registry-based policy changes for persistence
4. Privileged Group Enumeration
Track modifications to these critical groups:
- Domain Admins, Enterprise Admins, Schema Admins
- Account Operators, Backup Operators
- DnsAdmins (can execute arbitrary DLLs on DCs)
- Group Policy Creator Owners
- Protected Users group membership changes
5. Trust Relationship Analysis
- New forest/domain trusts created (Event ID 4706)
- SID History injection for privilege escalation
- Trust ticket forgery indicators
- Cross-domain authentication anomalies
Investigation Methodology
Phase 1: Scoping and Evidence Collection
1. Identify potentially compromised domain controllers
2. Collect Security, System, Directory Service event logs
3. Extract AD replication metadata using repadmin
4. Capture ntdsutil snapshots for offline analysis
5. Collect DNS server logs and zone transfer records
6. Export Group Policy Object configurations
7. Document current privileged group membershipsPhase 2: Authentication Log Analysis
1. Parse Event ID 4624/4625 for logon patterns
2. Identify pass-the-hash indicators (Event ID 4624 Type 3 with NTLM)
3. Analyze Event ID 4768/4769/4771 for Kerberos anomalies
4. Review Event ID 4776 for NTLM authentication failures
5. Cross-reference logon events with known compromised accounts
6. Map lateral movement paths through authentication chainsPhase 3: Persistence and Backdoor Detection
1. Enumerate AdminSDHolder ACL modifications
2. Check for SID History abuse on accounts
3. Verify krbtgt account password age
4. Audit DSRM password configuration
5. Check for skeleton key malware indicators
6. Review AD Certificate Services for rogue certificates
7. Validate DNS records for poisoningPhase 4: Remediation Planning
1. Double-rotate krbtgt password (wait replication between rotations)
2. Reset all compromised account passwords
3. Remove unauthorized privileged group members
4. Revoke rogue certificates if AD CS compromised
5. Rebuild domain controllers from clean media if needed
6. Implement tiered administration model
7. Enable Protected Users group for privileged accountsCritical Event IDs for AD Investigation
| Event ID | Source | Description |
|---|---|---|
| 4624 | Security | Successful logon |
| 4625 | Security | Failed logon |
| 4648 | Security | Explicit credential logon |
| 4662 | Security | Operation on AD object |
| 4768 | Security | Kerberos TGT requested |
| 4769 | Security | Kerberos service ticket requested |
| 4771 | Security | Kerberos pre-authentication failed |
| 4776 | Security | NTLM credential validation |
| 5136 | Security | Directory object modified |
| 5137 | Security | Directory object created |
| 4706 | Security | Trust created |
| 4707 | Security | Trust removed |
| 4742 | Security | Computer account changed |
| 8222 | System | Shadow copy created |
Tools for AD Investigation
| Tool | Purpose |
|---|---|
| BloodHound | Attack path mapping and privilege escalation analysis |
| Pingcastle | AD security assessment and risk scoring |
| Purple Knight | AD vulnerability scanning by Semperis |
| ADRecon | Active Directory data gathering |
| Mimikatz | Credential extraction and Kerberos analysis |
| Impacket | DCSync detection and NTLM relay analysis |
| Velociraptor | Remote forensic artifact collection |
| Timeline Explorer | Event log timeline analysis |
MITRE ATT&CK Mapping
| Technique | ID | Relevance |
|---|---|---|
| DCSync | T1003.006 | NTDS.dit credential extraction |
| Golden Ticket | T1558.001 | Kerberos TGT forgery |
| Silver Ticket | T1558.002 | Service ticket forgery |
| Kerberoasting | T1558.003 | Service account hash extraction |
| Pass-the-Hash | T1550.002 | NTLM hash reuse |
| Group Policy Modification | T1484.001 | Persistence via GPO |
| Account Manipulation | T1098 | Privileged group changes |
| SID-History Injection | T1134.005 | Privilege escalation |
References
References and resources
Everything below is rendered for inspection. Script files are read-only and never run.
References 3
api-reference.md2.6 KB
Active Directory Compromise Investigation - API Reference
Windows Security Event IDs
| Event ID | Description | Compromise Indicator |
|---|---|---|
| 4662 | Directory service object accessed | DCSync (replication GUIDs) |
| 4769 | Kerberos service ticket requested | Kerberoasting (RC4 encryption) |
| 4768 | Kerberos TGT requested | Golden Ticket (anomalous source) |
| 4672 | Special privileges assigned | Privileged logon tracking |
| 4624 | Successful logon | Lateral movement (Type 3) |
| 4648 | Explicit credential logon | Pass-the-hash, PsExec |
| 4720 | User account created | Persistence |
| 4728 | Member added to global group | Privilege escalation |
| 4732 | Member added to local group | Privilege escalation |
DCSync Detection
Replication GUIDs (Event 4662 ObjectType)
| GUID | Right |
|---|---|
1131f6aa-9c07-11d1-f79f-00c04fc2dcd2 |
DS-Replication-Get-Changes |
1131f6ad-9c07-11d1-f79f-00c04fc2dcd2 |
DS-Replication-Get-Changes-All |
89e95b76-444d-4c62-991a-0facbeda640c |
DS-Replication-Get-Changes-In-Filtered-Set |
When a non-DC account triggers 4662 with these GUIDs, it indicates DCSync attack (Mimikatz lsadump::dcsync).
Kerberoasting Detection
Event 4769 with TicketEncryptionType = 0x17 (RC4-HMAC) for service accounts. Normal behavior uses AES (0x11 or 0x12). RC4 requests by user accounts against service SPNs indicate offline cracking attempts.
Golden Ticket Detection
Event 4768 TGT requests from IPs that are not domain controllers. Golden tickets forged offline will show TGT requests from workstations rather than DCs.
Lateral Movement Detection
- Type 3 logon (Event 4624, LogonType=3): Network logon via SMB, WMI, PsExec
- Event 4648: Explicit credential use (runas, remote tools)
- Pattern: Multiple Type 3 logons from same source to different targets
Event Log JSON Format
The agent accepts JSON-exported event logs:
[
{
"EventID": 4769,
"TimeCreated": "2024-01-15T10:30:00Z",
"EventData": {
"TargetUserName": "svc_sql",
"ServiceName": "MSSQLSvc/db01:1433",
"TicketEncryptionType": "0x17"
}
}
]Export from PowerShell:
Get-WinEvent -LogName Security | ConvertTo-Json -Depth 5 > events.jsonOutput Schema
{
"report": "ad_compromise_investigation",
"total_events_analyzed": 50000,
"total_findings": 15,
"severity_summary": {"critical": 3, "high": 7, "medium": 5},
"findings": [{"type": "dcsync_detected", "severity": "critical"}]
}CLI Usage
python agent.py --log events.json --output report.jsonstandards.md2.6 KB
Standards and Frameworks for AD Compromise Investigation
NIST SP 800-61 Rev 2 - Computer Security Incident Handling Guide
- Provides incident response lifecycle: Preparation, Detection & Analysis, Containment, Eradication & Recovery, Post-Incident Activity
- AD compromise investigations follow all four phases with emphasis on scoping the identity compromise
CISA Alert: Detecting and Mitigating Active Directory Compromises
- Published guidance on AD-specific threats and detection methods
- Covers DCSync, Golden Ticket, Silver Ticket, and Kerberoasting attacks
- Recommends tiered administration, Protected Users group, and credential hygiene
- Reference: https://www.cisa.gov/resources-tools/resources/detecting-and-mitigating-active-directory-compromises
MITRE ATT&CK Framework - Credential Access Tactics
- T1003.006: OS Credential Dumping - DCSync
- T1558.001: Steal or Forge Kerberos Tickets - Golden Ticket
- T1558.002: Steal or Forge Kerberos Tickets - Silver Ticket
- T1558.003: Steal or Forge Kerberos Tickets - Kerberoasting
- T1550.002: Use Alternate Authentication Material - Pass the Hash
- T1484.001: Domain Policy Modification - Group Policy Modification
- T1098: Account Manipulation
Microsoft Security Best Practices
- Tiered administration model for Active Directory
- Privileged Access Workstations (PAWs) for domain admin tasks
- Microsoft Advanced Threat Analytics (ATA) / Defender for Identity
- LAPS (Local Administrator Password Solution) deployment
- Protected Users security group enforcement
- Reference: https://learn.microsoft.com/en-us/security/privileged-access-workstations/overview
CIS Benchmarks for Active Directory
- CIS Microsoft Windows Server Benchmark for DC hardening
- Password policy requirements (minimum 14 characters)
- Account lockout policy configuration
- Audit policy settings for security event logging
- GPO security baseline configurations
SANS FOR500 - Windows Forensic Analysis
- Windows artifact analysis methodology
- Registry analysis for persistence detection
- Event log forensics for authentication tracking
- Timeline analysis techniques for AD compromise
Semperis Identity Forensics and Incident Response (IFIR)
- AD-specific forensic methodology
- Replication metadata analysis
- Change tracking and rollback capabilities
- Forest recovery procedures
- Reference: https://www.semperis.com/solutions/active-directory-breach-forensics/
Key Windows Security Event IDs for AD Monitoring
- Microsoft documentation on security auditing events
- Advanced Audit Policy Configuration for DCs
- Kerberos event logging requirements
- Directory Service Access auditing
workflows.md4.0 KB
AD Compromise Investigation Workflows
Workflow 1: Initial Triage and Scoping
START: AD Compromise Alert Received
|
v
[Determine Scope of Potential Compromise]
|-- Single account compromised?
|-- Multiple accounts compromised?
|-- Domain controller compromised?
|-- Full domain compromise suspected?
|
v
[Collect Initial Evidence]
|-- Export Security event logs from all DCs
|-- Snapshot AD replication metadata
|-- Export current GPO configurations
|-- Document privileged group memberships
|
v
[Identify Compromised Accounts]
|-- Analyze logon events (4624/4625)
|-- Check for pass-the-hash patterns
|-- Review Kerberos ticket activity
|-- Cross-reference with threat intelligence
|
v
[Determine Attack Timeline]
|-- First known malicious activity
|-- Privilege escalation events
|-- Lateral movement chain
|-- Data access and exfiltration
|
v
[Report Findings and Escalate]Workflow 2: Kerberos Attack Investigation
START: Suspicious Kerberos Activity Detected
|
v
[Analyze Event ID 4768 - TGT Requests]
|-- Normal TGT requests from user workstations?
|-- TGT requests from unusual sources?
|-- Encryption type analysis (AES vs RC4)
|
v
[Analyze Event ID 4769 - Service Ticket Requests]
|-- High volume from single source? --> Kerberoasting
|-- Service tickets without prior TGT? --> Silver Ticket
|-- Unusual service names or SPNs?
|
v
[Check for Golden Ticket Indicators]
|-- TGT lifetime exceeds 10 hours?
|-- TGT for non-existent accounts?
|-- TGT without matching AS-REQ?
|-- krbtgt account age analysis
|
v
[Determine Remediation Actions]
|-- Kerberoasting: Reset affected service account passwords
|-- Golden Ticket: Double-rotate krbtgt password
|-- Silver Ticket: Reset affected service account passwords
|-- All: Review and harden SPN configurations
|
v
END: Remediation Complete + Monitoring EnhancedWorkflow 3: DCSync Attack Investigation
START: Replication from Non-DC Source Detected
|
v
[Verify Replication Source]
|-- Is source a legitimate domain controller?
|-- Check Event ID 4662 for replication rights usage
|-- Verify account has DS-Replication-Get-Changes-All
|
v
[If Unauthorized Replication Detected]
|-- Identify compromised account with replication rights
|-- Determine which credentials were replicated
|-- Check for Mimikatz/Impacket artifacts on source
|
v
[Assess Credential Exposure]
|-- All domain password hashes potentially compromised
|-- krbtgt hash exposure enables Golden Ticket
|-- Service account hashes enable Silver Tickets
|-- Trust keys enable cross-domain attacks
|
v
[Execute Full Credential Reset]
|-- Double-rotate krbtgt (wait for replication)
|-- Reset all privileged account passwords
|-- Reset all service account passwords
|-- Force password change for all users
|
v
END: Monitor for Continued Unauthorized ReplicationWorkflow 4: Post-Compromise Remediation
START: Compromise Scope Determined
|
v
[Immediate Containment]
|-- Disable compromised accounts
|-- Block attacker IP addresses
|-- Isolate compromised systems
|-- Revoke active sessions and tokens
|
v
[Credential Reset Sequence]
|-- Step 1: Reset krbtgt (first rotation)
|-- Step 2: Wait for full AD replication
|-- Step 3: Reset krbtgt (second rotation)
|-- Step 4: Wait for full AD replication
|-- Step 5: Reset all privileged accounts
|-- Step 6: Reset all service accounts
|-- Step 7: Force user password changes
|
v
[Persistence Removal]
|-- Remove unauthorized GPO modifications
|-- Clean AdminSDHolder ACL entries
|-- Remove SID History entries
|-- Revoke rogue AD CS certificates
|-- Remove skeleton key if detected
|-- Clean scheduled tasks and services
|
v
[Hardening Implementation]
|-- Deploy tiered administration model
|-- Enable Protected Users group
|-- Implement PAW for admin tasks
|-- Deploy LAPS for local admin
|-- Configure advanced audit policies
|
v
END: Continuous Monitoring EstablishedScripts 2
agent.py8.9 KB
#!/usr/bin/env python3
"""Active Directory Compromise Investigation agent - analyzes Windows Security
event logs for indicators of AD compromise including DCSync, Golden Ticket,
Kerberoasting, and lateral movement."""
import argparse
import json
from collections import Counter, defaultdict
from datetime import datetime
from pathlib import Path
COMPROMISE_INDICATORS = {
4662: {"name": "DCSync Detection", "description": "Directory service object accessed with replication rights"},
4769: {"name": "Kerberoasting", "description": "Kerberos service ticket requested with RC4 encryption"},
4768: {"name": "Golden Ticket", "description": "Kerberos TGT requested"},
4672: {"name": "Privileged Logon", "description": "Special privileges assigned to new logon"},
4624: {"name": "Logon Event", "description": "Successful logon - check Type 3 for lateral movement"},
4648: {"name": "Explicit Credential Logon", "description": "Logon using explicit credentials"},
4720: {"name": "Account Created", "description": "New user account created"},
4728: {"name": "Group Membership Change", "description": "Member added to global group"},
}
def parse_evtx_json(log_path: str) -> list[dict]:
"""Parse Windows event logs exported as JSON."""
content = Path(log_path).read_text(encoding="utf-8")
try:
events = json.loads(content)
except json.JSONDecodeError:
events = [json.loads(line) for line in content.strip().splitlines() if line.strip()]
return events
def detect_dcsync(events: list[dict]) -> list[dict]:
"""Detect DCSync attacks via Event ID 4662 with replication GUIDs."""
replication_guids = {
"1131f6aa-9c07-11d1-f79f-00c04fc2dcd2",
"1131f6ad-9c07-11d1-f79f-00c04fc2dcd2",
"89e95b76-444d-4c62-991a-0facbeda640c",
}
findings = []
for evt in events:
eid = evt.get("EventID") or evt.get("event_id")
if eid != 4662:
continue
data = evt.get("EventData", evt.get("event_data", {}))
obj_type = str(data.get("ObjectType", "")).lower().strip("{}")
if obj_type in replication_guids:
subject = data.get("SubjectUserName", "unknown")
findings.append({
"type": "dcsync_detected",
"severity": "critical",
"event_id": 4662,
"subject": subject,
"object_type": obj_type,
"timestamp": evt.get("TimeCreated", evt.get("timestamp", "")),
"detail": f"DCSync replication by {subject}",
})
return findings
def detect_kerberoasting(events: list[dict]) -> list[dict]:
"""Detect Kerberoasting via Event ID 4769 with RC4 encryption."""
findings = []
for evt in events:
eid = evt.get("EventID") or evt.get("event_id")
if eid != 4769:
continue
data = evt.get("EventData", evt.get("event_data", {}))
ticket_encryption = str(data.get("TicketEncryptionType", ""))
if ticket_encryption == "0x17":
service = data.get("ServiceName", "unknown")
client = data.get("TargetUserName", "unknown")
findings.append({
"type": "kerberoasting_detected",
"severity": "high",
"event_id": 4769,
"service": service,
"client": client,
"encryption": "RC4 (0x17)",
"timestamp": evt.get("TimeCreated", evt.get("timestamp", "")),
"detail": f"RC4 TGS request for {service} by {client}",
})
return findings
def detect_golden_ticket(events: list[dict]) -> list[dict]:
"""Detect Golden Ticket indicators via Event ID 4768 anomalies."""
findings = []
for evt in events:
eid = evt.get("EventID") or evt.get("event_id")
if eid != 4768:
continue
data = evt.get("EventData", evt.get("event_data", {}))
client = data.get("TargetUserName", "")
ip_addr = data.get("IpAddress", "")
if ip_addr and not ip_addr.startswith("::") and ip_addr != "127.0.0.1":
findings.append({
"type": "golden_ticket_indicator",
"severity": "critical",
"event_id": 4768,
"client": client,
"source_ip": ip_addr,
"timestamp": evt.get("TimeCreated", evt.get("timestamp", "")),
"detail": f"TGT request for {client} from {ip_addr}",
})
return findings
def detect_lateral_movement(events: list[dict]) -> list[dict]:
"""Detect lateral movement via Type 3 logons and explicit credential use."""
findings = []
lateral_events = defaultdict(list)
for evt in events:
eid = evt.get("EventID") or evt.get("event_id")
data = evt.get("EventData", evt.get("event_data", {}))
if eid == 4624:
logon_type = str(data.get("LogonType", ""))
if logon_type == "3":
src_ip = data.get("IpAddress", "")
user = data.get("TargetUserName", "")
lateral_events[(user, src_ip)].append(
evt.get("TimeCreated", evt.get("timestamp", "")))
elif eid == 4648:
user = data.get("SubjectUserName", "")
target = data.get("TargetServerName", "")
findings.append({
"type": "explicit_credential_use",
"severity": "medium",
"event_id": 4648,
"user": user,
"target_server": target,
"timestamp": evt.get("TimeCreated", evt.get("timestamp", "")),
"detail": f"Explicit credential logon by {user} to {target}",
})
for (user, src_ip), timestamps in lateral_events.items():
if len(timestamps) > 5:
findings.append({
"type": "lateral_movement_pattern",
"severity": "high",
"user": user,
"source_ip": src_ip,
"logon_count": len(timestamps),
"detail": f"{len(timestamps)} Type 3 logons by {user} from {src_ip}",
})
return findings
def detect_persistence(events: list[dict]) -> list[dict]:
"""Detect persistence mechanisms via account creation and group changes."""
findings = []
for evt in events:
eid = evt.get("EventID") or evt.get("event_id")
data = evt.get("EventData", evt.get("event_data", {}))
if eid == 4720:
creator = data.get("SubjectUserName", "unknown")
new_account = data.get("TargetUserName", "unknown")
findings.append({
"type": "account_created",
"severity": "medium",
"event_id": 4720,
"creator": creator,
"new_account": new_account,
"timestamp": evt.get("TimeCreated", evt.get("timestamp", "")),
"detail": f"Account {new_account} created by {creator}",
})
elif eid in (4728, 4732):
subject = data.get("SubjectUserName", "unknown")
member = data.get("MemberName", "unknown")
group = data.get("TargetUserName", "unknown")
findings.append({
"type": "group_membership_change",
"severity": "high",
"event_id": eid,
"subject": subject,
"member": member,
"group": group,
"timestamp": evt.get("TimeCreated", evt.get("timestamp", "")),
"detail": f"{member} added to {group} by {subject}",
})
return findings
def generate_report(log_path: str) -> dict:
"""Run all detection checks and produce consolidated report."""
events = parse_evtx_json(log_path)
findings = []
findings.extend(detect_dcsync(events))
findings.extend(detect_kerberoasting(events))
findings.extend(detect_golden_ticket(events))
findings.extend(detect_lateral_movement(events))
findings.extend(detect_persistence(events))
severity_counts = Counter(f["severity"] for f in findings)
return {
"report": "ad_compromise_investigation",
"generated_at": datetime.utcnow().isoformat() + "Z",
"log_file": log_path,
"total_events_analyzed": len(events),
"total_findings": len(findings),
"severity_summary": dict(severity_counts),
"findings": findings,
}
def main():
parser = argparse.ArgumentParser(description="AD Compromise Investigation Agent")
parser.add_argument("--log", required=True, help="Path to JSON-exported Windows Security event log")
parser.add_argument("--output", help="Output JSON file path")
args = parser.parse_args()
report = generate_report(args.log)
output = json.dumps(report, indent=2)
if args.output:
Path(args.output).write_text(output, encoding="utf-8")
print(f"Report written to {args.output}")
else:
print(output)
if __name__ == "__main__":
main()
process.py22.3 KB
"""
Active Directory Compromise Investigation Script
Analyzes Windows Security event logs for indicators of AD compromise
including DCSync, Golden Ticket, Kerberoasting, and lateral movement.
"""
import json
import csv
import os
import re
import xml.etree.ElementTree as ET
from datetime import datetime, timedelta
from collections import defaultdict, Counter
from pathlib import Path
class ADCompromiseInvestigator:
"""Investigates Active Directory compromise through event log analysis."""
# Critical Event IDs for AD investigation
EVENT_IDS = {
4624: "Successful Logon",
4625: "Failed Logon",
4648: "Explicit Credential Logon",
4662: "AD Object Operation",
4768: "Kerberos TGT Request (AS-REQ)",
4769: "Kerberos Service Ticket Request (TGS-REQ)",
4771: "Kerberos Pre-Auth Failed",
4776: "NTLM Credential Validation",
5136: "Directory Object Modified",
5137: "Directory Object Created",
4706: "Trust Created",
4707: "Trust Removed",
4728: "Member Added to Security-Enabled Global Group",
4732: "Member Added to Security-Enabled Local Group",
4756: "Member Added to Security-Enabled Universal Group",
4742: "Computer Account Changed",
8222: "Shadow Copy Created",
}
PRIVILEGED_GROUPS = [
"Domain Admins",
"Enterprise Admins",
"Schema Admins",
"Account Operators",
"Backup Operators",
"DnsAdmins",
"Group Policy Creator Owners",
"Server Operators",
"Print Operators",
"Protected Users",
]
DCSYNC_GUIDS = [
"1131f6aa-9c07-11d1-f79f-00c04fc2dcd2", # DS-Replication-Get-Changes
"1131f6ad-9c07-11d1-f79f-00c04fc2dcd2", # DS-Replication-Get-Changes-All
"89e95b76-444d-4c62-991a-0facbeda640c", # DS-Replication-Get-Changes-In-Filtered-Set
]
def __init__(self, output_dir="ad_investigation_results"):
self.output_dir = Path(output_dir)
self.output_dir.mkdir(parents=True, exist_ok=True)
self.findings = []
self.compromised_accounts = set()
self.lateral_movement_chain = []
self.kerberos_anomalies = []
self.privilege_escalations = []
def parse_evtx_xml(self, xml_file):
"""Parse exported Windows event log XML file."""
events = []
try:
tree = ET.parse(xml_file)
root = tree.getroot()
ns = {"ns": "http://schemas.microsoft.com/win/2004/08/events/event"}
for event_elem in root.findall(".//ns:Event", ns):
event = self._extract_event_data(event_elem, ns)
if event:
events.append(event)
except ET.ParseError as e:
print(f"[ERROR] XML parse error in {xml_file}: {e}")
return events
def _extract_event_data(self, event_elem, ns):
"""Extract structured data from an event XML element."""
system = event_elem.find("ns:System", ns)
if system is None:
return None
event_id_elem = system.find("ns:EventID", ns)
time_elem = system.find("ns:TimeCreated", ns)
computer_elem = system.find("ns:Computer", ns)
event = {
"event_id": int(event_id_elem.text) if event_id_elem is not None else 0,
"timestamp": time_elem.get("SystemTime", "") if time_elem is not None else "",
"computer": computer_elem.text if computer_elem is not None else "",
"data": {},
}
event_data = event_elem.find("ns:EventData", ns)
if event_data is not None:
for data in event_data.findall("ns:Data", ns):
name = data.get("Name", "")
value = data.text or ""
event[" data"][name] = value
return event
def analyze_dcsync_activity(self, events):
"""Detect DCSync attacks by analyzing replication permission usage."""
print("[*] Analyzing for DCSync activity...")
dcsync_events = []
for event in events:
if event["event_id"] != 4662:
continue
properties = event["data"].get("Properties", "")
for guid in self.DCSYNC_GUIDS:
if guid.lower() in properties.lower():
subject = event["data"].get("SubjectUserName", "Unknown")
dcsync_events.append({
"timestamp": event["timestamp"],
"account": subject,
"computer": event["computer"],
"guid": guid,
"severity": "CRITICAL",
"description": f"DCSync replication rights used by {subject}",
})
self.compromised_accounts.add(subject)
if dcsync_events:
finding = {
"category": "DCSync Attack",
"severity": "CRITICAL",
"count": len(dcsync_events),
"details": dcsync_events,
"recommendation": "Immediately revoke replication rights from non-DC accounts. "
"Double-rotate krbtgt password. Reset all domain passwords.",
}
self.findings.append(finding)
print(f" [!] CRITICAL: {len(dcsync_events)} DCSync events detected!")
else:
print(" [+] No DCSync activity detected.")
return dcsync_events
def analyze_golden_ticket_indicators(self, events):
"""Detect Golden Ticket usage through Kerberos anomalies."""
print("[*] Analyzing for Golden Ticket indicators...")
golden_ticket_indicators = []
tgt_requests = {}
service_tickets = []
for event in events:
if event["event_id"] == 4768:
account = event["data"].get("TargetUserName", "")
tgt_requests[account] = event
elif event["event_id"] == 4769:
service_tickets.append(event)
for ticket in service_tickets:
account = ticket["data"].get("TargetUserName", "")
encryption = ticket["data"].get("TicketEncryptionType", "")
# RC4 encryption (0x17) when AES should be used indicates forged ticket
if encryption == "0x17":
golden_ticket_indicators.append({
"timestamp": ticket["timestamp"],
"account": account,
"indicator": "RC4 encryption on service ticket (should be AES)",
"encryption_type": encryption,
"severity": "HIGH",
})
# Service ticket without corresponding TGT request
if account and account not in tgt_requests:
golden_ticket_indicators.append({
"timestamp": ticket["timestamp"],
"account": account,
"indicator": "Service ticket without matching TGT request",
"severity": "CRITICAL",
})
if golden_ticket_indicators:
finding = {
"category": "Golden Ticket Indicators",
"severity": "CRITICAL",
"count": len(golden_ticket_indicators),
"details": golden_ticket_indicators,
"recommendation": "Double-rotate krbtgt password immediately. "
"Wait for full replication between rotations.",
}
self.findings.append(finding)
print(f" [!] CRITICAL: {len(golden_ticket_indicators)} Golden Ticket indicators!")
else:
print(" [+] No Golden Ticket indicators detected.")
return golden_ticket_indicators
def analyze_kerberoasting(self, events):
"""Detect Kerberoasting by analyzing bulk service ticket requests."""
print("[*] Analyzing for Kerberoasting activity...")
tgs_requests_by_source = defaultdict(list)
for event in events:
if event["event_id"] != 4769:
continue
source_ip = event["data"].get("IpAddress", "")
service_name = event["data"].get("ServiceName", "")
encryption = event["data"].get("TicketEncryptionType", "")
account = event["data"].get("TargetUserName", "")
if service_name and not service_name.endswith("$"):
tgs_requests_by_source[source_ip].append({
"timestamp": event["timestamp"],
"service": service_name,
"account": account,
"encryption": encryption,
})
kerberoasting_sources = []
for source_ip, requests in tgs_requests_by_source.items():
# Threshold: more than 5 unique service tickets in short timeframe
unique_services = set(r["service"] for r in requests)
if len(unique_services) >= 5:
kerberoasting_sources.append({
"source_ip": source_ip,
"unique_services_requested": len(unique_services),
"total_requests": len(requests),
"services": list(unique_services)[:20],
"severity": "HIGH",
})
if kerberoasting_sources:
finding = {
"category": "Kerberoasting Activity",
"severity": "HIGH",
"count": len(kerberoasting_sources),
"details": kerberoasting_sources,
"recommendation": "Reset passwords of targeted service accounts. "
"Use Group Managed Service Accounts (gMSA) where possible. "
"Enforce long, complex passwords on service accounts.",
}
self.findings.append(finding)
print(f" [!] HIGH: {len(kerberoasting_sources)} sources performing Kerberoasting!")
else:
print(" [+] No Kerberoasting activity detected.")
return kerberoasting_sources
def analyze_pass_the_hash(self, events):
"""Detect Pass-the-Hash through NTLM logon analysis."""
print("[*] Analyzing for Pass-the-Hash indicators...")
pth_indicators = []
for event in events:
if event["event_id"] != 4624:
continue
logon_type = event["data"].get("LogonType", "")
auth_package = event["data"].get("AuthenticationPackageName", "")
logon_process = event["data"].get("LogonProcessName", "")
# Type 3 (Network) + NTLM + NtLmSsp = potential PtH
if logon_type == "3" and "NTLM" in auth_package and "NtLmSsp" in logon_process:
account = event["data"].get("TargetUserName", "")
source_ip = event["data"].get("IpAddress", "")
workstation = event["data"].get("WorkstationName", "")
pth_indicators.append({
"timestamp": event["timestamp"],
"account": account,
"source_ip": source_ip,
"workstation": workstation,
"severity": "HIGH",
"description": f"NTLM network logon from {source_ip} as {account}",
})
if pth_indicators:
finding = {
"category": "Pass-the-Hash Indicators",
"severity": "HIGH",
"count": len(pth_indicators),
"details": pth_indicators[:50],
"recommendation": "Enable Credential Guard. Enforce NTLMv2 only. "
"Add privileged accounts to Protected Users group. "
"Implement tiered administration.",
}
self.findings.append(finding)
print(f" [!] HIGH: {len(pth_indicators)} Pass-the-Hash indicators detected!")
else:
print(" [+] No Pass-the-Hash indicators detected.")
return pth_indicators
def analyze_privilege_escalation(self, events):
"""Detect unauthorized additions to privileged groups."""
print("[*] Analyzing for privilege escalation via group membership...")
escalations = []
group_change_events = [4728, 4732, 4756]
for event in events:
if event["event_id"] not in group_change_events:
continue
group_name = event["data"].get("TargetUserName", "")
member_added = event["data"].get("MemberName", "")
changed_by = event["data"].get("SubjectUserName", "")
for priv_group in self.PRIVILEGED_GROUPS:
if priv_group.lower() in group_name.lower():
escalations.append({
"timestamp": event["timestamp"],
"group": group_name,
"member_added": member_added,
"changed_by": changed_by,
"computer": event["computer"],
"severity": "CRITICAL",
})
self.privilege_escalations = escalations
if escalations:
finding = {
"category": "Privileged Group Modification",
"severity": "CRITICAL",
"count": len(escalations),
"details": escalations,
"recommendation": "Review all privileged group membership changes. "
"Remove unauthorized members. Audit AdminSDHolder.",
}
self.findings.append(finding)
print(f" [!] CRITICAL: {len(escalations)} privileged group modifications!")
else:
print(" [+] No suspicious privileged group modifications detected.")
return escalations
def analyze_lateral_movement(self, events):
"""Map lateral movement chains through authentication patterns."""
print("[*] Analyzing lateral movement patterns...")
auth_chain = defaultdict(lambda: defaultdict(set))
for event in events:
if event["event_id"] != 4624:
continue
logon_type = event["data"].get("LogonType", "")
if logon_type not in ("3", "10"):
continue
account = event["data"].get("TargetUserName", "")
source_ip = event["data"].get("IpAddress", "")
target = event["computer"]
if source_ip and account and not account.endswith("$"):
auth_chain[account][source_ip].add(target)
movement_paths = []
for account, sources in auth_chain.items():
for source_ip, targets in sources.items():
if len(targets) >= 3:
movement_paths.append({
"account": account,
"source_ip": source_ip,
"targets_accessed": list(targets),
"target_count": len(targets),
"severity": "HIGH" if len(targets) >= 5 else "MEDIUM",
})
if movement_paths:
finding = {
"category": "Lateral Movement Patterns",
"severity": "HIGH",
"count": len(movement_paths),
"details": sorted(movement_paths, key=lambda x: x["target_count"], reverse=True)[:20],
"recommendation": "Investigate accounts with wide lateral movement. "
"Implement network segmentation. Enable credential guard.",
}
self.findings.append(finding)
print(f" [!] HIGH: {len(movement_paths)} lateral movement patterns detected!")
else:
print(" [+] No significant lateral movement patterns detected.")
return movement_paths
def analyze_gpo_modifications(self, events):
"""Detect suspicious Group Policy Object modifications."""
print("[*] Analyzing GPO modifications...")
gpo_changes = []
for event in events:
if event["event_id"] != 5136:
continue
object_class = event["data"].get("ObjectClass", "")
if "grouppolicycontainer" in object_class.lower():
changed_by = event["data"].get("SubjectUserName", "")
attribute = event["data"].get("AttributeLDAPDisplayName", "")
value = event["data"].get("AttributeValue", "")
gpo_changes.append({
"timestamp": event["timestamp"],
"changed_by": changed_by,
"attribute": attribute,
"value": value[:200],
"computer": event["computer"],
"severity": "HIGH",
})
if gpo_changes:
finding = {
"category": "GPO Modifications",
"severity": "HIGH",
"count": len(gpo_changes),
"details": gpo_changes,
"recommendation": "Review all GPO changes for unauthorized modifications. "
"Check for malicious scheduled tasks, login scripts, "
"or software installation policies.",
}
self.findings.append(finding)
print(f" [!] HIGH: {len(gpo_changes)} GPO modifications detected!")
else:
print(" [+] No GPO modifications detected.")
return gpo_changes
def generate_report(self):
"""Generate comprehensive investigation report."""
report = {
"investigation_report": "Active Directory Compromise Investigation",
"generated_at": datetime.utcnow().isoformat(),
"summary": {
"total_findings": len(self.findings),
"critical_findings": sum(1 for f in self.findings if f["severity"] == "CRITICAL"),
"high_findings": sum(1 for f in self.findings if f["severity"] == "HIGH"),
"compromised_accounts": list(self.compromised_accounts),
},
"findings": self.findings,
"recommendations": self._generate_recommendations(),
}
report_path = self.output_dir / "ad_investigation_report.json"
with open(report_path, "w") as f:
json.dump(report, f, indent=2, default=str)
print(f"\n[+] Report saved to {report_path}")
self._print_summary(report)
return report
def _generate_recommendations(self):
"""Generate prioritized remediation recommendations."""
recommendations = []
has_critical = any(f["severity"] == "CRITICAL" for f in self.findings)
if has_critical:
recommendations.append({
"priority": 1,
"action": "Double-rotate krbtgt password",
"detail": "Reset krbtgt twice with full AD replication between rotations. "
"This invalidates all Kerberos tickets including Golden Tickets.",
})
recommendations.append({
"priority": 2,
"action": "Reset all privileged account passwords",
"detail": "Reset passwords for Domain Admins, Enterprise Admins, "
"and all service accounts with SPNs.",
})
if self.compromised_accounts:
recommendations.append({
"priority": 3,
"action": "Disable and investigate compromised accounts",
"detail": f"Accounts to investigate: {', '.join(self.compromised_accounts)}",
})
recommendations.extend([
{
"priority": 4,
"action": "Implement tiered administration model",
"detail": "Separate Tier 0 (AD), Tier 1 (servers), Tier 2 (workstations) "
"with no credential reuse across tiers.",
},
{
"priority": 5,
"action": "Deploy advanced monitoring",
"detail": "Enable Microsoft Defender for Identity or equivalent. "
"Configure advanced audit policies on all DCs.",
},
])
return recommendations
def _print_summary(self, report):
"""Print investigation summary to console."""
summary = report["summary"]
print("\n" + "=" * 60)
print("AD COMPROMISE INVESTIGATION SUMMARY")
print("=" * 60)
print(f"Total Findings: {summary['total_findings']}")
print(f"Critical: {summary['critical_findings']}")
print(f"High: {summary['high_findings']}")
print(f"Compromised Accounts: {len(summary['compromised_accounts'])}")
if summary["compromised_accounts"]:
for account in summary["compromised_accounts"]:
print(f" - {account}")
print("=" * 60)
def run_full_investigation(self, event_log_files):
"""Run complete AD compromise investigation."""
print("[*] Starting Active Directory Compromise Investigation")
print(f"[*] Processing {len(event_log_files)} event log files...")
all_events = []
for log_file in event_log_files:
events = self.parse_evtx_xml(log_file)
all_events.extend(events)
print(f" Parsed {len(events)} events from {log_file}")
print(f"[*] Total events to analyze: {len(all_events)}")
self.analyze_dcsync_activity(all_events)
self.analyze_golden_ticket_indicators(all_events)
self.analyze_kerberoasting(all_events)
self.analyze_pass_the_hash(all_events)
self.analyze_privilege_escalation(all_events)
self.analyze_lateral_movement(all_events)
self.analyze_gpo_modifications(all_events)
return self.generate_report()
def main():
import argparse
parser = argparse.ArgumentParser(
description="Active Directory Compromise Investigation Tool"
)
parser.add_argument(
"log_files",
nargs="+",
help="Windows Security event log XML files to analyze",
)
parser.add_argument(
"-o", "--output",
default="ad_investigation_results",
help="Output directory for investigation results",
)
args = parser.parse_args()
investigator = ADCompromiseInvestigator(output_dir=args.output)
report = investigator.run_full_investigation(args.log_files)
if report["summary"]["critical_findings"] > 0:
print("\n[!!!] CRITICAL FINDINGS DETECTED - IMMEDIATE ACTION REQUIRED")
exit(1)
if __name__ == "__main__":
main()