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

315 lines
12 KiB
Python

#!/usr/bin/env python3
"""Ransomware encryption behavior detection agent.
Detects ransomware activity using entropy analysis, file system I/O monitoring,
and behavioral heuristics. Monitors file modifications for entropy spikes and
mass rename patterns characteristic of ransomware encryption.
"""
import hashlib
import json
import logging
import math
import os
import sys
import time
from collections import Counter
logging.basicConfig(
level=logging.INFO,
format="%(asctime)s [%(levelname)s] %(message)s",
)
logger = logging.getLogger("ransomware_detector")
RANSOMWARE_EXTENSIONS = {
".locked", ".encrypted", ".crypt", ".locky", ".cerber", ".wncry",
".dharma", ".basta", ".blackcat", ".hive", ".royal", ".akira",
".lockbit", ".conti", ".ryuk", ".maze", ".revil", ".phobos",
}
RANSOM_NOTE_NAMES = {
"readme.txt", "readme.html", "decrypt.txt", "decrypt.html",
"how_to_decrypt.txt", "restore_files.txt", "how_to_recover.txt",
}
HIGH_VALUE_EXTENSIONS = {
".docx", ".xlsx", ".pptx", ".pdf", ".doc", ".xls", ".ppt",
".csv", ".sql", ".mdb", ".accdb", ".bak", ".zip", ".7z",
".pst", ".ost", ".eml", ".jpg", ".png", ".dwg", ".vmdk",
}
def shannon_entropy(data):
"""Calculate Shannon entropy of byte data (0.0 to 8.0)."""
if not data:
return 0.0
freq = Counter(data)
length = len(data)
return -sum((c / length) * math.log2(c / length) for c in freq.values())
def analyze_file_entropy(filepath):
"""Analyze entropy of a file and its segments."""
with open(filepath, "rb") as f:
data = f.read()
if not data:
return {"overall": 0.0, "is_encrypted": False}
overall = shannon_entropy(data)
file_size = len(data)
chunk_size = min(4096, file_size // 4) if file_size > 16 else file_size
first_entropy = shannon_entropy(data[:chunk_size])
mid_entropy = shannon_entropy(data[file_size // 2: file_size // 2 + chunk_size])
last_entropy = shannon_entropy(data[-chunk_size:])
return {
"overall": round(overall, 4),
"first_chunk": round(first_entropy, 4),
"mid_chunk": round(mid_entropy, 4),
"last_chunk": round(last_entropy, 4),
"file_size": file_size,
"is_encrypted": overall > 7.5,
"is_partial_encryption": abs(first_entropy - mid_entropy) > 2.0,
}
def scan_directory_entropy(directory, extensions=None):
"""Scan directory for files with high entropy indicating encryption."""
results = {"total_files": 0, "encrypted_files": 0, "files": []}
for root, dirs, files in os.walk(directory):
for filename in files:
filepath = os.path.join(root, filename)
ext = os.path.splitext(filename)[1].lower()
if extensions and ext not in extensions:
continue
try:
analysis = analyze_file_entropy(filepath)
results["total_files"] += 1
if analysis["is_encrypted"]:
results["encrypted_files"] += 1
analysis["path"] = filepath
analysis["filename"] = filename
results["files"].append(analysis)
except (OSError, PermissionError):
continue
results["encryption_ratio"] = (
round(results["encrypted_files"] / results["total_files"], 4)
if results["total_files"] > 0 else 0
)
return results
def detect_ransomware_indicators(directory):
"""Detect multiple ransomware indicators in a directory tree."""
indicators = {
"ransomware_extensions": [],
"ransom_notes": [],
"high_entropy_files": [],
"renamed_files": [],
"score": 0,
}
for root, dirs, files in os.walk(directory):
for filename in files:
filepath = os.path.join(root, filename)
lower_name = filename.lower()
# Check for ransomware file extensions
ext = os.path.splitext(filename)[1].lower()
if ext in RANSOMWARE_EXTENSIONS:
indicators["ransomware_extensions"].append(filepath)
# Check for double extensions (report.docx.locked)
parts = filename.rsplit(".", 2)
if len(parts) >= 3:
original_ext = "." + parts[-2].lower()
appended_ext = "." + parts[-1].lower()
if original_ext in HIGH_VALUE_EXTENSIONS and appended_ext in RANSOMWARE_EXTENSIONS:
indicators["renamed_files"].append({
"path": filepath,
"original_ext": original_ext,
"ransomware_ext": appended_ext,
})
# Check for ransom notes
if lower_name in RANSOM_NOTE_NAMES:
indicators["ransom_notes"].append(filepath)
# Check entropy of high-value file types
if ext in HIGH_VALUE_EXTENSIONS:
try:
analysis = analyze_file_entropy(filepath)
if analysis["is_encrypted"]:
indicators["high_entropy_files"].append({
"path": filepath,
"entropy": analysis["overall"],
})
except (OSError, PermissionError):
continue
# Calculate ransomware score
score = 0
score += min(len(indicators["ransomware_extensions"]) * 5, 30)
score += min(len(indicators["ransom_notes"]) * 15, 30)
score += min(len(indicators["high_entropy_files"]) * 3, 20)
score += min(len(indicators["renamed_files"]) * 5, 20)
indicators["score"] = min(score, 100)
if indicators["score"] >= 75:
indicators["verdict"] = "CRITICAL - Active ransomware encryption detected"
elif indicators["score"] >= 50:
indicators["verdict"] = "HIGH - Strong ransomware indicators present"
elif indicators["score"] >= 25:
indicators["verdict"] = "MEDIUM - Suspicious activity, investigate further"
else:
indicators["verdict"] = "LOW - No significant ransomware indicators"
return indicators
def snapshot_directory_state(directory):
"""Take a baseline snapshot of directory for differential analysis."""
snapshot = {}
for root, dirs, files in os.walk(directory):
for filename in files:
filepath = os.path.join(root, filename)
try:
stat = os.stat(filepath)
sha256 = hashlib.sha256()
with open(filepath, "rb") as f:
for chunk in iter(lambda: f.read(65536), b""):
sha256.update(chunk)
snapshot[filepath] = {
"hash": sha256.hexdigest(),
"size": stat.st_size,
"mtime": stat.st_mtime,
}
except (OSError, PermissionError):
continue
return snapshot
def compare_snapshots(before, after):
"""Compare two directory snapshots to detect bulk encryption."""
changes = {"modified": [], "deleted": [], "created": [], "total_changes": 0}
for path, info in before.items():
if path not in after:
changes["deleted"].append(path)
elif after[path]["hash"] != info["hash"]:
changes["modified"].append({
"path": path,
"size_before": info["size"],
"size_after": after[path]["size"],
})
for path in after:
if path not in before:
changes["created"].append(path)
changes["total_changes"] = (
len(changes["modified"]) + len(changes["deleted"]) + len(changes["created"])
)
if changes["total_changes"] > 0:
mod_ratio = len(changes["modified"]) / max(len(before), 1)
changes["bulk_modification_ratio"] = round(mod_ratio, 4)
changes["ransomware_likely"] = mod_ratio > 0.3 and len(changes["modified"]) > 10
else:
changes["bulk_modification_ratio"] = 0
changes["ransomware_likely"] = False
return changes
if __name__ == "__main__":
print("=" * 60)
print("Ransomware Encryption Behavior Detection Agent")
print("Entropy analysis, I/O monitoring, behavioral heuristics")
print("=" * 60)
if len(sys.argv) < 2:
print("\nUsage:")
print(" python agent.py scan <directory> Scan for ransomware indicators")
print(" python agent.py entropy <directory> Entropy scan of files")
print(" python agent.py entropy-file <file> Analyze single file entropy")
print(" python agent.py snapshot <directory> Take baseline snapshot")
print(" python agent.py compare <snap1> <snap2> Compare two snapshots")
sys.exit(0)
command = sys.argv[1]
if command == "scan":
target = sys.argv[2] if len(sys.argv) > 2 else os.getcwd()
print(f"\n[*] Scanning {target} for ransomware indicators...")
results = detect_ransomware_indicators(target)
print(f"\n--- Ransomware Detection Results ---")
print(f" Score: {results['score']}/100")
print(f" Verdict: {results['verdict']}")
print(f" Ransomware extensions found: {len(results['ransomware_extensions'])}")
print(f" Ransom notes found: {len(results['ransom_notes'])}")
print(f" High-entropy files: {len(results['high_entropy_files'])}")
print(f" Renamed files: {len(results['renamed_files'])}")
print(f"\n{json.dumps(results, indent=2, default=str)}")
elif command == "entropy":
target = sys.argv[2] if len(sys.argv) > 2 else os.getcwd()
print(f"\n[*] Entropy scanning {target}...")
results = scan_directory_entropy(target, HIGH_VALUE_EXTENSIONS)
print(f"\n--- Entropy Scan Results ---")
print(f" Total files scanned: {results['total_files']}")
print(f" Files with encrypted entropy: {results['encrypted_files']}")
print(f" Encryption ratio: {results['encryption_ratio']}")
for f in results["files"][:10]:
print(f" [!] {f['filename']}: entropy={f['overall']}")
elif command == "entropy-file":
if len(sys.argv) < 3:
print("[!] Provide a file path")
sys.exit(1)
filepath = sys.argv[2]
analysis = analyze_file_entropy(filepath)
print(f"\n--- File Entropy Analysis ---")
print(f" File: {filepath}")
print(f" Overall entropy: {analysis['overall']}")
print(f" First chunk: {analysis['first_chunk']}")
print(f" Mid chunk: {analysis['mid_chunk']}")
print(f" Last chunk: {analysis['last_chunk']}")
print(f" Encrypted: {analysis['is_encrypted']}")
print(f" Partial encryption: {analysis['is_partial_encryption']}")
elif command == "snapshot":
target = sys.argv[2] if len(sys.argv) > 2 else os.getcwd()
print(f"\n[*] Taking snapshot of {target}...")
snap = snapshot_directory_state(target)
output = f"snapshot_{int(time.time())}.json"
with open(output, "w") as f:
json.dump(snap, f, indent=2)
print(f"[+] Snapshot saved: {output} ({len(snap)} files)")
elif command == "compare":
if len(sys.argv) < 4:
print("[!] Provide two snapshot JSON files")
sys.exit(1)
with open(sys.argv[2]) as f:
snap1 = json.load(f)
with open(sys.argv[3]) as f:
snap2 = json.load(f)
changes = compare_snapshots(snap1, snap2)
print(f"\n--- Snapshot Comparison ---")
print(f" Modified: {len(changes['modified'])}")
print(f" Deleted: {len(changes['deleted'])}")
print(f" Created: {len(changes['created'])}")
print(f" Ransomware likely: {changes['ransomware_likely']}")
print(f"\n{json.dumps(changes, indent=2, default=str)}")
else:
print(f"[!] Unknown command: {command}")