#!/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 Scan for ransomware indicators") print(" python agent.py entropy Entropy scan of files") print(" python agent.py entropy-file Analyze single file entropy") print(" python agent.py snapshot Take baseline snapshot") print(" python agent.py compare 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}")