mirror of
https://github.com/mukul975/Anthropic-Cybersecurity-Skills.git
synced 2026-09-06 16:30:50 +03:00
Add 5 new cybersecurity skills with full implementations
- implementing-vulnerability-management-with-greenbone: python-gvm GMP API, scan task creation, XML report parsing - detecting-email-account-compromise: Microsoft Graph inbox rules, impossible travel detection, OAuth grant analysis - performing-threat-intelligence-sharing-with-misp: PyMISP event creation, attribute management, sharing validation - analyzing-cobaltstrike-malleable-c2-profiles: dissect.cobaltstrike C2Profile parsing, Suricata rule generation - hunting-for-registry-run-key-persistence: Sysmon Event 13 analysis, T1547.001 detection, Sigma rule generation
This commit is contained in:
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2025 Mahipal
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
@@ -0,0 +1,44 @@
|
||||
---
|
||||
name: detecting-pass-the-ticket-attacks
|
||||
description: Detect Kerberos Pass-the-Ticket (PtT) attacks by analyzing Windows Event IDs 4768, 4769, and 4771 for anomalous ticket usage patterns in Splunk and Elastic SIEM
|
||||
domain: cybersecurity
|
||||
subdomain: threat-detection
|
||||
tags:
|
||||
- kerberos
|
||||
- pass-the-ticket
|
||||
- active-directory
|
||||
- splunk
|
||||
- elastic
|
||||
- credential-theft
|
||||
- windows-security
|
||||
version: "1.0"
|
||||
author: mahipal
|
||||
license: Apache-2.0
|
||||
---
|
||||
|
||||
# Detecting Pass-the-Ticket Attacks
|
||||
|
||||
## Overview
|
||||
|
||||
Pass-the-Ticket (PtT) is a credential theft technique (MITRE ATT&CK T1550.003) where adversaries steal Kerberos tickets (TGT or TGS) from one system and replay them on another to authenticate without knowing the user's password. This skill teaches detection of PtT attacks by correlating Windows Security Event IDs 4768 (TGT request), 4769 (TGS request), and 4771 (pre-authentication failure) for anomalies such as ticket reuse across different hosts, RC4 encryption downgrades, and unusual service ticket request volumes.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- Windows Domain Controller with advanced audit policy enabled (Audit Kerberos Authentication Service, Audit Kerberos Service Ticket Operations)
|
||||
- Splunk or Elastic SIEM ingesting Windows Security event logs
|
||||
- Sysmon deployed on endpoints for supplementary process telemetry
|
||||
- Python 3.8+ with `requests` library
|
||||
|
||||
## Steps
|
||||
|
||||
1. Enable Kerberos audit logging on Domain Controllers via Group Policy
|
||||
2. Forward Event IDs 4768, 4769, and 4771 to SIEM platform
|
||||
3. Deploy detection rules for RC4 encryption downgrade (TicketEncryptionType 0x17)
|
||||
4. Create correlation rule for ticket reuse across multiple source IPs
|
||||
5. Build baseline of normal TGS request volume per user/host
|
||||
6. Alert on standard deviation anomalies in ticket request patterns
|
||||
7. Investigate flagged events with enrichment from Active Directory
|
||||
|
||||
## Expected Output
|
||||
|
||||
JSON report containing detected PtT indicators including anomalous ticket requests, RC4 downgrades, cross-host ticket reuse events, and risk-scored users with MITRE ATT&CK technique mapping.
|
||||
@@ -0,0 +1,104 @@
|
||||
# Pass-the-Ticket Detection API Reference
|
||||
|
||||
## Windows Security Event IDs
|
||||
|
||||
### Event ID 4768 - TGT Requested
|
||||
```
|
||||
Key Fields:
|
||||
TargetUserName - Account requesting TGT
|
||||
TargetDomainName - Domain of account
|
||||
IpAddress - Source IP of request
|
||||
TicketEncryptionType - 0x12 (AES256), 0x17 (RC4-HMAC)
|
||||
PreAuthType - 15 (PA-ENC-TIMESTAMP)
|
||||
```
|
||||
|
||||
### Event ID 4769 - TGS Requested
|
||||
```
|
||||
Key Fields:
|
||||
TargetUserName - Account using the ticket
|
||||
ServiceName - SPN of requested service
|
||||
IpAddress - Source IP
|
||||
TicketEncryptionType - 0x17 indicates RC4 downgrade
|
||||
TicketOptions - Kerberos ticket flags
|
||||
```
|
||||
|
||||
### Event ID 4771 - Kerberos Pre-Authentication Failed
|
||||
```
|
||||
Key Fields:
|
||||
TargetUserName - Account that failed
|
||||
IpAddress - Source of failure
|
||||
Status - 0x18 (wrong password), 0x12 (expired)
|
||||
```
|
||||
|
||||
## Splunk SPL Queries
|
||||
|
||||
### RC4 Encryption Downgrade Detection
|
||||
```spl
|
||||
index=wineventlog sourcetype="WinEventLog:Security" EventCode=4769
|
||||
TicketEncryptionType=0x17
|
||||
| stats count by TargetUserName, IpAddress, ServiceName
|
||||
| where count > 3
|
||||
```
|
||||
|
||||
### Cross-Host Ticket Reuse
|
||||
```spl
|
||||
index=wineventlog EventCode=4769
|
||||
| stats dc(IpAddress) as ip_count, values(IpAddress) as ips
|
||||
by TargetUserName
|
||||
| where ip_count > 1
|
||||
| sort -ip_count
|
||||
```
|
||||
|
||||
### TGS Volume Anomaly
|
||||
```spl
|
||||
index=wineventlog EventCode=4769
|
||||
| bin _time span=1h
|
||||
| stats count by TargetUserName, _time
|
||||
| eventstats avg(count) as avg_count, stdev(count) as sd by TargetUserName
|
||||
| where count > avg_count + (3 * sd)
|
||||
```
|
||||
|
||||
## Elastic / KQL Queries
|
||||
|
||||
### RC4 Downgrade in Elastic
|
||||
```kql
|
||||
event.code: "4769" AND winlog.event_data.TicketEncryptionType: "0x17"
|
||||
```
|
||||
|
||||
### Cross-Host Reuse in Elastic
|
||||
```json
|
||||
POST security-*/_search
|
||||
{
|
||||
"size": 0,
|
||||
"query": { "term": { "event.code": "4769" } },
|
||||
"aggs": {
|
||||
"by_user": {
|
||||
"terms": { "field": "winlog.event_data.TargetUserName" },
|
||||
"aggs": {
|
||||
"unique_ips": { "cardinality": { "field": "source.ip" } }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## MITRE ATT&CK Mapping
|
||||
|
||||
| Technique | ID | Detection |
|
||||
|---|---|---|
|
||||
| Use Alternate Authentication Material: Pass the Ticket | T1550.003 | RC4 downgrade, cross-host reuse |
|
||||
| Steal or Forge Kerberos Tickets: Kerberoasting | T1558.003 | High TGS volume for SPNs |
|
||||
| Brute Force: Password Spraying | T1110.003 | Pre-auth failure spikes |
|
||||
|
||||
## CLI Usage
|
||||
|
||||
```bash
|
||||
# Parse exported event log XML and detect PtT indicators
|
||||
python agent.py --evtx-xml security_events.xml --output report.json
|
||||
|
||||
# Show Splunk detection queries
|
||||
python agent.py --show-splunk
|
||||
|
||||
# Custom thresholds
|
||||
python agent.py --evtx-xml events.xml --tgs-threshold 30 --preauth-threshold 5
|
||||
```
|
||||
@@ -0,0 +1,203 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Detect Kerberos Pass-the-Ticket attacks via Windows Event ID 4768/4769/4771 analysis."""
|
||||
|
||||
import json
|
||||
import argparse
|
||||
import xml.etree.ElementTree as ET
|
||||
from collections import defaultdict
|
||||
from datetime import datetime
|
||||
|
||||
|
||||
def parse_evtx_xml(xml_path):
|
||||
"""Parse exported Windows Security event log XML for Kerberos events."""
|
||||
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, 4771):
|
||||
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,
|
||||
"target_user": fields.get("TargetUserName", ""),
|
||||
"target_domain": fields.get("TargetDomainName", ""),
|
||||
"ip_address": fields.get("IpAddress", ""),
|
||||
"service_name": fields.get("ServiceName", ""),
|
||||
"ticket_encryption_type": fields.get("TicketEncryptionType", ""),
|
||||
"status": fields.get("Status", ""),
|
||||
"pre_auth_type": fields.get("PreAuthType", ""),
|
||||
})
|
||||
return events
|
||||
|
||||
|
||||
def detect_rc4_downgrade(events):
|
||||
"""Detect RC4 encryption downgrade (TicketEncryptionType 0x17) in TGS requests."""
|
||||
alerts = []
|
||||
for ev in events:
|
||||
enc_type = ev["ticket_encryption_type"]
|
||||
if enc_type in ("0x17", "23"):
|
||||
alerts.append({
|
||||
"detection": "RC4 Encryption Downgrade",
|
||||
"mitre_technique": "T1550.003",
|
||||
"event_id": ev["event_id"],
|
||||
"timestamp": ev["timestamp"],
|
||||
"user": ev["target_user"],
|
||||
"domain": ev["target_domain"],
|
||||
"service": ev["service_name"],
|
||||
"ip_address": ev["ip_address"],
|
||||
"encryption_type": enc_type,
|
||||
"severity": "high",
|
||||
"description": "RC4 (0x17) ticket encryption detected; may indicate Pass-the-Ticket or Kerberoasting",
|
||||
})
|
||||
return alerts
|
||||
|
||||
|
||||
def detect_cross_host_ticket_reuse(events):
|
||||
"""Detect same user TGS requests from multiple source IPs within short window."""
|
||||
user_ips = defaultdict(set)
|
||||
user_events = defaultdict(list)
|
||||
for ev in events:
|
||||
if ev["event_id"] == 4769 and ev["target_user"] and ev["ip_address"]:
|
||||
key = f"{ev['target_user']}@{ev['target_domain']}"
|
||||
user_ips[key].add(ev["ip_address"])
|
||||
user_events[key].append(ev)
|
||||
alerts = []
|
||||
for user, ips in user_ips.items():
|
||||
if len(ips) >= 2:
|
||||
sample = user_events[user][:5]
|
||||
alerts.append({
|
||||
"detection": "Cross-Host Ticket Reuse",
|
||||
"mitre_technique": "T1550.003",
|
||||
"user": user,
|
||||
"source_ips": list(ips),
|
||||
"ip_count": len(ips),
|
||||
"request_count": len(user_events[user]),
|
||||
"severity": "critical",
|
||||
"sample_timestamps": [e["timestamp"] for e in sample],
|
||||
"description": "Same user ticket used from multiple IPs, indicating stolen ticket replay",
|
||||
})
|
||||
return alerts
|
||||
|
||||
|
||||
def detect_anomalous_tgs_volume(events, threshold=50):
|
||||
"""Detect users requesting abnormally high number of TGS tickets."""
|
||||
user_tgs = defaultdict(int)
|
||||
for ev in events:
|
||||
if ev["event_id"] == 4769 and ev["target_user"]:
|
||||
user_tgs[f"{ev['target_user']}@{ev['target_domain']}"] += 1
|
||||
alerts = []
|
||||
for user, count in user_tgs.items():
|
||||
if count >= threshold:
|
||||
alerts.append({
|
||||
"detection": "Anomalous TGS Volume",
|
||||
"mitre_technique": "T1550.003",
|
||||
"user": user,
|
||||
"tgs_request_count": count,
|
||||
"threshold": threshold,
|
||||
"severity": "high",
|
||||
"description": f"User requested {count} service tickets (threshold: {threshold})",
|
||||
})
|
||||
return alerts
|
||||
|
||||
|
||||
def detect_preauth_failures(events, threshold=10):
|
||||
"""Detect excessive Kerberos pre-authentication failures (Event ID 4771)."""
|
||||
user_failures = defaultdict(int)
|
||||
for ev in events:
|
||||
if ev["event_id"] == 4771:
|
||||
user_failures[f"{ev['target_user']}@{ev['target_domain']}"] += 1
|
||||
alerts = []
|
||||
for user, count in user_failures.items():
|
||||
if count >= threshold:
|
||||
alerts.append({
|
||||
"detection": "Excessive Pre-Auth Failures",
|
||||
"mitre_technique": "T1110.003",
|
||||
"user": user,
|
||||
"failure_count": count,
|
||||
"severity": "medium",
|
||||
"description": f"{count} Kerberos pre-authentication failures detected",
|
||||
})
|
||||
return alerts
|
||||
|
||||
|
||||
def generate_splunk_queries():
|
||||
"""Return SPL queries for Splunk-based PtT detection."""
|
||||
return {
|
||||
"rc4_downgrade": (
|
||||
'index=wineventlog sourcetype="WinEventLog:Security" EventCode=4769 '
|
||||
'TicketEncryptionType=0x17 | stats count by TargetUserName, IpAddress, ServiceName'
|
||||
),
|
||||
"cross_host_reuse": (
|
||||
'index=wineventlog EventCode=4769 | stats dc(IpAddress) as ip_count, '
|
||||
'values(IpAddress) as source_ips by TargetUserName | where ip_count > 1'
|
||||
),
|
||||
"tgs_volume_anomaly": (
|
||||
'index=wineventlog EventCode=4769 | stats count by TargetUserName '
|
||||
'| where count > 50 | sort -count'
|
||||
),
|
||||
"preauth_failures": (
|
||||
'index=wineventlog EventCode=4771 | stats count by TargetUserName, IpAddress '
|
||||
'| where count > 10'
|
||||
),
|
||||
}
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description="Pass-the-Ticket Attack Detector")
|
||||
parser.add_argument("--evtx-xml", help="Path to exported Security event log XML")
|
||||
parser.add_argument("--tgs-threshold", type=int, default=50, help="TGS volume alert threshold")
|
||||
parser.add_argument("--preauth-threshold", type=int, default=10, help="Pre-auth failure threshold")
|
||||
parser.add_argument("--output", default="ptt_detection_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:
|
||||
queries = generate_splunk_queries()
|
||||
for name, spl in queries.items():
|
||||
print(f"\n--- {name} ---\n{spl}")
|
||||
return
|
||||
|
||||
if not args.evtx_xml:
|
||||
print("[!] Provide --evtx-xml path to exported Windows Security event log XML")
|
||||
print("[*] Or use --show-splunk to get Splunk detection queries")
|
||||
return
|
||||
|
||||
events = parse_evtx_xml(args.evtx_xml)
|
||||
print(f"[+] Parsed {len(events)} Kerberos events (4768/4769/4771)")
|
||||
|
||||
rc4_alerts = detect_rc4_downgrade(events)
|
||||
reuse_alerts = detect_cross_host_ticket_reuse(events)
|
||||
volume_alerts = detect_anomalous_tgs_volume(events, args.tgs_threshold)
|
||||
preauth_alerts = detect_preauth_failures(events, args.preauth_threshold)
|
||||
|
||||
report = {
|
||||
"analysis_time": datetime.utcnow().isoformat() + "Z",
|
||||
"total_kerberos_events": len(events),
|
||||
"detections": {
|
||||
"rc4_downgrade": rc4_alerts,
|
||||
"cross_host_ticket_reuse": reuse_alerts,
|
||||
"anomalous_tgs_volume": volume_alerts,
|
||||
"preauth_failures": preauth_alerts,
|
||||
},
|
||||
"total_alerts": len(rc4_alerts) + len(reuse_alerts) + len(volume_alerts) + len(preauth_alerts),
|
||||
"mitre_techniques": ["T1550.003", "T1558.003", "T1110.003"],
|
||||
"splunk_queries": generate_splunk_queries(),
|
||||
}
|
||||
|
||||
with open(args.output, "w") as f:
|
||||
json.dump(report, f, indent=2)
|
||||
print(f"[+] Alerts: RC4={len(rc4_alerts)}, Reuse={len(reuse_alerts)}, "
|
||||
f"Volume={len(volume_alerts)}, PreAuth={len(preauth_alerts)}")
|
||||
print(f"[+] Report saved to {args.output}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user