发布于3小时前3小时 背景这是一类适合企业授权演练和 CTF 蓝队题的场景:参赛者拿到一封导出的 EML 邮件、邮件网关日志以及一份无害附件,需要判断邮件是否伪造、还原投递链路,并从附件和审计日志中提取 flag。重点不在“看起来像不像钓鱼”,而在于用 SPF、DKIM、DMARC、Received 链、哈希和终端日志形成可验证的证据闭环。以下操作仅针对自建靶场、CTF 题目或已明确授权的邮件安全演练。附件样本应使用无害文件,分析环境应与生产网络隔离。题目场景与证据假设题目提供 incident.eml、gateway.csv 和 attachment.zip。邮件显示 From 为 [email protected],正文要求收件人查看“季度奖金调整”,附件中实际只有一段经过 Base64 编码的 flag。网关日志记录 SMTP 会话,终端日志记录附件落盘。需要回答四个问题:信封发件人与头部发件人是否一致;SPF、DKIM、DMARC 是否通过;附件是否在传输后被替换;哪台终端打开过附件。先固定证据,避免后续解压、换行转换或编辑器自动保存改变原始文件。所有时间统一换算为 UTC,同时记录原始时区。mkdir -p case/{original,work,output} cp incident.eml gateway.csv attachment.zip case/original/ sha256sum case/original/* | tee case/output/SHA256SUMS cp case/original/incident.eml case/work/ file case/original/incident.eml case/original/attachment.zip unzip -l case/original/attachment.zip问题定位:不要先信 Authentication-ResultsAuthentication-Results 只是某台邮件服务器写入的判断。攻击者可以在进入可信邮件网关前伪造同名头,因此必须确认它由哪一跳添加。排查时从最靠近本组织的 Received 头向外回溯,找到组织控制的首台 MX;只有这台 MX 及其后续可信节点写入的认证结果才可作为依据。Return-Path 对应最终投递阶段看到的信封发件人,可能与 From 不同。SPF 校验的是连接 IP 与 MAIL FROM 或 HELO 域,不直接证明可见 From。DKIM 证明指定域对选定头部和正文做过签名,不等于发件人身份绝对可信。DMARC 要求 SPF 或 DKIM 至少一项通过,并与可见 From 域满足对齐关系。提取邮件头与 MIME 结构Python 标准库可以在不执行附件的情况下解析 EML。使用 policy.default 保留较合理的头部语义;导出附件时重新计算哈希,不依赖邮件正文中自报的散列值。python3 - <<'PY' from email import policy from email.parser import BytesParser from pathlib import Path import hashlib p = Path('case/work/incident.eml') msg = BytesParser(policy=policy.default).parsebytes(p.read_bytes()) for name in ['From','To','Subject','Date','Message-ID','Return-Path', 'Authentication-Results','DKIM-Signature']: for value in msg.get_all(name, []): print(f'{name}: {value}') out = Path('case/output/attachments') out.mkdir(parents=True, exist_ok=True) for i, part in enumerate(msg.walk()): if part.get_content_disposition() == 'attachment': data = part.get_payload(decode=True) or b'' name = part.get_filename() or f'part-{i}.bin' target = out / Path(name).name target.write_bytes(data) print(name, len(data), hashlib.sha256(data).hexdigest()) PY重点检查 MIME 的 Content-Type、Content-Disposition、Content-Transfer-Encoding 和真实文件类型。文件名后缀不能作为类型依据;例如 invoice.pdf.exe 在部分界面中可能被截断显示。可以配合 file、xxd 和 unzip -l 做静态检查,不要直接双击样本。技术分析一:SPF 与 DMARC 对齐从可信 Received 头确定源 IP,例如 192.0.2.44;再查询信封域的 SPF。注意 SPF 可能包含 include、redirect 和 DNS 查询次数限制,不能只看第一条 TXT。dig +short TXT example.net dig +short TXT _dmarc.example.com dig +short MX example.com # 若环境安装了 spfquery: spfquery --ip 192.0.2.44 --sender [email protected] --helo mail.example.net假设可见 From 是 [email protected],Return-Path 是 [email protected]。即使 example.net 的 SPF 通过,DMARC 仍要求 example.net 与 example.com 对齐;默认宽松模式按组织域判断,这两个域并不对齐。若 DKIM 的 d=example.com 验证通过且对齐,DMARC 仍可通过;若 DKIM 失败,则 DMARC 应失败。技术分析二:独立验证 DKIM从 DKIM-Signature 提取 d= 和 s=。例如 d=example.com、s=mail2025,则公钥位于 mail2025._domainkey.example.com。先取 DNS 记录,再使用成熟库验证,避免手写规范化逻辑。DKIM 的 relaxed/simple 规范化、重复头字段和折行处理都容易出错。dig +short TXT mail2025._domainkey.example.com python3 -m venv case/venv case/venv/bin/pip install 'dkimpy==1.1.8' case/venv/bin/python - <<'PY' import dkim raw = open('case/work/incident.eml','rb').read() print('DKIM valid:', dkim.verify(raw)) PY如果题目环境不允许联网,应由出题方提供 DNS 快照,并在隔离 DNS 或验证脚本中加载。验证失败时依次检查:原始 EML 是否保持 CRLF;正文是否被网关追加免责声明;签名覆盖的 h= 头字段是否被改写;DNS 中的 p= 公钥是否轮换;邮件是否在导出过程中改变编码。还可以核对 DKIM-Signature 中的 bh=。它是规范化后正文的哈希,而不是整个 EML 的文件哈希。若 bh 不匹配,说明正文在签名后发生变化;若 bh 匹配但签名值 b= 不通过,则更可能是签名头、公钥或被签名头字段的问题。技术分析三:附件线索与 flag 提取在本题中,附件是无害文本容器。先比较三个位置的哈希:原始 EML 中导出的附件、网关日志记录的附件哈希、终端落盘日志中的哈希。三者一致才能把邮件投递与终端行为关联起来。sha256sum case/output/attachments/* file case/output/attachments/* strings -a -n 8 case/output/attachments/* | head -50 # 假设发现 FLAG_B64=Q1RGe2RraW1fYWxpZ25tZW50X2NoZWNrZWR9 printf '%s' 'Q1RGe2RraW1fYWxpZ25tZW50X2NoZWNrZWR9' | base64 -d; echoBase64 只是编码,不是加密。CTF 中经常把它与十六进制、URL 编码或压缩层叠使用。处理时应逐层记录输入输出哈希和使用的命令,避免“解出了字符串”却无法复现。对于未知二进制,不建议直接运行;先做静态类型识别、归档清单和字符串检查。日志关联与投递链还原将 Message-ID、队列 ID、收件人、附件 SHA-256 和时间作为关联键。Message-ID 可由发件端自定义,不能单独作为可信主键;邮件网关队列 ID 通常更适合连接 SMTP 接收、内容扫描和最终投递事件。# 示例字段:time,queue_id,message_id,src_ip,recipient,sha256,action python3 - <<'PY' import csv with open('case/original/gateway.csv', newline='') as f: rows = list(csv.DictReader(f)) for r in rows: if r.get('message_id') == '<[email protected]>': print(r['time'], r['queue_id'], r['src_ip'], r['recipient'], r['sha256'], r['action']) PY终端侧可查询邮件客户端缓存写入、浏览器或解压程序启动、文件创建以及安全产品告警。Windows 环境建议关联 Sysmon Event ID 1(进程创建)、11(文件创建)和 22(DNS);Linux 桌面可结合 auditd、邮件客户端日志和文件系统审计。只凭“用户收到邮件”不能推断附件已打开。证据关键字段可证明内容可信 Received源 IP、时间、队列 ID邮件进入组织边界的路径SPF/DKIM/DMARC域、选择器、对齐结果域级授权与消息完整性附件哈希SHA-256、大小、MIME不同系统中的对象是否一致终端日志用户、主机、进程、文件路径附件是否落盘或被打开常见误判看到 SPF pass 就认定 From 可信:SPF 可能只对齐攻击者控制的 Return-Path 域。直接相信邮件中的 Authentication-Results:应先确定该头是否由可信 MX 写入。用当前 DNS 验证历史邮件:密钥和策略可能已轮换,正式取证需要保留当时的 DNS 或网关验证记录。只按时间关联日志:时区、NTP 漂移和队列延迟都会造成偏差,应叠加队列 ID、哈希和收件人。把 DKIM 通过理解为内容安全:合法域账号被接管后,恶意邮件同样可以得到有效签名。风险与修复建议在边界网关删除外部来源伪造的 Authentication-Results,并由可信网关重新写入;保留原始头供审计。为发信域部署 SPF、DKIM 和 DMARC。先使用 p=none 收集报告,确认所有合法发信源后逐步切换到 quarantine 或 reject。DKIM 私钥应限制读取权限并定期轮换;选择器保留合理过渡期,避免在途邮件因旧公钥立即删除而无法验证。对高风险业务邮件启用外部邮件标识、同形异义域检测和附件类型策略,但不要仅依赖主题关键字。邮件网关至少记录连接 IP、MAIL FROM、Header From、Message-ID、队列 ID、认证结果、附件哈希和处置动作;日志统一时钟并设置防篡改留存。社会工程演练使用专用域、无害附件和预先批准的收件人范围,不采集真实口令。需要验证用户行为时,记录匿名化点击事件即可。总结这类 CTF 的核心不是猜测邮件是否“可疑”,而是把密码学验证与社会工程链路还原结合起来:从可信 Received 边界确定源头,分别验证 SPF、DKIM 与 DMARC 对齐,再用附件哈希、队列 ID 和终端日志证明投递及打开行为。最终报告应明确区分“认证通过”“内容未被修改”“发件人可信”和“附件被执行”四种结论,它们不能互相替代。 服务器、云资产与权限边界梳理示意 日志证据、时间线与事件还原示意
2小时前2小时 这类复盘里最容易误判的一点,是把邮件头中的 From、Received 或“DKIM=pass”单独当成来源结论。更稳妥的做法是先划定可信边界:从收件方邮件网关写入的第一条 Received 开始逆向看,外部邮件自带的更早记录都可以伪造;同时对照 Authentication-Results 中的 header.d、header.from 和 smtp.mailfrom,判断 DKIM 域、可见发件域、Envelope-From 是否对齐。DKIM 通过只说明签名覆盖的字段和正文未被修改,并不说明显示名称或整封邮件可信。 建议在复盘中补一张“字段—证据—结论”表,并保留原始 EML 后离线验证,避免邮件客户端二次解析破坏证据: sha256sum sample.eml swaks --dump-mail < sample.eml opendkim-testmsg -vvv < sample.eml # 提取头部、URL、附件 MIME 信息 python3 -m mailparser -f sample.eml -j > parsed.json grep -Eai '^(received|return-path|message-id|authentication-results|dkim-signature):' sample.eml 如果 DKIM 离线验证失败但网关记录为 pass,先检查邮件是否在收取后被安全网关改写、是否缺少对应时间点的 DNS 公钥,以及 c=relaxed/relaxed 规范化差异;不要直接归因于伪造。DNS 查询最好同时记录 selector、查询时间和 TTL,例如从 DKIM-Signature 取出 s= 与 d= 后查询: dig +dnssec TXT selector._domainkey.example.com 附件部分建议递归拆包,而不是只看文件名和首层哈希。重点检查双扩展名、MIME 类型与 magic 不一致、OOXML 外链关系、LNK 参数、PDF/HTML 中的跳转,以及压缩包内再次落地的脚本: file attachment* sha256sum attachment* 7z l suspect.zip 7z x suspect.zip -oextracted find extracted -type f -exec file {} \; olevba --decode extracted/* exiftool -a -u -g1 extracted/* grep -RInaE 'https?://|powershell|cmd\.exe|mshta|rundll32' extracted 最后还原链路时,时间线应统一到 UTC,并区分“邮件投递时间、附件生成时间、首次解析时间、DNS/域名注册时间、外联时间”。附件元数据只能作为辅助线索,容易被修改;更有证明力的是邮件网关日志、DNS 历史、HTTP 重定向链和沙箱网络流量之间能否相互印证。IOC 发布时也建议标注来源与置信度,尤其不要把共享邮件平台 IP 或 CDN 节点直接定性为攻击基础设施。
创建帐户或登录后发表意见