mirror of
https://github.com/mukul975/Anthropic-Cybersecurity-Skills.git
synced 2026-07-28 07:00:58 +03:00
Add 5 new cybersecurity skills: golden ticket detection, traffic baselining, sandbox evasion analysis, domain fronting hunting, SpiderFoot OSINT
This commit is contained in:
@@ -0,0 +1,201 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Detect Kerberos Golden Ticket forgery via Windows Security event log analysis."""
|
||||
|
||||
import json
|
||||
import argparse
|
||||
import xml.etree.ElementTree as ET
|
||||
from collections import defaultdict
|
||||
from datetime import datetime
|
||||
|
||||
|
||||
def parse_security_events(xml_path):
|
||||
"""Parse exported Windows Security event log XML for Kerberos events 4768/4769."""
|
||||
tree = ET.parse(xml_path)
|
||||
root = tree.getroot()
|
||||
ns = {"e": "http://schemas.microsoft.com/win/2004/08/events/event"}
|
||||
events = []
|
||||
for event_el in root.findall(".//e:Event", ns):
|
||||
sys_el = event_el.find("e:System", ns)
|
||||
event_id = int(sys_el.find("e:EventID", ns).text)
|
||||
if event_id not in (4768, 4769):
|
||||
continue
|
||||
time_created = sys_el.find("e:TimeCreated", ns).attrib.get("SystemTime", "")
|
||||
data_el = event_el.find("e:EventData", ns)
|
||||
fields = {}
|
||||
for d in data_el.findall("e:Data", ns):
|
||||
fields[d.attrib.get("Name", "")] = d.text or ""
|
||||
events.append({"event_id": event_id, "timestamp": time_created, **fields})
|
||||
return events
|
||||
|
||||
|
||||
def detect_rc4_in_aes_environment(events):
|
||||
"""Detect RC4 encryption (0x17) in TGS requests where AES should be enforced."""
|
||||
alerts = []
|
||||
for ev in events:
|
||||
if ev["event_id"] != 4769:
|
||||
continue
|
||||
enc_type = ev.get("TicketEncryptionType", "")
|
||||
if enc_type in ("0x17", "23"):
|
||||
alerts.append({
|
||||
"detection": "RC4 Encryption in TGS Request",
|
||||
"mitre_technique": "T1558.001",
|
||||
"timestamp": ev["timestamp"],
|
||||
"user": ev.get("TargetUserName", ""),
|
||||
"domain": ev.get("TargetDomainName", ""),
|
||||
"service": ev.get("ServiceName", ""),
|
||||
"ip_address": ev.get("IpAddress", ""),
|
||||
"encryption_type": enc_type,
|
||||
"severity": "critical",
|
||||
"description": "RC4 (0x17) encryption detected in TGS request; Golden Ticket indicator in AES-enforced environments",
|
||||
})
|
||||
return alerts
|
||||
|
||||
|
||||
def detect_orphaned_tgs(events):
|
||||
"""Detect TGS requests (4769) without preceding TGT request (4768) from same user."""
|
||||
tgt_users = set()
|
||||
for ev in events:
|
||||
if ev["event_id"] == 4768:
|
||||
tgt_users.add(f"{ev.get('TargetUserName', '')}@{ev.get('TargetDomainName', '')}")
|
||||
alerts = []
|
||||
tgs_without_tgt = defaultdict(list)
|
||||
for ev in events:
|
||||
if ev["event_id"] != 4769:
|
||||
continue
|
||||
user_key = f"{ev.get('TargetUserName', '')}@{ev.get('TargetDomainName', '')}"
|
||||
if user_key not in tgt_users and ev.get("TargetUserName", ""):
|
||||
tgs_without_tgt[user_key].append(ev)
|
||||
for user, tgs_events in tgs_without_tgt.items():
|
||||
alerts.append({
|
||||
"detection": "Orphaned TGS Request (No Preceding TGT)",
|
||||
"mitre_technique": "T1558.001",
|
||||
"user": user,
|
||||
"tgs_count": len(tgs_events),
|
||||
"services": list({e.get("ServiceName", "") for e in tgs_events}),
|
||||
"source_ips": list({e.get("IpAddress", "") for e in tgs_events}),
|
||||
"first_seen": tgs_events[0]["timestamp"],
|
||||
"severity": "critical",
|
||||
"description": "TGS requests without corresponding TGT; forged ticket likely",
|
||||
})
|
||||
return alerts
|
||||
|
||||
|
||||
def detect_abnormal_ticket_lifetime(events, max_lifetime_hours=10):
|
||||
"""Detect tickets with lifetime exceeding domain policy (default MaxTicketAge=10h)."""
|
||||
user_tgt_times = defaultdict(list)
|
||||
for ev in events:
|
||||
if ev["event_id"] == 4768 and ev.get("TargetUserName"):
|
||||
try:
|
||||
ts = datetime.fromisoformat(ev["timestamp"].replace("Z", "+00:00"))
|
||||
user_tgt_times[ev["TargetUserName"]].append(ts)
|
||||
except (ValueError, AttributeError):
|
||||
continue
|
||||
alerts = []
|
||||
for user, times in user_tgt_times.items():
|
||||
if len(times) < 2:
|
||||
continue
|
||||
times.sort()
|
||||
for i in range(1, len(times)):
|
||||
gap_hours = (times[i] - times[i - 1]).total_seconds() / 3600
|
||||
if gap_hours > max_lifetime_hours * 2:
|
||||
alerts.append({
|
||||
"detection": "Abnormal TGT Renewal Gap",
|
||||
"mitre_technique": "T1558.001",
|
||||
"user": user,
|
||||
"gap_hours": round(gap_hours, 2),
|
||||
"max_expected_hours": max_lifetime_hours,
|
||||
"severity": "high",
|
||||
"description": f"TGT renewal gap of {gap_hours:.1f}h exceeds 2x MaxTicketAge ({max_lifetime_hours}h)",
|
||||
})
|
||||
return alerts
|
||||
|
||||
|
||||
def detect_krbtgt_service_anomaly(events):
|
||||
"""Detect TGS requests targeting the krbtgt service (unusual and suspicious)."""
|
||||
alerts = []
|
||||
for ev in events:
|
||||
if ev["event_id"] == 4769 and ev.get("ServiceName", "").lower().startswith("krbtgt"):
|
||||
alerts.append({
|
||||
"detection": "TGS Request Targeting krbtgt Service",
|
||||
"mitre_technique": "T1558.001",
|
||||
"timestamp": ev["timestamp"],
|
||||
"user": ev.get("TargetUserName", ""),
|
||||
"service": ev.get("ServiceName", ""),
|
||||
"ip_address": ev.get("IpAddress", ""),
|
||||
"severity": "critical",
|
||||
"description": "Direct TGS request for krbtgt service is highly anomalous",
|
||||
})
|
||||
return alerts
|
||||
|
||||
|
||||
def generate_splunk_queries():
|
||||
"""Return Splunk SPL queries for Golden Ticket detection."""
|
||||
return {
|
||||
"rc4_downgrade": (
|
||||
'index=wineventlog sourcetype="WinEventLog:Security" EventCode=4769 '
|
||||
'TicketEncryptionType=0x17 ServiceName!="krbtgt" '
|
||||
'| stats count by TargetUserName, IpAddress, ServiceName'
|
||||
),
|
||||
"orphaned_tgs": (
|
||||
'index=wineventlog EventCode=4769 '
|
||||
'| join type=left TargetUserName [search index=wineventlog EventCode=4768 '
|
||||
'| rename TargetUserName as tgt_user | dedup tgt_user | fields tgt_user] '
|
||||
'| where isnull(tgt_user) | stats count by TargetUserName, IpAddress'
|
||||
),
|
||||
"krbtgt_tgs": (
|
||||
'index=wineventlog EventCode=4769 ServiceName="krbtgt*" '
|
||||
'| table _time, TargetUserName, IpAddress, ServiceName, TicketEncryptionType'
|
||||
),
|
||||
}
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description="Golden Ticket Forgery Detector")
|
||||
parser.add_argument("--evtx-xml", help="Path to exported Security event log XML")
|
||||
parser.add_argument("--max-ticket-hours", type=int, default=10, help="MaxTicketAge in hours (default: 10)")
|
||||
parser.add_argument("--output", default="golden_ticket_report.json", help="Output report path")
|
||||
parser.add_argument("--show-splunk", action="store_true", help="Print Splunk SPL queries")
|
||||
args = parser.parse_args()
|
||||
|
||||
if args.show_splunk:
|
||||
for name, spl in generate_splunk_queries().items():
|
||||
print(f"\n--- {name} ---\n{spl}")
|
||||
return
|
||||
|
||||
if not args.evtx_xml:
|
||||
print("[!] Provide --evtx-xml path or use --show-splunk for detection queries")
|
||||
return
|
||||
|
||||
events = parse_security_events(args.evtx_xml)
|
||||
print(f"[+] Parsed {len(events)} Kerberos events (4768/4769)")
|
||||
|
||||
rc4_alerts = detect_rc4_in_aes_environment(events)
|
||||
orphan_alerts = detect_orphaned_tgs(events)
|
||||
lifetime_alerts = detect_abnormal_ticket_lifetime(events, args.max_ticket_hours)
|
||||
krbtgt_alerts = detect_krbtgt_service_anomaly(events)
|
||||
|
||||
report = {
|
||||
"analysis_time": datetime.utcnow().isoformat() + "Z",
|
||||
"total_events": len(events),
|
||||
"detections": {
|
||||
"rc4_encryption_downgrade": rc4_alerts,
|
||||
"orphaned_tgs_requests": orphan_alerts,
|
||||
"abnormal_ticket_lifetime": lifetime_alerts,
|
||||
"krbtgt_service_anomaly": krbtgt_alerts,
|
||||
},
|
||||
"total_alerts": len(rc4_alerts) + len(orphan_alerts) + len(lifetime_alerts) + len(krbtgt_alerts),
|
||||
"mitre_techniques": ["T1558.001"],
|
||||
"splunk_queries": generate_splunk_queries(),
|
||||
}
|
||||
|
||||
with open(args.output, "w") as f:
|
||||
json.dump(report, f, indent=2)
|
||||
print(f"[+] RC4 downgrades: {len(rc4_alerts)}")
|
||||
print(f"[+] Orphaned TGS: {len(orphan_alerts)}")
|
||||
print(f"[+] Lifetime anomalies: {len(lifetime_alerts)}")
|
||||
print(f"[+] krbtgt anomalies: {len(krbtgt_alerts)}")
|
||||
print(f"[+] Report saved to {args.output}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user