kratikajain09/Phishing-Triage-A-Malicious-PDF-Lure-from-a-Trusted-Vendor-Mailbox

GitHub: kratikajain09/Phishing-Triage-A-Malicious-PDF-Lure-from-a-Trusted-Vendor-Mailbox

这是一份详细的钓鱼邮件应急响应案例研究,展示了如何利用 Microsoft Sentinel 和 KQL 调查一封通过身份验证但实际源自被攻陷供应商邮箱的恶意邮件。

Stars: 0 | Forks: 0

# 钓鱼邮件分诊:来自受信任供应商邮箱的恶意 PDF 诱饵 ## TL;DR 一名用户报告了一封带有供应商主题的电子邮件,其中包含一个 PDF 文件,链接到以发票为主题的登录页面。所有的“安全”信号都指向了错误的方向——**SPF 和 DMARC 均通过,发件人是真实的商业合作伙伴,且威胁情报显示没有不良记录。** 这些确切的信号正是为什么它需要真正的调查而不是快速结案的原因。 这封电子邮件被证实是钓鱼邮件,发自一个**被攻陷的合法供应商邮箱**——后来供应商也证实了这一点。没有内部用户点击,没有凭证被提交,也没有任何账户被攻陷。本案例研究详细介绍了是如何得出这一结论的、用于证明这一点的 KQL、遏制措施序列,以及——最后——如果用户*确实*点击了,调查将如何升级。 **展示的技能:** 电子邮件标头/身份验证分析 · 钓鱼邮件分诊 · 影响范围界定 · 账户攻陷审查 · 在 Microsoft Sentinel / Defender 中使用 KQL · 遏制生命周期 · 供应商协调。 ## 调查概览 ![调查流程](https://static.pigsec.cn/wp-content/uploads/repos/cas/a9/a984ab0e9b67587aa82b9413c0e7ebcaffc9554a6d4baee4275596e2767164de.svg) 上述流程端到端地追踪了该事件:一封被报告的电子邮件、暴露出“来自受信任供应商的干净身份验证”这一矛盾的分诊、为排除影响而进行的影响范围和账户审查、对供应商邮箱被攻陷的外部确认、遏制措施,以及在供应商完成修复后处于监控下的解封。每个阶段的详细信息如下。 ## 环境 | | | |---|---| | **SIEM/XDR** | Microsoft Sentinel + Microsoft Defender for Office 365 | | **警报来源** | 用户报告的钓鱼邮件(通过报告按钮) | | **分析师角色** | SOC L1,分诊与调查 | ## 警报 一名标准企业用户报告了一封可疑的入站电子邮件: - **发件人:** `vendor.sender@fabrikam-travel.example`(显示名称 "External Vendor") - **主题:** `[EXTERNAL] - Document For Review` - **附件:** `Vendor_Document.pdf` — 一个带有“点击此处打开”按钮的文档审查诱饵 - **投递:** 已投递到收件箱,在接收时未被拦截 - **是否首次收到该发件人的邮件?** 否 — 双方存在既定的通信记录 该 PDF 不包含可执行的恶意软件。它是一个**诱饵**:该按钮链接到一个外部登录页面。 ## 为什么这不是一个可以快速结案的事件 处理报告的钓鱼邮件时,直觉是确认其恶意性,将其清除,然后结案。让这起事件值得进行真正调查的原因在于,那些轻易获取的信号全都说它很*安全*: | 信号 | 显示内容 | 为什么这具有误导性 | |---|---|---| | SPF | **通过** | 当邮件从域名的授权基础设施发送时会通过——包括该基础设施上*被攻陷*的邮箱 | | DMARC | **推测通过** | 对齐保持有效;但这并不能说明账户是否被劫持 | | DKIM | **缺失** | 稍微降低了可靠性,但单凭这一点并不具备一票否决权 | | 发件人域名信誉 | **干净** | 合法的企业域名之所以被滥用,恰恰*因为*它们是干净的 | | 业务关系 | **真实的供应商** | 信任是攻击者的资产,而不是一个缓解因素 | **关键的分析举措:** 干净的身份验证**并不能**排除被攻陷但合法的邮箱。来自受信任合作伙伴域名的 SPF/DMARC 通过,正是供应商电子邮件被攻陷的典型特征。这将整个调查的框架从“这是伪造的吗?”(答案:否)转变为“这是一个被劫持的真实账户吗?”(答案:是)。 ## 调查 ### 1. 确认邮件并提取其制品 识别被报告的邮件、其收件人、URL 以及投递处置情况。 ``` // Locate the reported message across the org by sender + subject EmailEvents | where Timestamp > ago(7d) | where SenderFromAddress == "vendor.sender@fabrikam-travel.example" | where Subject has "Document For Review" | project Timestamp, RecipientEmailAddress, SenderFromAddress, Subject, DeliveryAction, DeliveryLocation, AuthenticationDetails, NetworkMessageId | sort by Timestamp desc ``` ### 2. 界定影响范围——还有谁收到了它,有人点击吗? 在“它是恶意的吗?”之后,L1 最重要的问题是:**它传播了多远,有人与它交互了吗?** ``` // Pull URLs embedded in the campaign messages EmailUrlInfo | where NetworkMessageId in ( EmailEvents | where SenderFromAddress == "vendor.sender@fabrikam-travel.example" | where Subject has "Document For Review" | distinct NetworkMessageId ) | project NetworkMessageId, Url, UrlDomain ``` ``` // Did ANY internal user click the campaign URLs? (URL click telemetry) UrlClickEvents | where Timestamp > ago(7d) | where Url has_any ("document-hosting.example", "tracking-example.net") | project Timestamp, AccountUpn, Url, ActionType, IPAddress ``` ### 3. 排除投递后的账户被攻陷 即使没有点击,也要验证报告的收件人(以及任何其他收件人)是否显示出被攻陷的迹象。 ``` // Sign-in review for the recipient — look for anomalies, impossible travel, MFA failures SigninLogs | where TimeGenerated > ago(7d) | where UserPrincipalName == "user1@contoso.example" | project TimeGenerated, ResultType, AppDisplayName, IPAddress, Location, AuthenticationRequirement, RiskLevelDuringSignIn | sort by TimeGenerated desc ``` ``` // Check for suspicious mailbox rule creation / forwarding — classic BEC persistence OfficeActivity | where TimeGenerated > ago(7d) | where UserId == "user1@contoso.example" | where Operation in ("New-InboxRule", "Set-InboxRule", "Set-Mailbox") | project TimeGenerated, Operation, UserId, Parameters ``` ### 4. 分析更广泛关系的邮件流 ``` // Historical inbound/outbound with this sender and domain — establish what "normal" looked like EmailEvents | where Timestamp > ago(30d) | where SenderFromAddress == "vendor.sender@fabrikam-travel.example" or RecipientEmailAddress endswith "@fabrikam-travel.example" | summarize Messages = count() by bin(Timestamp, 1d), Direction = iff(SenderFromAddress has "fabrikam-travel", "Inbound", "Outbound") | sort by Timestamp desc ``` ### 5. 与业务部门确认,然后与供应商核实 威胁情报显示该域名是干净的,因此确认必须来自人工,而不是工具: - 内部利益相关者确认 Fabrikam 是合法的、预期的联系人。 - 但他们也证实,该供应商的**电子邮件内容和行为是异常的**。 - 一名业务联系人向供应商上报了此事,供应商**证实发送邮箱已被攻陷。** 该外部确认使整个事件形成了闭环:这是来自被劫持的真实邮箱的钓鱼邮件,而不是伪造的。 ## 遏制 采取的行动(顺序很重要——在采取行动之前先界定范围): 1. **在 Web/电子邮件安全层拦截了恶意 URL**。 2. **作为立即遏制措施,拦截了发件人地址**。 3. **一旦怀疑供应商被攻陷,暂时拦截了发件人域名**——考虑到真实的业务关系,这是经过深思熟虑的、影响较大的步骤。 4. **清除/隔离了所有收件人的相关邮件**。 5. **重新验证**了全组织范围内没有点击活动且没有凭证被提交。 6. **请求供应商提供修复确认。** 然后是将“拦截并遗忘”与生命周期处理区分开来的部分: 7. 在供应商确认完成修复(会话被吊销、密码重置、收件箱/转发规则已审计、可疑会话被拦截)后,**解除了域名拦截,并在一个较短的时间窗口内对发件人进行了监控**。 8. 未观察到进一步的可疑活动 → **调查结束。** ## 时间线 | 时间 | 事件 | |---|---| | T+0 | 用户报告带有 PDF 和外部 URL 的供应商主题电子邮件 | | T+0 | 审查了电子邮件、附件、URL、标头、身份验证结果 | | T+0 | 检查了 URL 点击遥测数据 — **无点击** | | T+0 | 审查了收件人账户 — 无被攻陷迹象 | | T+1 | 内部验证了供应商的业务关系 | | T+1 | 拦截了恶意 URL 和发件人地址 | | T+2 | 怀疑供应商邮箱被攻陷;拦截了发件人域名 | | T+3–T+5 | 与业务部门及供应商联系人进行额外验证 | | T+6 | 供应商确认修复完成 | | T+7 | 解除发件人域名拦截,进行监控 | | T+9 | 无进一步活动 — 调查结束 | ## 结果 - 没有内部用户点击恶意 URL。 - 未观察到凭证提交。 - 无收件人账户被攻陷。 - 无可疑登录;全程启用了 MFA。 - 供应商确认被攻陷的邮箱已完成修复。 这是一次“险些发生”的事故,并且依然停留在“未发生”状态,**因为**“安全”信号被视为展开调查的提示,而不是结案的理由。 ## 我会采取的改进措施/经验教训 - **干净的身份验证是开始的问题,而不是答案。** 受信任合作伙伴域名的 SPF/DMARC 通过,是*典型的*供应商被攻陷特征。 - **信任就是攻击面。** 既有的关系使诱饵变得可信;对于表现出异常行为的既有发件人,理应受到*更多*审查,而不是更少。 - **证明否定结论。** “没有人点击”只有在你能展示出证明这一点的查询时,才值得陈述。 - **遏制具有生命周期。** 拦截真实的合作伙伴域名具有破坏性;将其与确认修复后处于监控下的解封相配合,可以在安全性和业务连续性之间取得平衡。 ## 附录:升级分支 — *如果用户点击了*(假设情况) 如果 `UrlClickEvents` 返回了内部用户的命中记录,调查将从“界定和遏制诱饵”转向“在排除嫌疑之前,假设存在潜在的凭证泄露”: 1. **确定点击行为和用户。** 捕获时间戳、源 IP、设备,以及点击是否到达了凭证收集页面(重定向链),还是停留在第一个登录页面。 2. **假设凭证可能已被提交。** 立即将该账户视为处于风险中,而不是等待证据。 3. **追踪凭证提交和会话滥用:** ``` // Successful sign-ins shortly after the click, especially from new IPs/locations SigninLogs | where TimeGenerated > ago(2d) | where UserPrincipalName == "user1@contoso.example" | where ResultType == 0 | project TimeGenerated, IPAddress, Location, AppDisplayName, RiskLevelDuringSignIn, AuthenticationRequirement | sort by TimeGenerated asc ``` ``` // Post-click persistence: new inbox rules or forwarding (BEC hallmark) OfficeActivity | where TimeGenerated > ago(2d) | where UserId == "user1@contoso.example" | where Operation in ("New-InboxRule", "Set-InboxRule", "Set-Mailbox") | project TimeGenerated, Operation, Parameters ``` 4. **遏制身份,而不仅仅是电子邮件:** 强制重置密码,**吊销活动会话/token**(仅靠修改密码无法阻止 token 被盗用),并审查 MFA 注册信息,以排查攻击者添加的方法。 5. **将影响范围扩大**到所有点击了链接的收件人,将每个人视为一起独立的身份事件。 6. **带着账户被攻陷的发现和清晰的时间线升级给 L2 / IR 处理**。 **为什么 token 吊销的细节很重要:** 现代网络钓鱼越来越多地窃取会话 token,因此仅仅重置密码可能仍然会让攻击者保持一个活跃的会话。吊销会话是初级人员最常遗漏的步骤——这正是为什么它值得在此处记录下来的原因。
标签:KQL, 威胁分析, 安全运营, 库, 应急响应, 扫描框架, 自动化侦查工具, 钓鱼邮件