reverse-skills-max: initial public release (restored, redacted, hardened)
This commit is contained in:
@@ -0,0 +1,73 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Analyze extracted Excel 4.0 (XLM) macro formulas for malicious behavior.
|
||||
|
||||
Reads a text dump of macro-sheet formulas and flags auto-run entry points, execution/download
|
||||
primitives (EXEC/CALL/REGISTER), and obfuscation (CHAR concatenation, FORMULA.FILL self-writing).
|
||||
Parses text only; executes nothing and never opens the workbook. Defangs recovered URLs.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import json
|
||||
import re
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
AUTO_NAMES = ["auto_open", "auto_close", "auto_activate", "auto_deactivate"]
|
||||
EXEC_PRIMS = ["exec(", "call(", "register(", "fopen(", "fwrite("]
|
||||
DOWNLOAD_HINTS = ["urlmon", "urldownloadtofile", "http", "msxml2", "winhttp", "shellexecute"]
|
||||
EXECUTORS = ["regsvr32", "rundll32", "mshta", "powershell", "cmd.exe", "wscript", "cscript"]
|
||||
OBFUSCATION = ["char(", "formula.fill", "formula(", "mid(", "&", "concatenate("]
|
||||
RE_URL = re.compile(r"https?://[^\s\"'<>)]+", re.I)
|
||||
RE_CHAR = re.compile(r"CHAR\((\d{1,3})\)", re.I)
|
||||
|
||||
|
||||
def _defang(s: str) -> str:
|
||||
return s.replace("http", "hxxp").replace(".", "[.]")
|
||||
|
||||
|
||||
def _reconstruct_char(line: str) -> str:
|
||||
chars = RE_CHAR.findall(line)
|
||||
if len(chars) < 3:
|
||||
return ""
|
||||
try:
|
||||
return "".join(chr(int(c)) for c in chars if 0 <= int(c) < 256)
|
||||
except ValueError:
|
||||
return ""
|
||||
|
||||
|
||||
def analyze(path: str) -> dict:
|
||||
text = Path(path).read_text(encoding="utf-8-sig", errors="replace")
|
||||
low = text.lower()
|
||||
auto = sorted({n for n in AUTO_NAMES if n in low})
|
||||
exec_prims = sorted({p.rstrip("(") for p in EXEC_PRIMS if p in low})
|
||||
downloads = sorted({d for d in DOWNLOAD_HINTS if d in low})
|
||||
executors = sorted({e for e in EXECUTORS if e in low})
|
||||
obf = sorted({o.rstrip("(") for o in OBFUSCATION if o in low})
|
||||
urls = sorted({_defang(u) for u in RE_URL.findall(text)})
|
||||
reconstructed = sorted({r for line in text.splitlines()
|
||||
if (r := _reconstruct_char(line))})[:25]
|
||||
return {
|
||||
"auto_run_triggers": auto,
|
||||
"execution_primitives": exec_prims,
|
||||
"download_indicators": downloads,
|
||||
"executors": executors,
|
||||
"obfuscation_techniques": obf,
|
||||
"urls": urls[:50],
|
||||
"reconstructed_char_strings": reconstructed,
|
||||
"is_likely_malicious_xlm": bool(auto and (exec_prims or downloads)),
|
||||
}
|
||||
|
||||
|
||||
def main(argv: list[str] | None = None) -> int:
|
||||
parser = argparse.ArgumentParser(description="Analyze Excel 4.0 (XLM) macros")
|
||||
sub = parser.add_subparsers(dest="cmd", required=True)
|
||||
p = sub.add_parser("analyze")
|
||||
p.add_argument("file")
|
||||
args = parser.parse_args(argv)
|
||||
print(json.dumps(analyze(args.file), indent=2))
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
||||
Reference in New Issue
Block a user