mirror of
https://github.com/mukul975/Anthropic-Cybersecurity-Skills.git
synced 2026-06-12 22:24:56 +03:00
c47eed6a64
- Fix 25 shell=True subprocess calls with list-based commands - Fix 49 verify=False in defensive skills (env-var override) - Add timeout to 231 HTTP/subprocess/socket calls - Fix 6 SQL injection patterns with whitelist validation - Replace 8 __import__() with standard imports - Remove 701 unused imports across 442 files - Add authorized-testing disclaimers to all offensive skills - Complete 11 incomplete skill directories - Expand 10 stub SKILL.md files with full content - Fix 2 YAML parse errors in frontmatter - Fix 5 pre-existing syntax errors - Convert 22 hardcoded paths/ports to environment variables - Back up 21 redundant skill pairs to .bak - Fix 2 global declaration errors - 724/724 skills with full folder anatomy (SKILL.md + agent.py + api-reference.md + LICENSE) - 0 compile errors across all 724 agent.py files
143 lines
6.0 KiB
Python
143 lines
6.0 KiB
Python
#!/usr/bin/env python3
|
|
"""Splunk SPL Detection Rule Builder Agent - Generates and validates Splunk detection rules."""
|
|
|
|
import json
|
|
import logging
|
|
import os
|
|
import argparse
|
|
from datetime import datetime
|
|
|
|
import requests
|
|
|
|
logging.basicConfig(level=logging.INFO, format="%(asctime)s [%(levelname)s] %(message)s")
|
|
logger = logging.getLogger(__name__)
|
|
|
|
DETECTION_TEMPLATES = {
|
|
"brute_force": {
|
|
"spl": 'index=main sourcetype=WinEventLog:Security EventCode=4625 | stats count by src_ip, user | where count > {threshold}',
|
|
"description": "Detect brute force login attempts",
|
|
"mitre": "T1110",
|
|
"severity": "high",
|
|
},
|
|
"lateral_movement_rdp": {
|
|
"spl": 'index=main sourcetype=WinEventLog:Security EventCode=4624 Logon_Type=10 | stats count by src_ip, dest, user | where count > 1',
|
|
"description": "Detect RDP lateral movement",
|
|
"mitre": "T1021.001",
|
|
"severity": "high",
|
|
},
|
|
"powershell_encoded": {
|
|
"spl": 'index=main sourcetype=WinEventLog:Security EventCode=4104 ScriptBlockText="*-EncodedCommand*" OR ScriptBlockText="*FromBase64String*" | table _time, ComputerName, ScriptBlockText',
|
|
"description": "Detect encoded PowerShell execution",
|
|
"mitre": "T1059.001",
|
|
"severity": "critical",
|
|
},
|
|
"suspicious_process": {
|
|
"spl": 'index=main sourcetype=sysmon EventCode=1 (ParentImage="*\\\\cmd.exe" OR ParentImage="*\\\\powershell.exe") (Image="*\\\\whoami.exe" OR Image="*\\\\net.exe" OR Image="*\\\\nltest.exe") | table _time, Computer, User, ParentImage, Image, CommandLine',
|
|
"description": "Detect suspicious child process spawning",
|
|
"mitre": "T1059.003",
|
|
"severity": "high",
|
|
},
|
|
"scheduled_task_creation": {
|
|
"spl": 'index=main sourcetype=WinEventLog:Security EventCode=4698 | table _time, SubjectUserName, TaskName, TaskContent',
|
|
"description": "Detect new scheduled task creation",
|
|
"mitre": "T1053.005",
|
|
"severity": "medium",
|
|
},
|
|
"credential_dumping": {
|
|
"spl": 'index=main sourcetype=sysmon EventCode=10 TargetImage="*\\\\lsass.exe" GrantedAccess IN ("0x1010", "0x1038", "0x1fffff") | table _time, SourceImage, TargetImage, GrantedAccess',
|
|
"description": "Detect LSASS memory access (credential dumping)",
|
|
"mitre": "T1003.001",
|
|
"severity": "critical",
|
|
},
|
|
"data_exfiltration": {
|
|
"spl": 'index=main sourcetype=proxy | stats sum(bytes_out) as total_bytes by src_ip, dest | where total_bytes > {threshold_bytes} | eval MB=round(total_bytes/1024/1024,2)',
|
|
"description": "Detect large data transfers (exfiltration)",
|
|
"mitre": "T1048",
|
|
"severity": "high",
|
|
},
|
|
}
|
|
|
|
|
|
def generate_spl_rule(template_name, params=None):
|
|
"""Generate an SPL detection rule from template."""
|
|
if template_name not in DETECTION_TEMPLATES:
|
|
return {"error": f"Unknown template: {template_name}"}
|
|
template = DETECTION_TEMPLATES[template_name]
|
|
spl = template["spl"]
|
|
if params:
|
|
for key, value in params.items():
|
|
spl = spl.replace(f"{{{key}}}", str(value))
|
|
return {
|
|
"name": template_name,
|
|
"spl": spl,
|
|
"description": template["description"],
|
|
"mitre_technique": template["mitre"],
|
|
"severity": template["severity"],
|
|
}
|
|
|
|
|
|
def deploy_saved_search(splunk_url, token, rule_name, spl, severity="high"):
|
|
"""Deploy a saved search to Splunk via REST API."""
|
|
headers = {"Authorization": f"Bearer {token}"}
|
|
data = {
|
|
"name": f"Detection - {rule_name}",
|
|
"search": spl,
|
|
"is_scheduled": 1,
|
|
"cron_schedule": "*/5 * * * *",
|
|
"dispatch.earliest_time": "-5m",
|
|
"dispatch.latest_time": "now",
|
|
"alert.severity": {"info": 1, "low": 2, "medium": 3, "high": 4, "critical": 5}.get(severity, 4),
|
|
"alert_type": "number of events",
|
|
"alert_comparator": "greater than",
|
|
"alert_threshold": "0",
|
|
"actions": "email",
|
|
}
|
|
try:
|
|
resp = requests.post(f"{splunk_url}/servicesNS/admin/search/saved/searches", headers=headers, data=data, verify=not os.environ.get("SKIP_TLS_VERIFY", "").lower() == "true", timeout=30) # Set SKIP_TLS_VERIFY=true for self-signed certs in lab environments
|
|
return {"status": resp.status_code, "deployed": resp.status_code in (200, 201)}
|
|
except requests.RequestException as e:
|
|
return {"error": str(e)}
|
|
|
|
|
|
def generate_report(rules, deployment_results=None):
|
|
"""Generate detection rule report."""
|
|
report = {
|
|
"timestamp": datetime.utcnow().isoformat(),
|
|
"rules_generated": len(rules),
|
|
"rules": rules,
|
|
"deployment_results": deployment_results or [],
|
|
}
|
|
print(f"SPL REPORT: {len(rules)} detection rules generated")
|
|
return report
|
|
|
|
|
|
def main():
|
|
parser = argparse.ArgumentParser(description="Splunk SPL Detection Rule Builder Agent")
|
|
parser.add_argument("--templates", nargs="*", choices=list(DETECTION_TEMPLATES.keys()), default=list(DETECTION_TEMPLATES.keys()))
|
|
parser.add_argument("--threshold", type=int, default=10)
|
|
parser.add_argument("--threshold-bytes", type=int, default=104857600)
|
|
parser.add_argument("--splunk-url", help="Splunk URL for deployment")
|
|
parser.add_argument("--splunk-token", help="Splunk auth token")
|
|
parser.add_argument("--output", default="splunk_rules.json")
|
|
args = parser.parse_args()
|
|
|
|
rules = []
|
|
for template in args.templates:
|
|
rule = generate_spl_rule(template, {"threshold": args.threshold, "threshold_bytes": args.threshold_bytes})
|
|
rules.append(rule)
|
|
|
|
deployments = []
|
|
if args.splunk_url and args.splunk_token:
|
|
for rule in rules:
|
|
result = deploy_saved_search(args.splunk_url, args.splunk_token, rule["name"], rule["spl"], rule["severity"])
|
|
deployments.append({"rule": rule["name"], "result": result})
|
|
|
|
report = generate_report(rules, deployments)
|
|
with open(args.output, "w") as f:
|
|
json.dump(report, f, indent=2)
|
|
logger.info("Report saved to %s", args.output)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|