mirror of
https://github.com/mukul975/Anthropic-Cybersecurity-Skills.git
synced 2026-07-17 21:19:40 +03:00
Initial commit - 611 cybersecurity skills across all subdomains
This commit is contained in:
@@ -0,0 +1,558 @@
|
||||
"""
|
||||
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()
|
||||
Reference in New Issue
Block a user