钓鱼邮件调查之邮件头分析 是做什么的?
适用场景
- 调查疑似钓鱼邮件,确定其真实来源
- 验证发件人真实性并检测邮件欺骗(Spoofing)
- 在用户点击了钓鱼链接后的应急响应中
- 追踪可疑邮件的投递路径和转发服务器
- 验证 SPF、DKIM 和 DMARC 对齐情况,识别伪造邮件
前提条件
- 来自可疑邮件的原始邮件头(EML 或 MSG 格式)
- 了解 SMTP 协议和邮件头字段
- 能够使用 DNS 查询工具(dig、nslookup)验证 SPF/DKIM/DMARC
- 邮件头分析工具(MHA、emailheaders.net 等概念)
- 安装了用于自动分析的 Python 邮件解析库
- 能够访问威胁情报平台查询 IP/域名声誉
工作流程
步骤 1:提取原始邮件头
# 从 Outlook 导出:打开邮件 > 文件 > 属性 > Internet 邮件头
# 从 Gmail 导出:打开邮件 > 三点菜单 > 显示原始邮件
# 从 Thunderbird 导出:查看 > 消息源代码
# 如果从取证镜像中获得 EML 文件
cp /mnt/evidence/Users/suspect/AppData/Local/Microsoft/Outlook/phishing_email.eml \
/cases/case-2024-001/email/
# 如果处理的是 PST 文件,提取单个邮件
pip install pypff
python3 << 'PYEOF'
import pypff
pst = pypff.file()
pst.open("/cases/case-2024-001/email/outlook.pst")
root = pst.get_root_folder()
def extract_messages(folder, path=""):
for i in range(folder.get_number_of_sub_messages()):
msg = folder.get_sub_message(i)
headers = msg.get_transport_headers()
subject = msg.get_subject()
if headers:
filename = f"/cases/case-2024-001/email/msg_{i}_{subject[:30]}.txt"
with open(filename, 'w') as f:
f.write(headers)
for i in range(folder.get_number_of_sub_folders()):
extract_messages(folder.get_sub_folder(i))
extract_messages(root)
PYEOF
步骤 2:解析邮件头链
# 使用 Python 的 email 库解析邮件头
python3 << 'PYEOF'
import email
from email import policy
with open('/cases/case-2024-001/email/phishing_email.eml', 'r') as f:
msg = email.message_from_file(f, policy=policy.default)
print("=== 关键邮件头字段 ===")
print(f"发件人(From): {msg['From']}")
print(f"收件人(To): {msg['To']}")
print(f"主题(Subject): {msg['Subject']}")
print(f"日期(Date): {msg['Date']}")
print(f"消息ID(Message-ID): {msg['Message-ID']}")
print(f"回复地址(Reply-To): {msg['Reply-To']}")
print(f"退信地址(Return-Path): {msg['Return-Path']}")
print(f"X-Mailer: {msg['X-Mailer']}")
print(f"X-Originating-IP: {msg['X-Originating-IP']}")
print("\n=== 接收头(Received) (从下到上 = 时间顺序) ===")
received_headers = msg.get_all('Received')
if received_headers:
for i, header in enumerate(reversed(received_headers)):
print(f"\n跳数 {i+1}: {header.strip()}")
print("\n=== 认证结果 ===")
auth_results = msg.get_all('Authentication-Results')
if auth_results:
for result in auth_results:
print(result)
print(f"\nARC-Authentication-Results: {msg.get('ARC-Authentication-Results', '未出现')}")
print(f"Received-SPF: {msg.get('Received-SPF', '未出现')}")
print(f"DKIM-Signature: {msg.get('DKIM-Signature', '未出现')}")
PYEOF
步骤 3:验证 SPF、DKIM 和 DMARC 记录
# 提取信封发件人域名
SENDER_DOMAIN="example-corp.com"
# 检查 SPF 记录
dig TXT $SENDER_DOMAIN +short | grep "v=spf1"
# 示例输出:"v=spf1 include:_spf.google.com include:sendgrid.net ~all"
# 检查 DKIM 记录(选择器来自 DKIM-Signature 头,例如 "s=selector1")
DKIM_SELECTOR="selector1"
dig TXT ${DKIM_SELECTOR}._domainkey.${SENDER_DOMAIN} +short
# 检查 DMARC 记录
dig TXT _dmarc.${SENDER_DOMAIN} +short
# 示例输出:"v=DMARC1; p=reject; rua=mailto:[email protected]; pct=100"
# 验证发送 IP 是否符合 SPF
# 从第一个 Received 头中提取 IP
SENDING_IP="203.0.113.45"
# 使用 Python 进行 SPF 手动检查
python3 << 'PYEOF'
import spf # pip install pyspf
result, explanation = spf.check2(
i='203.0.113.45',
s='[email protected]',
h='mail.example-corp.com'
)
print(f"SPF 结果: {result}")
print(f"说明: {explanation}")
# 可能的结果:pass, fail, softfail, neutral, none, temperror, permerror
PYEOF
# 检查发送 IP 是否在已知恶意 IP 列表中
# 查询 AbuseIPDB 或 VirusTotal
curl -s "https://api.abuseipdb.com/api/v2/check?ipAddress=${SENDING_IP}" \
-H "Key: YOUR_API_KEY" -H "Accept: application/json" | python3 -m json.tool
步骤 4:分析发件人域名和基础设施
# WHOIS 查询发件人域名
whois $SENDER_DOMAIN | grep -iE '(registrar|creation|expiration|registrant|nameserver)'
# 检查域名年龄(最近注册的域名可疑)
# DNS 记录调查
dig A $SENDER_DOMAIN +short
dig MX $SENDER_DOMAIN +short
dig NS $SENDER_DOMAIN +short
# 发送 IP 的反向 DNS
dig -x $SENDING_IP +short
# 检查是否存在仿冒/钓鱼域名(视觉相似)
# 与合法域名对比
python3 << 'PYEOF'
import Levenshtein # pip install python-Levenshtein
legitimate = "microsoft.com"
suspicious = "micr0soft.com"
distance = Levenshtein.distance(legitimate, suspicious)
ratio = Levenshtein.ratio(legitimate, suspicious)
print(f"编辑距离: {distance}")
print(f"相似度: {ratio:.2%}")
if ratio > 0.8:
print("警告:极有可能是拼写诈骗/仿冒域名!")
PYEOF
# 在 VirusTotal 上检查域名声誉
curl -s "https://www.virustotal.com/api/v3/domains/${SENDER_DOMAIN}" \
-H "x-apikey: YOUR_VT_API_KEY" | python3 -m json.tool
# 检查 Reply-To 是否与 From 不同(常见的钓鱼指标)
python3 -c "
import email
with open('/cases/case-2024-001/email/phishing_email.eml') as f:
msg = email.message_from_file(f)
from_addr = email.utils.parseaddr(msg['From'])[1]
reply_to = email.utils.parseaddr(msg.get('Reply-To', msg['From']))[1]
if from_addr != reply_to:
print(f'警告:发件人({from_addr}) != 回复地址({reply_to})')
else:
print('发件人和回复地址匹配')
"
步骤 5:检查邮件正文和附件
# 从邮件正文中提取 URL
python3 << 'PYEOF'
import email
import re
from email import policy
with open('/cases/case-2024-001/email/phishing_email.eml', 'r') as f:
msg = email.message_from_file(f, policy=policy.default)
body = msg.get_body(preferencelist=('html', 'plain'))
if body:
content = body.get_content()
urls = re.findall(r'https?://[^\s<>\"\']+', content)
print("=== 邮件正文中发现的 URL ===")
for url in set(urls):
print(f" {url}")
# 检查 URL 混淆(显示文本 != href)
href_pattern = re.findall(r'<a[^>]*href=[\"\']([^\"\']+)[\"\'][^>]*>(.*?)</a>', content, re.DOTALL)
print("\n=== 超链接分析 ===")
for href, text in href_pattern:
display_url = re.findall(r'https?://[^\s<]+', text)
if display_url and display_url[0] != href:
print(f" 不匹配:显示='{display_url[0]}' -> 实际='{href}'")
# 提取并计算附件哈希
print("\n=== 附件 ===")
for part in msg.walk():
if part.get_content_disposition() == 'attachment':
filename = part.get_filename()
content = part.get_payload(decode=True)
import hashlib
sha256 = hashlib.sha256(content).hexdigest()
print(f" 文件: {filename}, 大小: {len(content)}, SHA-256: {sha256}")
with open(f'/cases/case-2024-001/email/attachments/{filename}', 'wb') as af:
af.write(content)
PYEOF
# 将附件哈希提交到 VirusTotal
# 将 URL 提交到 URLhaus 或 PhishTank 进行声誉检查
关键概念
| 概念 | 描述 |
|---|---|
| SPF(发件人策略框架) | 指定域名的授权邮件服务器的 DNS 记录 |
| DKIM(域名密钥识别邮件) | 验证邮件内容完整性的加密签名 |
| DMARC | 结合 SPF 和 DKIM 进行发件人认证的策略框架 |
| Received 头 | 服务器添加的头部,显示投递链中的每一跳(从下到上阅读) |
| Return-Path | 用于退信的信封发件人地址;可能与 From 不同 |
| Message-ID | 由原始邮件服务器分配的唯一标识符 |
| X-Originating-IP | 原始发件人 IP 地址(由某些邮件服务添加) |
| 头伪造 | 攻击者可以伪造 From、Reply-To 等头,但无法伪造 Received 链 |
工具与系统
| 工具 | 用途 |
|---|---|
| MXToolbox | 在线邮件头分析器和 DNS 查询 |
| dig/nslookup | 用于 SPF、DKIM、DMARC 验证的 DNS 记录查询 |
| pyspf | Python SPF 记录验证库 |
| dkimpy | Python DKIM 签名验证库 |
| PhishTool | 专门的钓鱼邮件分析平台 |
| VirusTotal | URL 和文件信誉检查服务 |
| AbuseIPDB | IP 地址信誉数据库 |
| whois | 域名注册信息查询 |
常见场景
场景 1:CEO 欺诈 / 商业邮件入侵 (BEC) 邮件声称来自 CEO,但 Reply-To 指向一个 Gmail 地址,SPF 验证失败(因为发送 IP 未被授权用于被欺骗的域名),DKIM 缺失,From 域是一个仿冒域名(ceo-company.com 对比 company.com)。
场景 2:凭证收集钓鱼 邮件包含一个链接,显示为 "login.microsoft.com" 但 href 指向一个仿冒域名;附件是一个 HTML 文件,包含带有凭证窃取 JavaScript 的虚假登录页面;发送域名是在 3 天前注册的。
场景 3:通过附件传递恶意软件 邮件包含带有宏的 Office 文档附件;发件人域名通过了 SPF 验证(但账户已被入侵);DKIM 签名有效(通过合法基础设施发送);附件 SHA-256 与 VirusTotal 上的已知恶意软件匹配。
场景 4:使用合法服务的鱼叉式钓鱼 攻击者使用合法的邮件营销服务发送钓鱼邮件;SPF 和 DKIM 都通过验证(因为该服务已获得授权);钓鱼内容隐藏在邮件正文中,而非基础设施层面;需要 URL 和内容分析,而非邮件头身份验证检查。
输出格式
邮件头分析报告:
主题(Subject): "紧急:发票付款请求"
发件人(From): [email protected] (伪造)
回复地址(Reply-To): [email protected] (不匹配)
退信地址(Return-Path): <[email protected]>
日期(Date): 2024-01-15 09:23:45 UTC
投递路径(4 跳):
跳 1:mail-server.xyz [203.0.113.45] -> relay1.isp.com
跳 2:relay1.isp.com -> mx.target-company.com
跳 3:mx.target-company.com -> internal-filter.target.com
跳 4:internal-filter.target.com -> mailbox
身份验证:
SPF: 失败 (203.0.113.45 未被授权用于 examp1e-corp.com)
DKIM: 无 (未出现签名)
DMARC: 失败 (p=none, 未强制执行)
钓鱼指标:
- 仿冒域名 (examp1e-corp.com vs example-corp.com, 96% 相似)
- From/Reply-To 不匹配
- 域名在邮件发送前 2 天注册
- 正文中的 URL 指向凭证收集页面
- 附件:invoice.xlsm (SHA-256: a3f2...) - VT 上已知恶意软件
风险等级:高