Files
Anthropic-Cybersecurity-Skills/skills/building-cloud-siem-with-sentinel/scripts/agent.py
T
mukul975 27c6414ca5 Add folder anatomy (scripts/agent.py + references/api-reference.md) for 648 cybersecurity skills
Complete skill folder anatomy across all cybersecurity skills:
- scripts/agent.py: 80-150 line Python agents using real libraries (impacket,
  boto3, azure-mgmt-*, kubernetes, pefile, yara, scapy, shodan, stix2, etc.)
- references/api-reference.md: real API documentation with method signatures
- LICENSE: MIT license for all skill folders
2026-03-10 21:02:12 +01:00

170 lines
6.7 KiB
Python

#!/usr/bin/env python3
"""Agent for managing Microsoft Sentinel SIEM operations."""
import os
import json
import argparse
from datetime import datetime, timedelta
from azure.identity import DefaultAzureCredential, ClientSecretCredential
from azure.monitor.query import LogsQueryClient, LogsQueryStatus
from azure.mgmt.securityinsight import SecurityInsights
def get_credential(tenant_id=None, client_id=None, client_secret=None):
"""Get Azure credential for authentication."""
if client_id and client_secret and tenant_id:
return ClientSecretCredential(tenant_id, client_id, client_secret)
return DefaultAzureCredential()
def run_kql_query(credential, workspace_id, query, timespan_hours=24):
"""Execute a KQL query against a Log Analytics workspace."""
client = LogsQueryClient(credential)
timespan = timedelta(hours=timespan_hours)
response = client.query_workspace(workspace_id, query, timespan=timespan)
if response.status == LogsQueryStatus.SUCCESS:
rows = []
for table in response.tables:
columns = [col.name for col in table.columns]
for row in table.rows:
rows.append(dict(zip(columns, row)))
return rows
return []
def detect_impossible_travel(credential, workspace_id):
"""Detect impossible travel sign-ins using KQL."""
query = """
SigninLogs
| where ResultType == 0
| project TimeGenerated, UserPrincipalName, IPAddress,
Latitude = toreal(LocationDetails.geoCoordinates.latitude),
Longitude = toreal(LocationDetails.geoCoordinates.longitude)
| sort by UserPrincipalName asc, TimeGenerated asc
| extend PrevLat = prev(Latitude), PrevLon = prev(Longitude),
PrevTime = prev(TimeGenerated), PrevUser = prev(UserPrincipalName)
| where UserPrincipalName == PrevUser
| extend TimeDiff = datetime_diff('minute', TimeGenerated, PrevTime)
| where TimeDiff < 60
| extend Distance = geo_distance_2points(Longitude, Latitude, PrevLon, PrevLat) / 1000
| where Distance > 500
| project TimeGenerated, UserPrincipalName, IPAddress, Distance, TimeDiff
"""
return run_kql_query(credential, workspace_id, query, 24)
def detect_aws_credential_abuse(credential, workspace_id):
"""Detect AWS credential abuse via CloudTrail in Sentinel."""
query = """
AWSCloudTrail
| where EventName in ("ConsoleLogin", "AssumeRole", "GetSessionToken")
| where ErrorCode == ""
| summarize LoginCount = count(), DistinctIPs = dcount(SourceIpAddress),
IPList = make_set(SourceIpAddress, 10)
by UserIdentityArn, bin(TimeGenerated, 1h)
| where DistinctIPs > 3
"""
return run_kql_query(credential, workspace_id, query, 24)
def detect_mass_deletion(credential, workspace_id):
"""Detect mass S3 object deletion (potential ransomware)."""
query = """
AWSCloudTrail
| where EventName in ("DeleteObject", "DeleteObjects")
| summarize DeleteCount = count(), Buckets = dcount(RequestParameters_bucketName)
by UserIdentityArn, bin(TimeGenerated, 10m)
| where DeleteCount > 100
"""
return run_kql_query(credential, workspace_id, query, 24)
def get_incident_summary(credential, workspace_id, days=7):
"""Get incident summary from Sentinel."""
query = f"""
SecurityIncident
| where TimeGenerated > ago({days}d)
| summarize count() by Severity
| order by Severity
"""
return run_kql_query(credential, workspace_id, query, days * 24)
def match_threat_intelligence(credential, workspace_id):
"""Match network traffic against threat intelligence indicators."""
query = """
let TI_IPs = ThreatIntelligenceIndicator
| where isnotempty(NetworkIP)
| distinct NetworkIP;
CommonSecurityLog
| where DestinationIP in (TI_IPs)
| project TimeGenerated, SourceIP, DestinationIP, DestinationPort, DeviceAction
| take 100
"""
return run_kql_query(credential, workspace_id, query, 24)
def list_analytics_rules(credential, subscription_id, resource_group, workspace_name):
"""List active Sentinel analytics rules."""
client = SecurityInsights(credential, subscription_id)
rules = client.alert_rules.list(resource_group, workspace_name)
result = []
for rule in rules:
result.append({
"name": rule.display_name if hasattr(rule, "display_name") else rule.name,
"severity": getattr(rule, "severity", "Unknown"),
"enabled": getattr(rule, "enabled", True),
})
return result
def main():
parser = argparse.ArgumentParser(description="Microsoft Sentinel SIEM Agent")
parser.add_argument("--workspace-id", default=os.getenv("SENTINEL_WORKSPACE_ID"))
parser.add_argument("--tenant-id", default=os.getenv("AZURE_TENANT_ID"))
parser.add_argument("--client-id", default=os.getenv("AZURE_CLIENT_ID"))
parser.add_argument("--client-secret", default=os.getenv("AZURE_CLIENT_SECRET"))
parser.add_argument("--output", default="sentinel_report.json")
parser.add_argument("--action", choices=[
"impossible_travel", "aws_abuse", "mass_deletion",
"incidents", "threat_intel", "full_hunt"
], default="full_hunt")
args = parser.parse_args()
credential = get_credential(args.tenant_id, args.client_id, args.client_secret)
report = {"scan_date": datetime.utcnow().isoformat(), "findings": {}}
if args.action in ("impossible_travel", "full_hunt"):
results = detect_impossible_travel(credential, args.workspace_id)
report["findings"]["impossible_travel"] = results
print(f"[+] Impossible travel detections: {len(results)}")
if args.action in ("aws_abuse", "full_hunt"):
results = detect_aws_credential_abuse(credential, args.workspace_id)
report["findings"]["aws_credential_abuse"] = results
print(f"[+] AWS credential abuse events: {len(results)}")
if args.action in ("mass_deletion", "full_hunt"):
results = detect_mass_deletion(credential, args.workspace_id)
report["findings"]["mass_deletion"] = results
print(f"[+] Mass deletion events: {len(results)}")
if args.action in ("incidents", "full_hunt"):
results = get_incident_summary(credential, args.workspace_id)
report["findings"]["incidents_7d"] = results
print(f"[+] Incident summary (7d): {results}")
if args.action in ("threat_intel", "full_hunt"):
results = match_threat_intelligence(credential, args.workspace_id)
report["findings"]["ti_matches"] = results
print(f"[+] Threat intel matches: {len(results)}")
with open(args.output, "w") as f:
json.dump(report, f, indent=2, default=str)
print(f"[+] Report saved to {args.output}")
if __name__ == "__main__":
main()