#!/usr/bin/env python3 """Agent for hunting spearphishing indicators across email and endpoint logs.""" import json import argparse import re from datetime import datetime SUSPICIOUS_EXTENSIONS = [ ".exe", ".scr", ".bat", ".cmd", ".ps1", ".vbs", ".js", ".hta", ".iso", ".img", ".lnk", ".dll", ".msi", ".wsf", ] MACRO_EXTENSIONS = [".xlsm", ".docm", ".pptm", ".xlsb"] PHISHING_URL_PATTERNS = [ r"https?://bit\.ly/", r"https?://tinyurl\.com/", r"https?://[^/]*login[^/]*\.", r"https?://[^/]*signin[^/]*\.", r"https?://[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}", r"https?://[^/]*\.top/", r"https?://[^/]*\.xyz/", ] URGENCY_KEYWORDS = [ "urgent", "immediate action", "account suspended", "verify your", "password expired", "click here immediately", "security alert", "unauthorized access", "confirm your identity", ] def load_email_logs(log_path): """Load email logs from JSON lines file.""" entries = [] with open(log_path) as f: for line in f: try: entries.append(json.loads(line)) except json.JSONDecodeError: continue return entries def check_attachment_risk(filename): """Assess risk of email attachment by extension.""" lower = filename.lower() if any(lower.endswith(ext) for ext in SUSPICIOUS_EXTENSIONS): return "CRITICAL" if any(lower.endswith(ext) for ext in MACRO_EXTENSIONS): return "HIGH" if lower.endswith(".html") or lower.endswith(".htm"): return "HIGH" if lower.endswith(".zip") or lower.endswith(".rar") or lower.endswith(".7z"): return "MEDIUM" return "LOW" def detect_suspicious_attachments(emails): """Find emails with dangerous attachment types.""" findings = [] for email in emails: attachments = email.get("attachments", []) for att in attachments: name = att if isinstance(att, str) else att.get("filename", "") risk = check_attachment_risk(name) if risk in ("CRITICAL", "HIGH"): findings.append({ "subject": email.get("subject", ""), "sender": email.get("from", email.get("sender", "")), "recipient": email.get("to", email.get("recipient", "")), "timestamp": email.get("timestamp", email.get("date", "")), "attachment": name, "risk": risk, "category": "suspicious_attachment", }) return findings def detect_phishing_urls(emails): """Detect phishing URLs in email body or links.""" findings = [] for email in emails: body = email.get("body", email.get("content", "")) urls = email.get("urls", []) text = body + " " + " ".join(urls) if urls else body for pattern in PHISHING_URL_PATTERNS: matches = re.findall(pattern, text, re.IGNORECASE) for url in matches: findings.append({ "subject": email.get("subject", ""), "sender": email.get("from", email.get("sender", "")), "recipient": email.get("to", email.get("recipient", "")), "url": url[:200], "pattern": pattern, "severity": "HIGH", "category": "phishing_url", }) return findings def detect_urgency_lures(emails): """Detect social engineering urgency keywords.""" findings = [] for email in emails: subject = email.get("subject", "") body = email.get("body", email.get("content", "")) text = (subject + " " + body).lower() matched = [kw for kw in URGENCY_KEYWORDS if kw in text] if len(matched) >= 2: findings.append({ "subject": email.get("subject", ""), "sender": email.get("from", email.get("sender", "")), "recipient": email.get("to", email.get("recipient", "")), "keywords_matched": matched, "severity": "MEDIUM", "category": "urgency_lure", }) return findings def detect_sender_spoofing(emails): """Detect display name vs envelope sender mismatches.""" findings = [] for email in emails: display_from = email.get("from", email.get("sender", "")) envelope = email.get("envelope_from", email.get("return_path", "")) if display_from and envelope: display_domain = re.search(r"@([\w.-]+)", display_from) envelope_domain = re.search(r"@([\w.-]+)", envelope) if display_domain and envelope_domain: if display_domain.group(1).lower() != envelope_domain.group(1).lower(): findings.append({ "display_from": display_from, "envelope_from": envelope, "recipient": email.get("to", email.get("recipient", "")), "subject": email.get("subject", ""), "severity": "HIGH", "category": "sender_spoofing", }) return findings def main(): parser = argparse.ArgumentParser(description="Spearphishing Indicator Hunter") parser.add_argument("--email-log", required=True, help="JSON lines email log") parser.add_argument("--output", default="spearphishing_hunt_report.json") parser.add_argument("--action", choices=[ "attachments", "urls", "urgency", "spoofing", "full_analysis" ], default="full_analysis") args = parser.parse_args() emails = load_email_logs(args.email_log) report = {"generated_at": datetime.utcnow().isoformat(), "total_emails": len(emails), "findings": {}} print(f"[+] Loaded {len(emails)} email entries") if args.action in ("attachments", "full_analysis"): f = detect_suspicious_attachments(emails) report["findings"]["suspicious_attachments"] = f print(f"[+] Suspicious attachments: {len(f)}") if args.action in ("urls", "full_analysis"): f = detect_phishing_urls(emails) report["findings"]["phishing_urls"] = f print(f"[+] Phishing URLs: {len(f)}") if args.action in ("urgency", "full_analysis"): f = detect_urgency_lures(emails) report["findings"]["urgency_lures"] = f print(f"[+] Urgency lure emails: {len(f)}") if args.action in ("spoofing", "full_analysis"): f = detect_sender_spoofing(emails) report["findings"]["sender_spoofing"] = f print(f"[+] Sender spoofing detected: {len(f)}") with open(args.output, "w") as f: json.dump(report, f, indent=2, default=str) print(f"[+] Report saved to {args.output}") if __name__ == "__main__": main()