mirror of
https://github.com/mukul975/Anthropic-Cybersecurity-Skills.git
synced 2026-07-15 04:05:17 +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
165 lines
5.6 KiB
Python
165 lines
5.6 KiB
Python
#!/usr/bin/env python3
|
|
"""Agent for detecting spearphishing emails using email gateway log analysis."""
|
|
|
|
import argparse
|
|
import email
|
|
import json
|
|
import os
|
|
import re
|
|
from datetime import datetime, timezone
|
|
from email import policy
|
|
|
|
|
|
EXECUTIVE_TITLES = [
|
|
"ceo", "cfo", "cto", "cio", "coo", "president", "director",
|
|
"vp", "vice president", "managing director", "partner",
|
|
]
|
|
URGENCY_KEYWORDS = [
|
|
"urgent", "immediate", "asap", "right away", "time sensitive",
|
|
"confidential", "do not share", "wire transfer", "bank account",
|
|
"update payment", "invoice attached", "past due",
|
|
]
|
|
SUSPICIOUS_EXTENSIONS = {
|
|
".exe", ".scr", ".bat", ".cmd", ".ps1", ".vbs", ".js",
|
|
".hta", ".lnk", ".iso", ".img", ".dll", ".msi",
|
|
}
|
|
|
|
|
|
def parse_email_headers(eml_path):
|
|
"""Extract security-relevant headers from an email."""
|
|
with open(eml_path, "rb") as f:
|
|
msg = email.message_from_binary_file(f, policy=policy.default)
|
|
headers = {
|
|
"from": msg.get("From", ""),
|
|
"to": msg.get("To", ""),
|
|
"subject": msg.get("Subject", ""),
|
|
"reply_to": msg.get("Reply-To", ""),
|
|
"return_path": msg.get("Return-Path", ""),
|
|
"received_spf": msg.get("Received-SPF", ""),
|
|
"dkim_signature": msg.get("DKIM-Signature", ""),
|
|
"authentication_results": msg.get("Authentication-Results", ""),
|
|
"x_mailer": msg.get("X-Mailer", ""),
|
|
"message_id": msg.get("Message-ID", ""),
|
|
}
|
|
return headers, msg
|
|
|
|
|
|
def check_authentication(headers):
|
|
"""Verify SPF, DKIM, DMARC authentication results."""
|
|
issues = []
|
|
auth_results = headers.get("authentication_results", "").lower()
|
|
if "spf=fail" in auth_results or "spf=softfail" in auth_results:
|
|
issues.append("SPF validation failed")
|
|
if "dkim=fail" in auth_results:
|
|
issues.append("DKIM validation failed")
|
|
if "dmarc=fail" in auth_results:
|
|
issues.append("DMARC validation failed")
|
|
if not headers.get("dkim_signature"):
|
|
issues.append("No DKIM signature present")
|
|
from_domain = re.search(r"@([\w.-]+)", headers.get("from", ""))
|
|
reply_domain = re.search(r"@([\w.-]+)", headers.get("reply_to", ""))
|
|
if from_domain and reply_domain and from_domain.group(1) != reply_domain.group(1):
|
|
issues.append(f"Reply-To domain mismatch: {reply_domain.group(1)} vs {from_domain.group(1)}")
|
|
return issues
|
|
|
|
|
|
def check_content_indicators(msg):
|
|
"""Analyze email body for spearphishing content indicators."""
|
|
indicators = []
|
|
body = ""
|
|
for part in msg.walk():
|
|
if part.get_content_type() in ("text/plain", "text/html"):
|
|
payload = part.get_payload(decode=True)
|
|
if payload:
|
|
body += payload.decode("utf-8", errors="replace")
|
|
|
|
body_lower = body.lower()
|
|
for kw in URGENCY_KEYWORDS:
|
|
if kw in body_lower:
|
|
indicators.append(f"Urgency keyword: '{kw}'")
|
|
|
|
urls = re.findall(r'https?://[^\s<>"\']+', body)
|
|
for url in urls[:10]:
|
|
if re.search(r'\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}', url):
|
|
indicators.append(f"URL with IP address: {url[:80]}")
|
|
if len(url) > 100:
|
|
indicators.append(f"Unusually long URL: {url[:80]}...")
|
|
|
|
attachments = []
|
|
for part in msg.walk():
|
|
fname = part.get_filename()
|
|
if fname:
|
|
ext = os.path.splitext(fname)[1].lower()
|
|
attachments.append(fname)
|
|
if ext in SUSPICIOUS_EXTENSIONS:
|
|
indicators.append(f"Suspicious attachment: {fname}")
|
|
if ".." in fname or ext == ".exe.pdf":
|
|
indicators.append(f"Double extension: {fname}")
|
|
|
|
return indicators, attachments
|
|
|
|
|
|
def analyze_email(eml_path):
|
|
"""Full spearphishing analysis of a single email."""
|
|
headers, msg = parse_email_headers(eml_path)
|
|
auth_issues = check_authentication(headers)
|
|
content_indicators, attachments = check_content_indicators(msg)
|
|
|
|
all_indicators = auth_issues + content_indicators
|
|
score = len(all_indicators) * 15
|
|
risk = "CRITICAL" if score >= 75 else "HIGH" if score >= 50 else "MEDIUM" if score >= 25 else "LOW"
|
|
|
|
return {
|
|
"file": eml_path,
|
|
"from": headers["from"],
|
|
"to": headers["to"],
|
|
"subject": headers["subject"],
|
|
"auth_issues": auth_issues,
|
|
"content_indicators": content_indicators,
|
|
"attachments": attachments,
|
|
"risk_score": min(score, 100),
|
|
"risk_level": risk,
|
|
}
|
|
|
|
|
|
def main():
|
|
parser = argparse.ArgumentParser(
|
|
description="Detect spearphishing via email gateway analysis"
|
|
)
|
|
parser.add_argument("input", help=".eml file or directory")
|
|
parser.add_argument("--output", "-o", help="Output JSON report")
|
|
parser.add_argument("--verbose", "-v", action="store_true")
|
|
args = parser.parse_args()
|
|
|
|
print("[*] Spearphishing Detection Agent")
|
|
results = []
|
|
|
|
if os.path.isdir(args.input):
|
|
for root, _, files in os.walk(args.input):
|
|
for f in files:
|
|
if f.lower().endswith(".eml"):
|
|
results.append(analyze_email(os.path.join(root, f)))
|
|
else:
|
|
results.append(analyze_email(args.input))
|
|
|
|
flagged = [r for r in results if r["risk_level"] in ("HIGH", "CRITICAL")]
|
|
report = {
|
|
"timestamp": datetime.now(timezone.utc).isoformat(),
|
|
"emails_scanned": len(results),
|
|
"spearphishing_detected": len(flagged),
|
|
"results": results,
|
|
}
|
|
|
|
print(f"[*] Scanned {len(results)} emails, {len(flagged)} flagged")
|
|
|
|
if args.output:
|
|
with open(args.output, "w") as f:
|
|
json.dump(report, f, indent=2)
|
|
print(f"[*] Report saved to {args.output}")
|
|
else:
|
|
print(json.dumps(report, indent=2))
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|