mirror of
https://github.com/mukul975/Anthropic-Cybersecurity-Skills.git
synced 2026-06-16 16:03:17 +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
191 lines
7.2 KiB
Python
191 lines
7.2 KiB
Python
#!/usr/bin/env python3
|
|
"""Agent for detecting ransomware precursor activity in network traffic and logs."""
|
|
|
|
import argparse
|
|
import json
|
|
import os
|
|
import subprocess
|
|
import sys
|
|
from datetime import datetime, timezone
|
|
|
|
|
|
RANSOMWARE_PORTS = {445, 3389, 4444, 5985, 5986, 135, 139, 8443}
|
|
SUSPICIOUS_PROCESSES = [
|
|
"vssadmin.exe", "wmic.exe", "bcdedit.exe", "wbadmin.exe",
|
|
"powershell.exe", "cmd.exe", "certutil.exe", "bitsadmin.exe",
|
|
"mshta.exe", "rundll32.exe", "regsvr32.exe", "cscript.exe",
|
|
]
|
|
SHADOW_COPY_PATTERNS = [
|
|
r"vssadmin\s+delete\s+shadows",
|
|
r"wmic\s+shadowcopy\s+delete",
|
|
r"bcdedit.*recoveryenabled.*no",
|
|
r"wbadmin\s+delete\s+(catalog|systemstatebackup)",
|
|
]
|
|
SMB_LATERAL_PATTERNS = [
|
|
r"\\\\[\d\.]+\\(C\$|ADMIN\$|IPC\$)",
|
|
r"psexec",
|
|
r"wmiexec",
|
|
]
|
|
|
|
|
|
def parse_zeek_conn_log(log_path):
|
|
"""Parse Zeek conn.log for suspicious network connections."""
|
|
alerts = []
|
|
try:
|
|
with open(log_path, "r") as f:
|
|
for line in f:
|
|
if line.startswith("#"):
|
|
continue
|
|
fields = line.strip().split("\t")
|
|
if len(fields) < 7:
|
|
continue
|
|
src_ip, src_port, dst_ip, dst_port = fields[2], fields[3], fields[4], fields[5]
|
|
try:
|
|
dp = int(dst_port)
|
|
except ValueError:
|
|
continue
|
|
if dp in RANSOMWARE_PORTS:
|
|
alerts.append({
|
|
"type": "suspicious_port",
|
|
"src": src_ip,
|
|
"dst": dst_ip,
|
|
"port": dp,
|
|
"detail": f"Connection to ransomware-associated port {dp}",
|
|
})
|
|
except FileNotFoundError:
|
|
print(f"[!] Log file not found: {log_path}")
|
|
return alerts
|
|
|
|
|
|
def analyze_event_logs_windows():
|
|
"""Check Windows event logs for ransomware precursors."""
|
|
alerts = []
|
|
queries = [
|
|
("Shadow copy deletion", "Get-WinEvent -FilterHashtable @{LogName='System';Id=7036} "
|
|
"| Where-Object {$_.Message -match 'Volume Shadow Copy'} | Select-Object -First 10 "
|
|
"| ConvertTo-Json"),
|
|
("RDP brute force", "Get-WinEvent -FilterHashtable @{LogName='Security';Id=4625} "
|
|
"| Select-Object -First 20 | Group-Object {$_.Properties[5].Value} "
|
|
"| Where-Object {$_.Count -gt 5} | ConvertTo-Json"),
|
|
("Service installs", "Get-WinEvent -FilterHashtable @{LogName='System';Id=7045} "
|
|
"| Select-Object -First 10 | ConvertTo-Json"),
|
|
]
|
|
for name, ps_cmd in queries:
|
|
try:
|
|
result = subprocess.check_output(
|
|
["powershell", "-NoProfile", "-Command", ps_cmd],
|
|
text=True, errors="replace", timeout=30
|
|
)
|
|
if result.strip():
|
|
data = json.loads(result) if result.strip().startswith(("[", "{")) else result
|
|
alerts.append({"check": name, "findings": data})
|
|
except (subprocess.SubprocessError, json.JSONDecodeError):
|
|
pass
|
|
return alerts
|
|
|
|
|
|
def scan_process_list():
|
|
"""Check running processes for ransomware tooling."""
|
|
suspicious = []
|
|
if sys.platform == "win32":
|
|
try:
|
|
out = subprocess.check_output(
|
|
["tasklist", "/FO", "CSV", "/NH"], text=True, errors="replace",
|
|
timeout=120,
|
|
)
|
|
for line in out.splitlines():
|
|
parts = line.strip('"').split('","')
|
|
if parts:
|
|
pname = parts[0].lower()
|
|
for sp in SUSPICIOUS_PROCESSES:
|
|
if pname == sp.lower():
|
|
suspicious.append({"process": pname, "pid": parts[1] if len(parts) > 1 else "?"})
|
|
except subprocess.SubprocessError:
|
|
pass
|
|
else:
|
|
try:
|
|
out = subprocess.check_output(["ps", "-eo", "pid,comm", "--no-headers"], text=True, timeout=120)
|
|
for line in out.splitlines():
|
|
parts = line.split(None, 1)
|
|
if len(parts) == 2:
|
|
for sp in SUSPICIOUS_PROCESSES:
|
|
if parts[1].strip().lower() == sp.replace(".exe", ""):
|
|
suspicious.append({"process": parts[1].strip(), "pid": parts[0]})
|
|
except subprocess.SubprocessError:
|
|
pass
|
|
return suspicious
|
|
|
|
|
|
def check_file_encryption_activity(directory, threshold=50):
|
|
"""Detect mass file renaming or new encrypted extensions."""
|
|
suspicious_exts = {".encrypted", ".locked", ".crypto", ".crypt", ".enc",
|
|
".locky", ".cerber", ".zepto", ".thor", ".aaa"}
|
|
findings = []
|
|
count = 0
|
|
try:
|
|
for root, _, files in os.walk(directory):
|
|
for f in files:
|
|
ext = os.path.splitext(f)[1].lower()
|
|
if ext in suspicious_exts:
|
|
count += 1
|
|
if count <= 10:
|
|
findings.append(os.path.join(root, f))
|
|
if count >= threshold:
|
|
break
|
|
except PermissionError:
|
|
pass
|
|
return {"encrypted_file_count": count, "samples": findings, "threshold_exceeded": count >= threshold}
|
|
|
|
|
|
def main():
|
|
parser = argparse.ArgumentParser(
|
|
description="Detect ransomware precursor activity in network and host"
|
|
)
|
|
parser.add_argument("--conn-log", help="Path to Zeek conn.log")
|
|
parser.add_argument("--scan-dir", help="Directory to scan for encrypted files")
|
|
parser.add_argument("--check-processes", action="store_true", help="Scan running processes")
|
|
parser.add_argument("--windows-logs", action="store_true", help="Check Windows event logs")
|
|
parser.add_argument("--output", "-o", help="Output JSON report path")
|
|
args = parser.parse_args()
|
|
|
|
print("[*] Ransomware Precursor Detection Agent")
|
|
report = {"timestamp": datetime.now(timezone.utc).isoformat(), "findings": {}}
|
|
|
|
if args.conn_log:
|
|
alerts = parse_zeek_conn_log(args.conn_log)
|
|
report["findings"]["network"] = alerts
|
|
print(f"[*] Network alerts: {len(alerts)}")
|
|
|
|
if args.check_processes:
|
|
procs = scan_process_list()
|
|
report["findings"]["suspicious_processes"] = procs
|
|
print(f"[*] Suspicious processes: {len(procs)}")
|
|
|
|
if args.windows_logs and sys.platform == "win32":
|
|
events = analyze_event_logs_windows()
|
|
report["findings"]["windows_events"] = events
|
|
print(f"[*] Windows event findings: {len(events)}")
|
|
|
|
if args.scan_dir:
|
|
enc = check_file_encryption_activity(args.scan_dir)
|
|
report["findings"]["encryption_activity"] = enc
|
|
print(f"[*] Encrypted files found: {enc['encrypted_file_count']}")
|
|
|
|
total = sum(
|
|
len(v) if isinstance(v, list) else (1 if isinstance(v, dict) and v.get("threshold_exceeded") else 0)
|
|
for v in report["findings"].values()
|
|
)
|
|
report["risk_level"] = "CRITICAL" if total >= 10 else "HIGH" if total >= 5 else "MEDIUM" if total > 0 else "LOW"
|
|
print(f"[*] Overall risk: {report['risk_level']}")
|
|
|
|
if args.output:
|
|
with open(args.output, "w") as f:
|
|
json.dump(report, f, indent=2)
|
|
print(f"[*] Report saved to {args.output}")
|
|
else:
|
|
print(json.dumps(report, indent=2))
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|