Files
Anthropic-Cybersecurity-Skills/skills/analyzing-security-logs-with-splunk/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

165 lines
6.5 KiB
Python

#!/usr/bin/env python3
"""Agent for analyzing security logs with Splunk using splunk-sdk."""
import os
import sys
import json
import time
import argparse
from datetime import datetime, timedelta
import splunklib.client as client
import splunklib.results as results
def connect_splunk(host, port, username, password):
"""Establish connection to Splunk instance."""
service = client.connect(
host=host,
port=port,
username=username,
password=password,
autologin=True,
)
return service
def run_search(service, query, earliest="-24h", latest="now"):
"""Execute a Splunk search and return parsed results."""
kwargs_search = {
"earliest_time": earliest,
"latest_time": latest,
"search_mode": "normal",
"exec_mode": "blocking",
}
job = service.jobs.create(f"search {query}", **kwargs_search)
reader = results.JSONResultsReader(job.results(output_mode="json"))
rows = [row for row in reader if isinstance(row, dict)]
job.cancel()
return rows
def detect_brute_force(service, threshold=10, earliest="-24h"):
"""Detect brute force attacks via failed logon events (EventCode 4625)."""
query = (
'index=windows sourcetype="WinEventLog:Security" EventCode=4625 '
f"| stats count as failed_attempts, dc(src_ip) as unique_sources, "
f"values(src_ip) as source_ips by TargetUserName "
f"| where failed_attempts > {threshold} "
f"| sort -failed_attempts"
)
return run_search(service, query, earliest=earliest)
def detect_lateral_movement(service, earliest="-24h"):
"""Detect lateral movement via Type 3 network logons to multiple hosts."""
query = (
'index=windows sourcetype="WinEventLog:Security" EventCode=4624 '
"Logon_Type=3 "
"| stats dc(ComputerName) as unique_targets, values(ComputerName) as targets "
"by TargetUserName, src_ip "
"| where unique_targets > 3 "
"| sort -unique_targets"
)
return run_search(service, query, earliest=earliest)
def detect_suspicious_powershell(service, earliest="-24h"):
"""Detect encoded or download-cradle PowerShell execution via Sysmon."""
query = (
'index=sysmon EventCode=1 Image="*\\\\powershell.exe" '
'(CommandLine="*-enc*" OR CommandLine="*-encodedcommand*" '
'OR CommandLine="*downloadstring*" OR CommandLine="*iex*") '
"| table _time, host, User, ParentImage, CommandLine "
"| sort _time"
)
return run_search(service, query, earliest=earliest)
def detect_lsass_access(service, earliest="-24h"):
"""Detect credential dumping via LSASS process access (Sysmon Event 10)."""
query = (
'index=sysmon EventCode=10 TargetImage="*\\\\lsass.exe" '
"GrantedAccess=0x1010 "
"| table _time, host, SourceImage, SourceUser, GrantedAccess"
)
return run_search(service, query, earliest=earliest)
def build_incident_timeline(service, hosts, users, earliest="-24h", latest="now"):
"""Build a unified incident timeline across multiple log sources."""
host_filter = " OR ".join(f'host="{h}"' for h in hosts)
user_filter = " OR ".join(f'user="{u}"' for u in users)
query = (
f"index=windows OR index=sysmon OR index=proxy OR index=firewall "
f"({host_filter} OR {user_filter}) "
'| eval event_summary=case('
' sourcetype=="WinEventLog:Security" AND EventCode==4624, '
' "Logon: ".TargetUserName." from ".src_ip, '
' sourcetype=="WinEventLog:Security" AND EventCode==4625, '
' "Failed logon: ".TargetUserName, '
' EventCode==1, "Process: ".Image." by ".User, '
' 1==1, sourcetype.": ".EventCode) '
"| table _time, sourcetype, host, event_summary "
"| sort _time"
)
return run_search(service, query, earliest=earliest, latest=latest)
def generate_report(findings):
"""Format investigation findings into a structured report."""
report = {
"report_type": "SPLUNK INVESTIGATION REPORT",
"generated_at": datetime.utcnow().isoformat() + "Z",
"findings": findings,
}
return json.dumps(report, indent=2, default=str)
def main():
parser = argparse.ArgumentParser(description="Splunk Security Log Analysis Agent")
parser.add_argument("--host", default=os.getenv("SPLUNK_HOST", "localhost"))
parser.add_argument("--port", type=int, default=int(os.getenv("SPLUNK_PORT", "8089")))
parser.add_argument("--username", default=os.getenv("SPLUNK_USERNAME", "admin"))
parser.add_argument("--password", default=os.getenv("SPLUNK_PASSWORD", ""))
parser.add_argument("--earliest", default="-24h", help="Search earliest time")
parser.add_argument("--action", choices=[
"brute_force", "lateral_movement", "powershell",
"lsass_access", "timeline", "full_investigation"
], default="full_investigation")
parser.add_argument("--hosts", nargs="*", default=[], help="Target hosts for timeline")
parser.add_argument("--users", nargs="*", default=[], help="Target users for timeline")
parser.add_argument("--threshold", type=int, default=10)
args = parser.parse_args()
service = connect_splunk(args.host, args.port, args.username, args.password)
findings = {}
if args.action in ("brute_force", "full_investigation"):
findings["brute_force"] = detect_brute_force(service, args.threshold, args.earliest)
print(f"[+] Brute force: {len(findings['brute_force'])} accounts targeted")
if args.action in ("lateral_movement", "full_investigation"):
findings["lateral_movement"] = detect_lateral_movement(service, args.earliest)
print(f"[+] Lateral movement: {len(findings['lateral_movement'])} suspicious paths")
if args.action in ("powershell", "full_investigation"):
findings["suspicious_powershell"] = detect_suspicious_powershell(service, args.earliest)
print(f"[+] Suspicious PowerShell: {len(findings['suspicious_powershell'])} events")
if args.action in ("lsass_access", "full_investigation"):
findings["lsass_access"] = detect_lsass_access(service, args.earliest)
print(f"[+] LSASS access: {len(findings['lsass_access'])} events")
if args.action == "timeline" and args.hosts:
findings["timeline"] = build_incident_timeline(
service, args.hosts, args.users, args.earliest
)
print(f"[+] Timeline: {len(findings['timeline'])} events")
print(generate_report(findings))
if __name__ == "__main__":
main()