mirror of
https://github.com/mukul975/Anthropic-Cybersecurity-Skills.git
synced 2026-06-11 13:44:56 +03:00
c47eed6a64
- 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
218 lines
7.8 KiB
Python
218 lines
7.8 KiB
Python
#!/usr/bin/env python3
|
|
"""Static malware analysis agent using pefile for PE binary inspection."""
|
|
|
|
import pefile
|
|
import hashlib
|
|
import math
|
|
import os
|
|
import re
|
|
import sys
|
|
import datetime
|
|
|
|
|
|
SUSPICIOUS_IMPORTS = {
|
|
"Process Injection": [
|
|
"VirtualAllocEx", "WriteProcessMemory", "CreateRemoteThread",
|
|
"NtCreateThreadEx", "QueueUserAPC", "NtMapViewOfSection",
|
|
],
|
|
"Keylogging": [
|
|
"GetAsyncKeyState", "SetWindowsHookExA", "SetWindowsHookExW",
|
|
"GetKeyState", "GetKeyboardState",
|
|
],
|
|
"Persistence": [
|
|
"RegSetValueExA", "RegSetValueExW", "CreateServiceA",
|
|
"CreateServiceW", "RegCreateKeyExA",
|
|
],
|
|
"Evasion": [
|
|
"IsDebuggerPresent", "CheckRemoteDebuggerPresent",
|
|
"NtQueryInformationProcess", "GetTickCount", "QueryPerformanceCounter",
|
|
],
|
|
"Network": [
|
|
"InternetOpenA", "InternetOpenW", "HttpSendRequestA",
|
|
"URLDownloadToFileA", "URLDownloadToFileW", "WSAStartup",
|
|
"InternetConnectA", "HttpOpenRequestA",
|
|
],
|
|
"Crypto": [
|
|
"CryptEncrypt", "CryptDecrypt", "CryptAcquireContextA",
|
|
"CryptGenKey", "CryptImportKey",
|
|
],
|
|
}
|
|
|
|
PACKER_SECTIONS = {
|
|
".upx0": "UPX", ".upx1": "UPX", ".aspack": "ASPack",
|
|
".adata": "ASPack", ".nsp0": "NsPack", ".vmprotect": "VMProtect",
|
|
".themida": "Themida", ".enigma1": "Enigma", ".petite": "Petite",
|
|
}
|
|
|
|
|
|
def compute_hashes(filepath):
|
|
with open(filepath, "rb") as f:
|
|
data = f.read()
|
|
return {
|
|
"md5": hashlib.md5(data).hexdigest(),
|
|
"sha1": hashlib.sha1(data).hexdigest(),
|
|
"sha256": hashlib.sha256(data).hexdigest(),
|
|
"size": len(data),
|
|
}
|
|
|
|
|
|
def analyze_sections(pe):
|
|
sections = []
|
|
for section in pe.sections:
|
|
name = section.Name.decode(errors="replace").rstrip("\x00")
|
|
entropy = section.get_entropy()
|
|
raw_size = section.SizeOfRawData
|
|
virtual_size = section.Misc_VirtualSize
|
|
ratio = virtual_size / raw_size if raw_size > 0 else 0
|
|
flags = []
|
|
if entropy > 7.0:
|
|
flags.append("HIGH_ENTROPY")
|
|
if ratio > 10:
|
|
flags.append("HIGH_VR_RATIO")
|
|
sections.append({
|
|
"name": name, "entropy": round(entropy, 2),
|
|
"raw_size": raw_size, "virtual_size": virtual_size,
|
|
"ratio": round(ratio, 2), "flags": flags,
|
|
})
|
|
return sections
|
|
|
|
|
|
def detect_packer(pe):
|
|
detected = []
|
|
for section in pe.sections:
|
|
name = section.Name.decode(errors="replace").rstrip("\x00").lower()
|
|
if name in PACKER_SECTIONS:
|
|
detected.append(PACKER_SECTIONS[name])
|
|
import_count = 0
|
|
if hasattr(pe, "DIRECTORY_ENTRY_IMPORT"):
|
|
import_count = sum(len(e.imports) for e in pe.DIRECTORY_ENTRY_IMPORT)
|
|
if import_count < 10:
|
|
detected.append(f"SUSPECTED_PACKED (only {import_count} imports)")
|
|
return detected
|
|
|
|
|
|
def analyze_imports(pe):
|
|
findings = []
|
|
if not hasattr(pe, "DIRECTORY_ENTRY_IMPORT"):
|
|
return [{"category": "PACKED", "dll": "N/A", "function": "No imports found"}]
|
|
for entry in pe.DIRECTORY_ENTRY_IMPORT:
|
|
dll_name = entry.dll.decode(errors="replace")
|
|
for imp in entry.imports:
|
|
if imp.name:
|
|
func_name = imp.name.decode(errors="replace")
|
|
for category, funcs in SUSPICIOUS_IMPORTS.items():
|
|
if func_name in funcs:
|
|
findings.append({
|
|
"category": category, "dll": dll_name,
|
|
"function": func_name,
|
|
})
|
|
return findings
|
|
|
|
|
|
def extract_strings(filepath, min_length=6):
|
|
indicators = {"urls": [], "ips": [], "emails": [], "registry": [], "paths": []}
|
|
with open(filepath, "rb") as f:
|
|
data = f.read()
|
|
ascii_strings = re.findall(rb"[\x20-\x7e]{%d,}" % min_length, data)
|
|
for s in ascii_strings:
|
|
s_decoded = s.decode("ascii", errors="ignore")
|
|
if re.search(r"https?://", s_decoded):
|
|
indicators["urls"].append(s_decoded)
|
|
if re.search(r"\b(\d{1,3}\.){3}\d{1,3}\b", s_decoded):
|
|
indicators["ips"].append(s_decoded)
|
|
if re.search(r"[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}", s_decoded):
|
|
indicators["emails"].append(s_decoded)
|
|
if re.search(r"HKLM|HKCU|CurrentVersion\\Run", s_decoded, re.IGNORECASE):
|
|
indicators["registry"].append(s_decoded)
|
|
if re.search(r"\.(exe|dll|bat|ps1|vbs|tmp)", s_decoded, re.IGNORECASE):
|
|
indicators["paths"].append(s_decoded)
|
|
for key in indicators:
|
|
indicators[key] = list(set(indicators[key]))[:20]
|
|
return indicators
|
|
|
|
|
|
def analyze_resources(pe):
|
|
resources = []
|
|
if not hasattr(pe, "DIRECTORY_ENTRY_RESOURCE"):
|
|
return resources
|
|
for rtype in pe.DIRECTORY_ENTRY_RESOURCE.entries:
|
|
if hasattr(rtype, "directory"):
|
|
for rid in rtype.directory.entries:
|
|
if hasattr(rid, "directory"):
|
|
for rlang in rid.directory.entries:
|
|
data = pe.get_data(
|
|
rlang.data.struct.OffsetToData,
|
|
rlang.data.struct.Size,
|
|
)
|
|
entropy = 0.0
|
|
if len(data) > 0:
|
|
freq = [0] * 256
|
|
for b in data:
|
|
freq[b] += 1
|
|
for f in freq:
|
|
if f > 0:
|
|
p = f / len(data)
|
|
entropy -= p * math.log2(p)
|
|
flags = []
|
|
if entropy > 7.0:
|
|
flags.append("HIGH_ENTROPY")
|
|
if data[:2] == b"MZ":
|
|
flags.append("EMBEDDED_PE")
|
|
resources.append({
|
|
"type_id": rtype.id, "size": len(data),
|
|
"entropy": round(entropy, 2), "flags": flags,
|
|
})
|
|
return resources
|
|
|
|
|
|
def analyze_pe(filepath):
|
|
hashes = compute_hashes(filepath)
|
|
pe = pefile.PE(filepath)
|
|
timestamp = pe.FILE_HEADER.TimeDateStamp
|
|
compile_time = datetime.datetime.utcfromtimestamp(timestamp).isoformat() + "Z"
|
|
report = {
|
|
"file": os.path.basename(filepath),
|
|
"hashes": hashes,
|
|
"compile_time": compile_time,
|
|
"sections": analyze_sections(pe),
|
|
"packer_indicators": detect_packer(pe),
|
|
"suspicious_imports": analyze_imports(pe),
|
|
"string_indicators": extract_strings(filepath),
|
|
"resources": analyze_resources(pe),
|
|
}
|
|
pe.close()
|
|
return report
|
|
|
|
|
|
def print_report(report):
|
|
print("STATIC MALWARE ANALYSIS REPORT")
|
|
print("=" * 40)
|
|
print(f"Sample: {report['file']}")
|
|
print(f"MD5: {report['hashes']['md5']}")
|
|
print(f"SHA-256: {report['hashes']['sha256']}")
|
|
print(f"Size: {report['hashes']['size']} bytes")
|
|
print(f"Compile Time: {report['compile_time']}")
|
|
if report["packer_indicators"]:
|
|
print(f"\nPACKER: {', '.join(report['packer_indicators'])}")
|
|
print("\nSECTIONS:")
|
|
for s in report["sections"]:
|
|
flags = f" [{', '.join(s['flags'])}]" if s["flags"] else ""
|
|
print(f" {s['name']:8s} entropy={s['entropy']} raw={s['raw_size']}{flags}")
|
|
print("\nSUSPICIOUS IMPORTS:")
|
|
for imp in report["suspicious_imports"]:
|
|
print(f" [{imp['category']}] {imp['dll']} -> {imp['function']}")
|
|
indicators = report["string_indicators"]
|
|
if any(indicators.values()):
|
|
print("\nEXTRACTED INDICATORS:")
|
|
for key, vals in indicators.items():
|
|
if vals:
|
|
print(f" {key}: {', '.join(vals[:5])}")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
if len(sys.argv) < 2:
|
|
print("Usage: python agent.py <pe_file>")
|
|
sys.exit(1)
|
|
result = analyze_pe(sys.argv[1])
|
|
print_report(result)
|