mirror of
https://github.com/mukul975/Anthropic-Cybersecurity-Skills.git
synced 2026-07-05 23:38:57 +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,219 @@
|
||||
#!/usr/bin/env python3
|
||||
""".NET malware reverse engineering agent using subprocess wrappers for dnSpy/de4dot."""
|
||||
|
||||
import subprocess
|
||||
import os
|
||||
import sys
|
||||
import re
|
||||
import json
|
||||
import hashlib
|
||||
import struct
|
||||
|
||||
|
||||
def compute_hashes(filepath):
|
||||
"""Compute hashes for sample identification."""
|
||||
with open(filepath, "rb") as f:
|
||||
data = f.read()
|
||||
return {
|
||||
"md5": hashlib.md5(data).hexdigest(),
|
||||
"sha256": hashlib.sha256(data).hexdigest(),
|
||||
"size": len(data),
|
||||
}
|
||||
|
||||
|
||||
def detect_dotnet_assembly(filepath):
|
||||
"""Check if file is a .NET assembly by looking for CLI header."""
|
||||
with open(filepath, "rb") as f:
|
||||
data = f.read(512)
|
||||
if data[:2] != b"MZ":
|
||||
return {"is_dotnet": False, "reason": "Not a PE file"}
|
||||
try:
|
||||
pe_offset = struct.unpack_from("<I", data, 0x3C)[0]
|
||||
if pe_offset + 4 > len(data):
|
||||
return {"is_dotnet": False, "reason": "Invalid PE header"}
|
||||
if data[pe_offset:pe_offset + 4] != b"PE\x00\x00":
|
||||
return {"is_dotnet": False, "reason": "Invalid PE signature"}
|
||||
opt_offset = pe_offset + 24
|
||||
magic = struct.unpack_from("<H", data, opt_offset)[0]
|
||||
if magic == 0x10B:
|
||||
clr_offset = opt_offset + 208
|
||||
elif magic == 0x20B:
|
||||
clr_offset = opt_offset + 224
|
||||
else:
|
||||
return {"is_dotnet": False, "reason": "Unknown PE format"}
|
||||
if clr_offset + 8 <= len(data):
|
||||
clr_rva, clr_size = struct.unpack_from("<II", data, clr_offset)
|
||||
if clr_rva > 0 and clr_size > 0:
|
||||
return {"is_dotnet": True, "clr_header_rva": clr_rva, "clr_size": clr_size}
|
||||
return {"is_dotnet": False, "reason": "No CLR header"}
|
||||
except (struct.error, IndexError):
|
||||
return {"is_dotnet": False, "reason": "Parse error"}
|
||||
|
||||
|
||||
def detect_obfuscator(filepath):
|
||||
"""Detect .NET obfuscator using Detect It Easy."""
|
||||
try:
|
||||
result = subprocess.run(
|
||||
["diec", filepath], capture_output=True, text=True, timeout=30
|
||||
)
|
||||
output = result.stdout
|
||||
obfuscators = {
|
||||
"ConfuserEx": "confuser" in output.lower(),
|
||||
"SmartAssembly": "smartassembly" in output.lower(),
|
||||
".NET Reactor": "reactor" in output.lower(),
|
||||
"Dotfuscator": "dotfuscator" in output.lower(),
|
||||
"Babel": "babel" in output.lower(),
|
||||
"Eazfuscator": "eazfuscator" in output.lower(),
|
||||
"Crypto Obfuscator": "crypto" in output.lower() and "obfuscator" in output.lower(),
|
||||
}
|
||||
detected = [name for name, found in obfuscators.items() if found]
|
||||
return {"detected": detected, "raw_output": output.strip()}
|
||||
except FileNotFoundError:
|
||||
return {"detected": [], "raw_output": "diec not installed"}
|
||||
|
||||
|
||||
def deobfuscate_with_de4dot(filepath, output_path):
|
||||
"""Run de4dot to deobfuscate .NET assembly."""
|
||||
try:
|
||||
result = subprocess.run(
|
||||
["de4dot", filepath, "-o", output_path],
|
||||
capture_output=True, text=True, timeout=120
|
||||
)
|
||||
return {
|
||||
"success": result.returncode == 0,
|
||||
"output_path": output_path,
|
||||
"stdout": result.stdout[-500:] if result.stdout else "",
|
||||
}
|
||||
except FileNotFoundError:
|
||||
return {"success": False, "error": "de4dot not installed"}
|
||||
|
||||
|
||||
def extract_strings(filepath, min_length=8):
|
||||
"""Extract strings and classify for IOCs."""
|
||||
with open(filepath, "rb") as f:
|
||||
data = f.read()
|
||||
unicode_strings = re.findall(
|
||||
rb"(?:[\x20-\x7e]\x00){%d,}" % min_length, data
|
||||
)
|
||||
ascii_strings = re.findall(
|
||||
rb"[\x20-\x7e]{%d,}" % min_length, data
|
||||
)
|
||||
all_strings = set()
|
||||
for s in ascii_strings:
|
||||
all_strings.add(s.decode("ascii", errors="ignore"))
|
||||
for s in unicode_strings:
|
||||
all_strings.add(s.decode("utf-16-le", errors="ignore"))
|
||||
indicators = {
|
||||
"urls": [], "ips": [], "emails": [],
|
||||
"registry_keys": [], "file_paths": [],
|
||||
"base64_strings": [], "suspicious_strings": [],
|
||||
}
|
||||
suspicious_keywords = [
|
||||
"keylog", "screenshot", "clipboard", "password", "credential",
|
||||
"smtp", "telegram", "discord", "webhook", "stealer",
|
||||
"inject", "hook", "persist", "startup",
|
||||
]
|
||||
for s in all_strings:
|
||||
if re.search(r"https?://", s):
|
||||
indicators["urls"].append(s)
|
||||
if re.search(r"\b(\d{1,3}\.){3}\d{1,3}\b", s):
|
||||
indicators["ips"].append(s)
|
||||
if re.search(r"[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}", s):
|
||||
indicators["emails"].append(s)
|
||||
if re.search(r"HKLM|HKCU|SOFTWARE\\", s, re.IGNORECASE):
|
||||
indicators["registry_keys"].append(s)
|
||||
if re.search(r"[A-Za-z0-9+/]{40,}={0,2}$", s):
|
||||
indicators["base64_strings"].append(s[:100])
|
||||
for kw in suspicious_keywords:
|
||||
if kw in s.lower():
|
||||
indicators["suspicious_strings"].append(s[:100])
|
||||
break
|
||||
for key in indicators:
|
||||
indicators[key] = list(set(indicators[key]))[:20]
|
||||
return indicators
|
||||
|
||||
|
||||
def analyze_dotnet_metadata(filepath):
|
||||
"""Extract .NET metadata using monodis or ilspy CLI if available."""
|
||||
metadata = {}
|
||||
try:
|
||||
result = subprocess.run(
|
||||
["monodis", "--assembly", filepath],
|
||||
capture_output=True, text=True, timeout=15
|
||||
)
|
||||
if result.returncode == 0:
|
||||
metadata["assembly_info"] = result.stdout.strip()
|
||||
except FileNotFoundError:
|
||||
pass
|
||||
try:
|
||||
result = subprocess.run(
|
||||
["monodis", "--typedef", filepath],
|
||||
capture_output=True, text=True, timeout=15
|
||||
)
|
||||
if result.returncode == 0:
|
||||
types = re.findall(r"(\S+)\s+flags", result.stdout)
|
||||
metadata["type_count"] = len(types)
|
||||
metadata["types"] = types[:30]
|
||||
except FileNotFoundError:
|
||||
pass
|
||||
try:
|
||||
result = subprocess.run(
|
||||
["monodis", "--method", filepath],
|
||||
capture_output=True, text=True, timeout=15
|
||||
)
|
||||
if result.returncode == 0:
|
||||
methods = re.findall(r"(\S+)\s+\(", result.stdout)
|
||||
metadata["method_count"] = len(methods)
|
||||
except FileNotFoundError:
|
||||
pass
|
||||
return metadata
|
||||
|
||||
|
||||
def analyze_dotnet_malware(filepath, output_dir="/tmp/dotnet_analysis"):
|
||||
"""Full .NET malware analysis pipeline."""
|
||||
os.makedirs(output_dir, exist_ok=True)
|
||||
report = {"file": filepath}
|
||||
report["hashes"] = compute_hashes(filepath)
|
||||
report["dotnet_check"] = detect_dotnet_assembly(filepath)
|
||||
if not report["dotnet_check"].get("is_dotnet"):
|
||||
report["error"] = "Not a .NET assembly"
|
||||
return report
|
||||
report["obfuscator"] = detect_obfuscator(filepath)
|
||||
deobf_path = os.path.join(output_dir, "deobfuscated.exe")
|
||||
report["deobfuscation"] = deobfuscate_with_de4dot(filepath, deobf_path)
|
||||
analysis_target = deobf_path if report["deobfuscation"].get("success") else filepath
|
||||
report["strings"] = extract_strings(analysis_target)
|
||||
report["metadata"] = analyze_dotnet_metadata(analysis_target)
|
||||
return report
|
||||
|
||||
|
||||
def print_report(report):
|
||||
print(".NET Malware Analysis Report")
|
||||
print("=" * 50)
|
||||
print(f"File: {report['file']}")
|
||||
print(f"SHA-256: {report['hashes']['sha256']}")
|
||||
print(f".NET Assembly: {report['dotnet_check'].get('is_dotnet', False)}")
|
||||
obf = report.get("obfuscator", {})
|
||||
if obf.get("detected"):
|
||||
print(f"Obfuscator: {', '.join(obf['detected'])}")
|
||||
deobf = report.get("deobfuscation", {})
|
||||
print(f"Deobfuscation: {'Success' if deobf.get('success') else 'Failed/Skipped'}")
|
||||
meta = report.get("metadata", {})
|
||||
if meta:
|
||||
print(f"Types: {meta.get('type_count', 'N/A')}, Methods: {meta.get('method_count', 'N/A')}")
|
||||
strings = report.get("strings", {})
|
||||
if strings:
|
||||
print("\nExtracted Indicators:")
|
||||
for cat, values in strings.items():
|
||||
if values:
|
||||
print(f" {cat}: {len(values)}")
|
||||
for v in values[:3]:
|
||||
print(f" - {v[:80]}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
if len(sys.argv) < 2:
|
||||
print("Usage: python agent.py <dotnet_executable>")
|
||||
sys.exit(1)
|
||||
result = analyze_dotnet_malware(sys.argv[1])
|
||||
print_report(result)
|
||||
Reference in New Issue
Block a user