$npx -y skills add killvxk/cybersecurity-skills-zh --skill analyzing-email-headers-for-phishing-investigation解析和分析电子邮件头部以追踪钓鱼邮件的来源,通过 SPF、DKIM 和 DMARC 验证来核实发件人真实性并识别伪造行为。
| 1 | # 分析电子邮件头部用于钓鱼调查 |
| 2 | |
| 3 | ## 适用场景 |
| 4 | - 调查疑似钓鱼(Phishing)邮件以确定其真实来源时 |
| 5 | - 验证发件人真实性并检测电子邮件伪造时 |
| 6 | - 用户点击钓鱼链接后的事件响应期间 |
| 7 | - 追踪可疑邮件的投递路径和中继服务器时 |
| 8 | - 验证 SPF、DKIM 和 DMARC 对齐以识别伪造时 |
| 9 | |
| 10 | ## 前置条件 |
| 11 | - 来自可疑邮件的原始邮件头部(EML 或 MSG 格式) |
| 12 | - 了解 SMTP 协议和电子邮件头部字段 |
| 13 | - 访问 DNS 查询工具(dig、nslookup)用于 SPF/DKIM/DMARC 验证 |
| 14 | - 电子邮件头部分析工具(MHA、emailheaders.net 相关概念) |
| 15 | - 带邮件解析库的 Python 用于自动化分析 |
| 16 | - 访问威胁情报(Threat Intelligence)平台进行 IP/域名声誉查询 |
| 17 | |
| 18 | ## 工作流程 |
| 19 | |
| 20 | ### 步骤 1:提取原始电子邮件头部 |
| 21 | |
| 22 | ```bash |
| 23 | # 从 Outlook 导出: 打开邮件 > 文件 > 属性 > Internet 头部 |
| 24 | # 从 Gmail 导出: 打开邮件 > 三个点 > 显示原始邮件 |
| 25 | # 从 Thunderbird 导出: 查看 > 消息源码 |
| 26 | |
| 27 | # 如果从取证镜像处理 EML 文件 |
| 28 | cp /mnt/evidence/Users/suspect/AppData/Local/Microsoft/Outlook/phishing_email.eml \ |
| 29 | /cases/case-2024-001/email/ |
| 30 | |
| 31 | # 如果处理 PST 文件,提取单个消息 |
| 32 | pip install pypff |
| 33 | python3 << 'PYEOF' |
| 34 | import pypff |
| 35 | |
| 36 | pst = pypff.file() |
| 37 | pst.open("/cases/case-2024-001/email/outlook.pst") |
| 38 | root = pst.get_root_folder() |
| 39 | |
| 40 | def extract_messages(folder, path=""): |
| 41 | for i in range(folder.get_number_of_sub_messages()): |
| 42 | msg = folder.get_sub_message(i) |
| 43 | headers = msg.get_transport_headers() |
| 44 | subject = msg.get_subject() |
| 45 | if headers: |
| 46 | filename = f"/cases/case-2024-001/email/msg_{i}_{subject[:30]}.txt" |
| 47 | with open(filename, 'w') as f: |
| 48 | f.write(headers) |
| 49 | for i in range(folder.get_number_of_sub_folders()): |
| 50 | extract_messages(folder.get_sub_folder(i)) |
| 51 | |
| 52 | extract_messages(root) |
| 53 | PYEOF |
| 54 | ``` |
| 55 | |
| 56 | ### 步骤 2:解析电子邮件头部链 |
| 57 | |
| 58 | ```bash |
| 59 | # 使用 Python email 库解析头部 |
| 60 | python3 << 'PYEOF' |
| 61 | import email |
| 62 | from email import policy |
| 63 | |
| 64 | with open('/cases/case-2024-001/email/phishing_email.eml', 'r') as f: |
| 65 | msg = email.message_from_file(f, policy=policy.default) |
| 66 | |
| 67 | print("=== 关键头部字段 ===") |
| 68 | print(f"From: {msg['From']}") |
| 69 | print(f"To: {msg['To']}") |
| 70 | print(f"Subject: {msg['Subject']}") |
| 71 | print(f"Date: {msg['Date']}") |
| 72 | print(f"Message-ID: {msg['Message-ID']}") |
| 73 | print(f"Reply-To: {msg['Reply-To']}") |
| 74 | print(f"Return-Path: {msg['Return-Path']}") |
| 75 | print(f"X-Mailer: {msg['X-Mailer']}") |
| 76 | print(f"X-Originating-IP: {msg['X-Originating-IP']}") |
| 77 | |
| 78 | print("\n=== Received 头部(从底部到顶部 = 时间顺序)===") |
| 79 | received_headers = msg.get_all('Received') |
| 80 | if received_headers: |
| 81 | for i, header in enumerate(reversed(received_headers)): |
| 82 | print(f"\n跳 {i+1}: {header.strip()}") |
| 83 | |
| 84 | print("\n=== 认证结果 ===") |
| 85 | auth_results = msg.get_all('Authentication-Results') |
| 86 | if auth_results: |
| 87 | for result in auth_results: |
| 88 | print(result) |
| 89 | |
| 90 | print(f"\nARC-Authentication-Results: {msg.get('ARC-Authentication-Results', '不存在')}") |
| 91 | print(f"Received-SPF: {msg.get('Received-SPF', '不存在')}") |
| 92 | print(f"DKIM-Signature: {msg.get('DKIM-Signature', '不存在')}") |
| 93 | PYEOF |
| 94 | ``` |
| 95 | |
| 96 | ### 步骤 3:验证 SPF、DKIM 和 DMARC 记录 |
| 97 | |
| 98 | ```bash |
| 99 | # 提取信封发件人域名 |
| 100 | SENDER_DOMAIN="example-corp.com" |
| 101 | |
| 102 | # 检查 SPF 记录 |
| 103 | dig TXT $SENDER_DOMAIN +short | grep "v=spf1" |
| 104 | # 示例: "v=spf1 include:_spf.google.com include:sendgrid.net ~all" |
| 105 | |
| 106 | # 检查 DKIM 记录(选择器来自 DKIM-Signature 头部,例如 "s=selector1") |
| 107 | DKIM_SELECTOR="selector1" |
| 108 | dig TXT ${DKIM_SELECTOR}._domainkey.${SENDER_DOMAIN} +short |
| 109 | |
| 110 | # 检查 DMARC 记录 |
| 111 | dig TXT _dmarc.${SENDER_DOMAIN} +short |
| 112 | # 示例: "v=DMARC1; p=reject; rua=mailto:dmarc@example-corp.com; pct=100" |
| 113 | |
| 114 | # 对照 SPF 验证发送 IP |
| 115 | # 从第一个 Received 头部提取 IP |
| 116 | SENDING_IP="203.0.113.45" |
| 117 | |
| 118 | # 使用 Python 手动进行 SPF 检查 |
| 119 | python3 << 'PYEOF' |
| 120 | import spf # pip install pyspf |
| 121 | |
| 122 | result, explanation = spf.check2( |
| 123 | i='203.0.113.45', |
| 124 | s='sender@example-corp.com', |
| 125 | h='mail.example-corp.com' |
| 126 | ) |
| 127 | print(f"SPF 结果: {result}") |
| 128 | print(f"说明: {explanation}") |
| 129 | # 结果: pass(通过), fail(失败), softfail(软失败), neutral(中性), none(无), temperror(临时错误), permerror(永久错误) |
| 130 | PYEOF |
| 131 | |
| 132 | # 检查发送 IP 是否在已知恶意 IP 列表中 |
| 133 | # 查询 AbuseIPDB 或 VirusTotal |
| 134 | curl -s "https://api.abuseipdb.com/api/v2/check?ipAddress=${SENDING_IP}" \ |
| 135 | -H "Key: YOUR_API_KEY" -H "Accept: application/json" | python3 -m json.tool |
| 136 | ``` |
| 137 | |
| 138 | ### 步骤 4:分析发件人域名和基础设施 |
| 139 | |
| 140 | ```bash |
| 141 | # 对发件人域名进行 WHOIS 查询 |
| 142 | whois $SENDER_DOMAIN | grep -iE '(registrar|creation|expiration|registrant|nameserver)' |
| 143 | |
| 144 | # 检查域名年龄(最近注册的域名可疑) |
| 145 | # DNS 记录调查 |
| 146 | dig A $SENDER_DOMAIN +short |
| 147 | dig MX $SENDER_DOMAIN +short |
| 148 | dig NS $SENDER_DOMAIN +short |
| 149 | |
| 150 | # 对发送 IP 进行反向 DNS 查询 |
| 151 | dig -x $SENDING_IP +short |
| 152 | |
| 153 | # 检查仿冒/错字域名 |
| 154 | # 使用视觉相似度与合法域名进行比较 |
| 155 | python3 << 'PYEOF' |
| 156 | import Levenshtein # pip install python-Levenshtein |
| 157 | |
| 158 | legitimate = "microsoft.com" |
| 159 | suspicious = "micr0soft.com" |
| 160 | |
| 161 | distance = Levenshtein.distance(legitimate, suspicious) |
| 162 | ratio = Levenshtein.ratio(legitimate, suspicious) |
| 163 | print(f"编辑距离: {distance}") |
| 164 | print(f"相似度: {ratio:.2%}") |
| 165 | if ratio > 0.8: |
| 166 | print("警告: 可能是错字/仿冒域名!") |
| 167 | PYEOF |
| 168 | |
| 169 | # 在 VirusTotal 上检查域名声誉 |
| 170 | curl -s "https://www.virustotal.com/api/v3/domains/${SENDER_DOMAIN}" \ |
| 171 | -H "x-apikey: YOUR_VT_API_KEY" | python3 -m json.tool |
| 172 | |
| 173 | # 检查 Reply-To 是否与 From 不同(常见钓鱼指标) |
| 174 | python3 -c " |
| 175 | import email |
| 176 | with open('/cases/case-2024-001/email/phishing_email.eml') as f: |
| 177 | msg = emai |