Files
T
mukul975 c47eed6a64 Production hardening: security fixes, code quality, 724 skills complete
- 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
2026-03-19 13:26:49 +01:00

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()