mirror of
https://github.com/mukul975/Anthropic-Cybersecurity-Skills.git
synced 2026-07-19 14:09:40 +03:00
Add folder anatomy (scripts/agent.py + references/api-reference.md) for 648 cybersecurity skills
Complete skill folder anatomy across all cybersecurity skills: - scripts/agent.py: 80-150 line Python agents using real libraries (impacket, boto3, azure-mgmt-*, kubernetes, pefile, yara, scapy, shodan, stix2, etc.) - references/api-reference.md: real API documentation with method signatures - LICENSE: MIT license for all skill folders
This commit is contained in:
@@ -0,0 +1,354 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Ransomware encryption mechanism analysis agent.
|
||||
|
||||
Analyzes encryption algorithms, key management, file encryption routines,
|
||||
and assesses decryption feasibility for ransomware samples and encrypted files.
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
import struct
|
||||
import hashlib
|
||||
import math
|
||||
import json
|
||||
import re
|
||||
from collections import Counter
|
||||
|
||||
|
||||
def compute_hash(filepath):
|
||||
"""Compute SHA-256 hash of a file."""
|
||||
sha256 = hashlib.sha256()
|
||||
with open(filepath, "rb") as f:
|
||||
for chunk in iter(lambda: f.read(65536), b""):
|
||||
sha256.update(chunk)
|
||||
return sha256.hexdigest()
|
||||
|
||||
|
||||
def shannon_entropy(data):
|
||||
"""Calculate Shannon entropy of byte data."""
|
||||
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())
|
||||
|
||||
|
||||
CRYPTO_CONSTANTS = {
|
||||
bytes.fromhex("637c777bf26b6fc53001672bfed7ab76"): "AES S-Box (Rijndael)",
|
||||
bytes.fromhex("52096ad53036a538bf40a39e81f3d7fb"): "AES S-Box (continued)",
|
||||
bytes.fromhex("6a09e667bb67ae853c6ef372a54ff53a"): "SHA-256 initialization vector",
|
||||
b"expand 32-byte k": "ChaCha20/Salsa20 constant (256-bit key)",
|
||||
b"expand 16-byte k": "ChaCha20/Salsa20 constant (128-bit key)",
|
||||
bytes.fromhex("d1310ba698dfb5ac"): "Blowfish P-array fragment",
|
||||
}
|
||||
|
||||
CRYPTO_API_NAMES = [
|
||||
b"CryptAcquireContext", b"CryptGenKey", b"CryptEncrypt", b"CryptDecrypt",
|
||||
b"CryptImportKey", b"CryptExportKey", b"CryptGenRandom", b"CryptDeriveKey",
|
||||
b"BCryptOpenAlgorithmProvider", b"BCryptEncrypt", b"BCryptGenerateKeyPair",
|
||||
b"BCryptGenerateSymmetricKey", b"BCryptCreateHash",
|
||||
b"RtlEncryptMemory", b"RtlDecryptMemory",
|
||||
]
|
||||
|
||||
RANSOMWARE_EXTENSIONS = {
|
||||
".locked": ["LockBit", "Generic"],
|
||||
".encrypt": ["Generic"],
|
||||
".crypt": ["CryptXXX", "Generic"],
|
||||
".locky": ["Locky"],
|
||||
".cerber": ["Cerber"],
|
||||
".zepto": ["Locky variant"],
|
||||
".odin": ["Locky variant"],
|
||||
".aesir": ["Locky variant"],
|
||||
".wncry": ["WannaCry"],
|
||||
".WNCRY": ["WannaCry"],
|
||||
".wnry": ["WannaCry"],
|
||||
".wcry": ["WannaCry"],
|
||||
".dharma": ["Dharma/CrySiS"],
|
||||
".basta": ["Black Basta"],
|
||||
".blackcat": ["BlackCat/ALPHV"],
|
||||
".hive": ["Hive"],
|
||||
".royal": ["Royal"],
|
||||
".rhysida": ["Rhysida"],
|
||||
".akira": ["Akira"],
|
||||
".lockbit": ["LockBit 3.0"],
|
||||
".conti": ["Conti"],
|
||||
".ryuk": ["Ryuk"],
|
||||
".maze": ["Maze"],
|
||||
".revil": ["REvil/Sodinokibi"],
|
||||
".sodinokibi": ["REvil/Sodinokibi"],
|
||||
".phobos": ["Phobos"],
|
||||
".makop": ["Makop"],
|
||||
".stop": ["STOP/Djvu"],
|
||||
".djvu": ["STOP/Djvu"],
|
||||
}
|
||||
|
||||
|
||||
def identify_ransomware_extension(filepath):
|
||||
"""Identify ransomware family from file extension."""
|
||||
ext = os.path.splitext(filepath)[1].lower()
|
||||
if ext in RANSOMWARE_EXTENSIONS:
|
||||
return {"extension": ext, "families": RANSOMWARE_EXTENSIONS[ext]}
|
||||
for known_ext, families in RANSOMWARE_EXTENSIONS.items():
|
||||
if ext.endswith(known_ext):
|
||||
return {"extension": ext, "families": families}
|
||||
return {"extension": ext, "families": ["Unknown"]}
|
||||
|
||||
|
||||
def scan_crypto_constants(filepath):
|
||||
"""Scan binary for known cryptographic constants."""
|
||||
with open(filepath, "rb") as f:
|
||||
data = f.read()
|
||||
findings = []
|
||||
for const_bytes, description in CRYPTO_CONSTANTS.items():
|
||||
offset = data.find(const_bytes)
|
||||
if offset != -1:
|
||||
findings.append({
|
||||
"constant": description,
|
||||
"offset": f"0x{offset:08X}",
|
||||
"hex": const_bytes[:16].hex(),
|
||||
})
|
||||
return findings
|
||||
|
||||
|
||||
def scan_crypto_apis(filepath):
|
||||
"""Scan binary for Windows Crypto API string references."""
|
||||
with open(filepath, "rb") as f:
|
||||
data = f.read()
|
||||
found = []
|
||||
for api in CRYPTO_API_NAMES:
|
||||
if api in data:
|
||||
found.append(api.decode("ascii", errors="replace"))
|
||||
return found
|
||||
|
||||
|
||||
def analyze_encrypted_file(filepath):
|
||||
"""Analyze an encrypted file for ransomware characteristics."""
|
||||
with open(filepath, "rb") as f:
|
||||
data = f.read()
|
||||
|
||||
file_size = len(data)
|
||||
entropy = shannon_entropy(data)
|
||||
|
||||
# Check for appended metadata (many ransomware families append key material)
|
||||
tail_256 = data[-256:] if file_size >= 256 else data
|
||||
tail_entropy = shannon_entropy(tail_256)
|
||||
|
||||
# Check for ECB mode (duplicate 16-byte blocks)
|
||||
blocks_16 = [data[i:i+16] for i in range(0, min(len(data), 65536), 16)]
|
||||
unique_16 = len(set(blocks_16))
|
||||
total_16 = len(blocks_16)
|
||||
ecb_ratio = 1.0 - (unique_16 / total_16) if total_16 > 0 else 0
|
||||
|
||||
# Check for partial encryption (low entropy regions)
|
||||
chunk_size = min(4096, file_size // 4) if file_size > 16 else file_size
|
||||
first_entropy = shannon_entropy(data[:chunk_size]) if chunk_size > 0 else 0
|
||||
mid_offset = file_size // 2
|
||||
mid_entropy = shannon_entropy(data[mid_offset:mid_offset+chunk_size]) if chunk_size > 0 else 0
|
||||
last_entropy = shannon_entropy(data[-chunk_size:]) if chunk_size > 0 else 0
|
||||
|
||||
# Detect magic bytes at tail (ransomware markers)
|
||||
tail_8 = data[-8:] if file_size >= 8 else data
|
||||
tail_marker = tail_8.hex() if all(b > 0 for b in tail_8) else None
|
||||
|
||||
return {
|
||||
"file_size": file_size,
|
||||
"overall_entropy": round(entropy, 4),
|
||||
"tail_256_entropy": round(tail_entropy, 4),
|
||||
"ecb_duplicate_ratio": round(ecb_ratio, 4),
|
||||
"ecb_likely": ecb_ratio > 0.05,
|
||||
"partial_encryption": {
|
||||
"first_chunk_entropy": round(first_entropy, 4),
|
||||
"mid_chunk_entropy": round(mid_entropy, 4),
|
||||
"last_chunk_entropy": round(last_entropy, 4),
|
||||
"likely_partial": abs(first_entropy - mid_entropy) > 2.0,
|
||||
},
|
||||
"tail_marker_hex": tail_marker,
|
||||
"fully_encrypted": entropy > 7.5,
|
||||
}
|
||||
|
||||
|
||||
def xor_key_recovery(encrypted_data, known_plaintext):
|
||||
"""Attempt XOR key recovery from known plaintext-ciphertext pair."""
|
||||
if len(known_plaintext) == 0:
|
||||
return None
|
||||
key_stream = bytes(c ^ p for c, p in zip(encrypted_data, known_plaintext))
|
||||
# Detect repeating key
|
||||
for key_len in range(1, min(256, len(key_stream) // 2)):
|
||||
candidate = key_stream[:key_len]
|
||||
match = all(
|
||||
key_stream[i] == candidate[i % key_len]
|
||||
for i in range(min(len(key_stream), key_len * 4))
|
||||
)
|
||||
if match and key_len < len(key_stream):
|
||||
return {"key_hex": candidate.hex(), "key_length": key_len, "key_ascii": candidate.decode("ascii", errors="replace")}
|
||||
return None
|
||||
|
||||
|
||||
def check_file_header_known_plaintext(encrypted_filepath):
|
||||
"""Check if encrypted file retains known file header (partial encryption indicator)."""
|
||||
KNOWN_HEADERS = {
|
||||
b"%PDF": "PDF document",
|
||||
b"PK\x03\x04": "ZIP/DOCX/XLSX archive",
|
||||
b"\x89PNG": "PNG image",
|
||||
b"\xff\xd8\xff": "JPEG image",
|
||||
b"MZ": "PE executable",
|
||||
b"\x7fELF": "ELF executable",
|
||||
b"Rar!": "RAR archive",
|
||||
b"\xd0\xcf\x11\xe0": "OLE2 (DOC/XLS)",
|
||||
b"SQLite format 3": "SQLite database",
|
||||
}
|
||||
with open(encrypted_filepath, "rb") as f:
|
||||
header = f.read(16)
|
||||
for magic, filetype in KNOWN_HEADERS.items():
|
||||
if header[:len(magic)] == magic:
|
||||
return {"detected": True, "original_type": filetype,
|
||||
"note": "File header intact - partial encryption or not encrypted"}
|
||||
return {"detected": False, "note": "No known file header found - likely fully encrypted from start"}
|
||||
|
||||
|
||||
def assess_decryption_feasibility(crypto_constants, crypto_apis, enc_analysis):
|
||||
"""Assess decryption feasibility based on analysis results."""
|
||||
weaknesses = []
|
||||
strong_points = []
|
||||
|
||||
if enc_analysis.get("ecb_likely"):
|
||||
weaknesses.append("ECB mode detected - block patterns preserved, partial plaintext recovery possible")
|
||||
if enc_analysis.get("partial_encryption", {}).get("likely_partial"):
|
||||
weaknesses.append("Partial encryption detected - unencrypted file regions may aid recovery")
|
||||
if enc_analysis.get("overall_entropy", 8) < 6.0:
|
||||
weaknesses.append("Low entropy suggests weak or partial encryption")
|
||||
|
||||
has_csprng = any("GenRandom" in api for api in crypto_apis)
|
||||
has_rsa = any("KeyPair" in api or "ImportKey" in api for api in crypto_apis)
|
||||
has_aes = any("AES" in c.get("constant", "") or "Rijndael" in c.get("constant", "") for c in crypto_constants)
|
||||
has_chacha = any("ChaCha" in c.get("constant", "") or "Salsa" in c.get("constant", "") for c in crypto_constants)
|
||||
|
||||
if has_csprng:
|
||||
strong_points.append("CSPRNG key generation (CryptGenRandom) - keys not predictable")
|
||||
if has_rsa:
|
||||
strong_points.append("RSA key wrapping - per-file keys protected by asymmetric encryption")
|
||||
if has_aes:
|
||||
strong_points.append("AES encryption identified")
|
||||
if has_chacha:
|
||||
strong_points.append("ChaCha20/Salsa20 stream cipher identified")
|
||||
|
||||
if not has_csprng:
|
||||
weaknesses.append("No CSPRNG detected - key generation may be predictable")
|
||||
|
||||
feasibility = "NOT POSSIBLE" if len(strong_points) >= 2 and len(weaknesses) == 0 else \
|
||||
"POSSIBLE" if len(weaknesses) >= 2 else \
|
||||
"UNLIKELY - check for specific implementation flaws"
|
||||
|
||||
return {
|
||||
"feasibility": feasibility,
|
||||
"weaknesses": weaknesses,
|
||||
"strong_points": strong_points,
|
||||
"recommendation": "Check NoMoreRansom.org and memory forensics" if feasibility != "NOT POSSIBLE"
|
||||
else "Restore from backups; no cryptographic weakness found",
|
||||
}
|
||||
|
||||
|
||||
def generate_report(sample_path=None, encrypted_path=None):
|
||||
"""Generate full ransomware encryption analysis report."""
|
||||
report = {"analysis_type": "Ransomware Encryption Mechanism Analysis"}
|
||||
|
||||
if sample_path and os.path.exists(sample_path):
|
||||
report["sample"] = {
|
||||
"path": sample_path,
|
||||
"sha256": compute_hash(sample_path),
|
||||
"size": os.path.getsize(sample_path),
|
||||
"entropy": round(shannon_entropy(open(sample_path, "rb").read()), 4),
|
||||
}
|
||||
report["crypto_constants"] = scan_crypto_constants(sample_path)
|
||||
report["crypto_apis"] = scan_crypto_apis(sample_path)
|
||||
|
||||
if encrypted_path and os.path.exists(encrypted_path):
|
||||
report["encrypted_file"] = {
|
||||
"path": encrypted_path,
|
||||
"sha256": compute_hash(encrypted_path),
|
||||
"family_match": identify_ransomware_extension(encrypted_path),
|
||||
}
|
||||
report["encryption_analysis"] = analyze_encrypted_file(encrypted_path)
|
||||
report["header_check"] = check_file_header_known_plaintext(encrypted_path)
|
||||
|
||||
if "crypto_constants" in report or "encryption_analysis" in report:
|
||||
report["feasibility"] = assess_decryption_feasibility(
|
||||
report.get("crypto_constants", []),
|
||||
report.get("crypto_apis", []),
|
||||
report.get("encryption_analysis", {}),
|
||||
)
|
||||
|
||||
return report
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
print("=" * 60)
|
||||
print("Ransomware Encryption Mechanism Analysis Agent")
|
||||
print("Algorithm identification, key analysis, decryption feasibility")
|
||||
print("=" * 60)
|
||||
|
||||
if len(sys.argv) < 2:
|
||||
print("\n[DEMO] Usage:")
|
||||
print(" python agent.py <ransomware_binary> # Analyze ransomware sample")
|
||||
print(" python agent.py <ransomware_binary> <encrypted_file> # Full analysis")
|
||||
print(" python agent.py --encrypted <encrypted_file> # Analyze encrypted file only")
|
||||
sys.exit(0)
|
||||
|
||||
sample = None
|
||||
encrypted = None
|
||||
|
||||
if sys.argv[1] == "--encrypted" and len(sys.argv) > 2:
|
||||
encrypted = sys.argv[2]
|
||||
else:
|
||||
sample = sys.argv[1]
|
||||
encrypted = sys.argv[2] if len(sys.argv) > 2 else None
|
||||
|
||||
report = generate_report(sample_path=sample, encrypted_path=encrypted)
|
||||
|
||||
if sample and os.path.exists(sample):
|
||||
info = report.get("sample", {})
|
||||
print(f"\n[*] Sample: {sample}")
|
||||
print(f" SHA-256: {info.get('sha256', 'N/A')}")
|
||||
print(f" Size: {info.get('size', 0)} bytes")
|
||||
print(f" Entropy: {info.get('entropy', 0)}")
|
||||
|
||||
print("\n--- Crypto Constants Found ---")
|
||||
for c in report.get("crypto_constants", []):
|
||||
print(f" [{c['offset']}] {c['constant']}")
|
||||
|
||||
print("\n--- Crypto API Imports ---")
|
||||
for api in report.get("crypto_apis", []):
|
||||
print(f" {api}")
|
||||
|
||||
if encrypted and os.path.exists(encrypted):
|
||||
fm = report.get("encrypted_file", {}).get("family_match", {})
|
||||
print(f"\n[*] Encrypted file: {encrypted}")
|
||||
print(f" Extension: {fm.get('extension', '?')}")
|
||||
print(f" Possible families: {', '.join(fm.get('families', ['Unknown']))}")
|
||||
|
||||
ea = report.get("encryption_analysis", {})
|
||||
print(f"\n--- Encryption Analysis ---")
|
||||
print(f" Overall entropy: {ea.get('overall_entropy', 0)}")
|
||||
print(f" Fully encrypted: {ea.get('fully_encrypted', False)}")
|
||||
print(f" ECB mode likely: {ea.get('ecb_likely', False)}")
|
||||
partial = ea.get("partial_encryption", {})
|
||||
print(f" Partial encryption: {partial.get('likely_partial', False)}")
|
||||
|
||||
hc = report.get("header_check", {})
|
||||
print(f"\n--- Header Check ---")
|
||||
print(f" Known header: {hc.get('detected', False)}")
|
||||
print(f" Note: {hc.get('note', '')}")
|
||||
|
||||
if "feasibility" in report:
|
||||
f = report["feasibility"]
|
||||
print(f"\n--- Decryption Feasibility ---")
|
||||
print(f" Assessment: {f['feasibility']}")
|
||||
print(f" Weaknesses:")
|
||||
for w in f.get("weaknesses", []):
|
||||
print(f" [!] {w}")
|
||||
print(f" Strong points:")
|
||||
for s in f.get("strong_points", []):
|
||||
print(f" [+] {s}")
|
||||
print(f" Recommendation: {f['recommendation']}")
|
||||
|
||||
print(f"\n[*] Full report:\n{json.dumps(report, indent=2, default=str)}")
|
||||
Reference in New Issue
Block a user