文章总结: 该文档基于BlackHatUSA2026议题,提出利用GitHub自身事件流构建代码托管EDR以检测供应链攻击。核心观点是攻击者常利用平台允许的动作如移动Tag、修改Workflow进行投毒,传统SIEM仅关注单条事件不够。关键发现包括身份不一致、Tag状态差分和OIDC权限变化是稳定检测模式。可操作建议是建立状态快照、进行身份差分并监控Workflow权限变更。 综合评分: 85 文章分类: 供应链安全,安全运营,威胁情报,安全工具
Black Hat USA 2026:GitHub入侵检测
原创
Max Luo Max Luo
白帽子罗棋琛
2026年8月23日 08:19 广东
在小说阅读器读本章
去阅读
GitHub 正在告诉你仓库被入侵:用事件流构建代码托管 EDR
Black Hat USA 2026 议题笔记:GitHub Can Tell You’re Being Hacked. You’re Just Not Listening: Building EDR for GitHub from Its Own Event Stream
终端 EDR 的基本逻辑,是持续记录进程、文件、网络和身份行为,再从关联事件中识别攻击链。代码仓库同样有进程入口、身份切换、持久化、凭据使用和证据销毁,只是这些动作换成了 Commit、Tag、Workflow、OIDC、Release 和 Webhook。
不少团队已经把 GitHub Audit Log 接入 SIEM,却仍然很难发现供应链攻击。原因通常不是“没有日志”,而是只盯着单条高危事件。攻击者使用的动作往往都是平台允许的:推送 Commit、移动 Tag、修改 Workflow、触发发布、删除运行记录。风险来自对象之间不正常的关系。
公开课件把这套检测思路称为 GitHub EDR:监听平台活动,保存可比较的历史状态,用行为规则检测,再把相关信号拼成调查时间线。
图 1:用 GitHub 自身事件流构建 EDR 的议题课件封面
下面的表结构、查询和接收器代码,是根据课件架构整理的最小实现,不是项目仓库源码。
1、先把 GitHub 当作生产运行环境
对现代软件供应链来说,GitHub 不只是代码存储。它同时承担:
- 身份和授权:用户、GitHub App、Deploy Key、
GITHUB_TOKEN; - 执行环境:GitHub Actions Runner;
- 云身份交换:OIDC 联邦和 Trusted Publishing;
- 制品发布:Release、Package、Action Tag;
- 审批与变更:Pull Request、Branch Protection、Environment;
- 取证记录:Workflow Run、Commit Graph、Webhook 和 Audit Log。
这意味着仓库安全不能只检查默认分支上的代码 diff。攻击者可以不改业务代码,通过移动 Release Tag、污染 Actions 缓存、覆盖 Workflow、伪造作者元数据或者让 OIDC 工作流在错误上下文中运行,完成同样的供应链投毒。
图 2:课件归纳的六类 GitHub 攻击技术
课件把常见手法分为六组:制品投毒、OIDC 滥用、Workflow 注入与触发器滥用、身份和 Commit 元数据伪造、证据销毁,以及通过 Fork 或游离对象注入恶意代码。
检测设计应围绕这些 TTP,而不是围绕某一次事件的 IOC。仓库名、攻击账号和恶意 Hash 会变,Tag 批量移动、作者与实际推送者不一致、发布工作流突然获得 id-token: write 等行为模式更稳定。
2、Git 元数据不是认证身份
Git Commit 中的 author.name、author.email、时间戳、Committer、Message、Tree 和 Parent 都可以由提交者设置。GitHub UI 可能根据邮箱把 Author 显示成一个真实账号,甚至带头像和可点击链接,但这不证明该账号完成了推送。
真正经过 GitHub 身份认证的,是事件中的 Pusher 或 Sender。
图 3:应比较可伪造的 Commit Author 与经过平台认证的 Pusher
数据入库时必须同时保留两类身份:
sql
CREATE TABLE github_commit_event ( delivery_id TEXT PRIMARY KEY, repo_full_name TEXT NOT NULL, commit_sha TEXT NOT NULL, author_name TEXT, author_email TEXT, author_login TEXT, committer_login TEXT, pusher_login TEXT NOT NULL, sender_login TEXT NOT NULL, ref TEXT NOT NULL, received_at TIMESTAMPTZ NOT NULL, raw_payload_sha256 TEXT NOT NULL ); CREATE INDEX ON github_commit_event (repo_full_name, received_at); CREATE INDEX ON github_commit_event (author_email, received_at);
最基础的身份不一致检测可以直接写成 SQL:
sql
SELECT repo_full_name, commit_sha, author_login, pusher_login, received_at FROM github_commit_event WHERE author_login ISNOT NULLANDlower(author_login) <>lower(pusher_login) AND received_at >= now() -interval'24 hours';
这条规则本身只能作为低置信度信号。机器人代提交、Web UI 合并、自动同步和 Release Bot 都可能造成 Author 与 Pusher 不同。真正可疑的是它与其他条件同时出现:未签名 Commit、直接推送默认分支、修改 .github/workflows/、Pusher 首次出现在该仓库,或者相同伪造邮箱短时间横跨多个无关组织。
跨组织关联还需要调用 Commit Search API 或自建索引。课件给出的思路是:对每个 author != pusher 的事件,统计同一 Author Email 出现在多少个不相关 Owner 中。批量攻击工具复用伪造身份时,这个特征比单仓库告警更有价值。
3、Webhook 不是完整历史,Tag 投毒必须做状态差分
供应链攻击里一个常见动作,是把多个版本 Tag 同时移动到被污染的 Commit。依赖方如果写的是 uses: org/action@v1,下一次运行就会解析到新对象。
GitHub Activity 页面并不完整展示 Tag Push。课件引用的 Webhook 行为还指出:一次推送超过三个 Tag 时,不会为这些 Tag 创建事件。这意味着最需要检测的批量 Tag 移动,恰好可能绕过只依赖 Webhook 的采集器。
图 4:可靠检测需要通过 Tags API 保存并比较 Tag 指向
正确做法是周期性获取 git/refs/tags,建立不可变快照:
sql
CREATE TABLE github_tag_snapshot ( repo_full_name TEXT NOT NULL, tag_name TEXT NOT NULL, target_sha TEXT NOT NULL, observed_at TIMESTAMPTZ NOT NULL, PRIMARY KEY (repo_full_name, tag_name, observed_at) );
下面的查询检测一个采集周期内被移动的 Tag:
sql
WITH ordered AS ( SELECT repo_full_name, tag_name, target_sha, observed_at, lag(target_sha) OVER ( PARTITIONBY repo_full_name, tag_name ORDERBY observed_at ) AS previous_sha FROM github_tag_snapshot ) SELECT repo_full_name, tag_name, previous_sha, target_sha, observed_at FROM ordered WHERE previous_sha ISNOT NULLAND previous_sha <> target_sha;
单个 Tag 移动可能是正常维护。应继续计算四个特征:同一时间窗内移动的 Tag 数量、是否集中指向同一 Commit、目标 Commit 是否可从受保护分支到达、移动后是否迅速回滚。最后一种“flip-flop”常用于投毒后恢复现场。
4、OIDC 让 Workflow 本身成为身份
过去攻击发布链路,常见目标是仓库 Secret 中的 NPM_TOKEN。使用 OIDC Trusted Publishing 后,长期 Secret 减少了,但攻击目标发生了变化:不再需要窃取 Token,只要让一个拥有 id-token: write 的 Workflow 在攻击者可控的上下文中运行。
图 5:需要检测新增或被修改的 OIDC 授权工作流
因此,检测对象要从 Secret Access 扩展到 Workflow 身份边界:
yaml
name:publishon:push:tags: ["v*"] permissions:contents:readid-token:writejobs:release:environment:productionruns-on:ubuntu-latest
上面的配置并非天然不安全,但任何涉及以下变化的 Commit 都应提高风险分:
- 新增
id-token: write; - 触发器从受保护分支扩大到任意 Tag、Fork PR 或手工输入;
- 删除
environment审批; - 将
pull_request改成pull_request_target,同时 Checkout 不可信 PR 内容; - 发布 Job 使用可变第三方 Action Tag;
- Workflow 由直接 Push 修改,而不是经过受保护 PR。
可以在入库的 Workflow 版本表上做差分:
sql
SELECT current.repo_full_name, current.path, current.commit_sha, current.pusher_login FROM workflow_version currentJOIN workflow_version previous ON previous.repo_full_name = current.repo_full_name AND previous.path = current.path AND previous.version = current.version -1WHERE current.has_oidc_write =trueAND previous.has_oidc_write =false;
云侧也要把 OIDC Subject、Repository、Ref、Workflow Path 和 Environment 写入访问日志。只在 GitHub 侧看到 Workflow 修改,却无法关联它最终换取了哪个云角色,调查链仍然是不完整的。
5、采集层必须同时有实时流、快照和对象检查
课件给出的 GitHub EDR 架构分成四步:Collect、Enrich、Detect、Act。Webhook Receiver、Snapshot Collector 和 CLI 负责采集,PostgreSQL 保存事件与上下文,SQL 查询运行行为规则,前端和 CLI 输出时间线。
图 6:采集、状态存储、检测与调查分层
三个数据源各自有明显缺口:
图 7:Webhook、GitHub API 和 Git 对象检查需要互相补位
Webhook 实时、字段丰富,但可以被禁用,接收失败后的事件也可能永久丢失。接收器首先要验证签名并对 Delivery ID 去重:
python
import hashlib import hmac defverify_webhook(secret: bytes, body: bytes, signature: str) -> bool: ifnot signature.startswith("sha256="): returnFalse expected = hmac.new(secret, body, hashlib.sha256).hexdigest() supplied = signature.removeprefix("sha256=") return hmac.compare_digest(expected, supplied)
GitHub API 适合周期性快照和基线比较,不容易被攻击者关闭成“采集通道”,但 API 对象可能已经被删除,字段比 Webhook 少,还受 Rate Limit 约束。
Git Inspect 直接读取 Commit、Tree、Tag 和可达性关系,适合判断 Tag Provenance、游离 Commit 和不可能的 Parent 时间线。代价是需要 Clone 或对象抓取,不实时,成本也高。
因此,采集策略可以按层次安排:Webhook 负责低延迟信号;API 快照负责补洞和状态差分;只有命中候选规则的仓库再执行 Git Inspect。这样既保留证据,也避免对所有仓库持续全量 Clone。
Webhook 原始载荷不要只解析后丢弃。至少保存 Body Hash、Delivery ID、Event Type、接收时间和对象存储引用,以便在解析器升级后重新回放。
6、数据模型要能回答“之前是什么”
传统 SIEM 常把 GitHub 事件拍平成一行 JSON。这样可以搜索“谁删除了 Workflow Run”,却无法稳定回答:Tag 以前指向哪里、这个 Pusher 是否第一次修改发布 Workflow、当前 Release Asset 是否与昨天相同。
GitHub EDR 的核心不是告警规则数量,而是保存活动记忆。建议至少维护这些状态表:
text
repository_baseline 默认分支、保护规则、Webhook、成员和 App tag_history Tag -> Commit 的时间序列 commit_identity Author、Committer、Pusher、签名和可达性 workflow_version YAML 摘要、触发器、权限、Action 依赖 workflow_run 触发 Ref、Actor、OIDC、Artifact、删除状态 release_history Tag、Asset 摘要、签名、创建者和修改时间 finding 规则、证据引用、风险分和处置状态
仓库快照还应带采集批次号。只有同一批次完整结束后才参与差分,避免 API 分页失败造成“对象被删除”的假阳性。
7、复合规则比单条高危规则更实用
课件展示的一个例子是:
- 多个 Tag 同时指向同一 Commit,单独看是低危;
- Tag 指向不在任何分支上的 Commit,单独看是中危;
- 两者在同一仓库和时间窗内同时发生,升级为高危“批量 Tag 投毒到不可达 Commit”。
图 8:低、中置信度信号组合成高置信度攻击行为
SQL 可以直接表达这种相关性:
sql
SELECT burst.repo_full_name, burst.target_sha, burst.moved_tag_count, reach.is_reachable_from_default FROM tag_move_burst burst JOIN commit_reachability reach ON reach.repo_full_name = burst.repo_full_name AND reach.commit_sha = burst.target_sha WHERE burst.moved_tag_count >=4AND reach.is_reachable_from_default =falseAND burst.window_start >= now() -interval'15 minutes';
阈值不能从示例直接复制。大型 Monorepo、自动 Release 工具和版本回填任务可能正常移动多个 Tag。更稳妥的基线是每仓库统计过去 30 天的 Tag 变更分布,再对首次出现、超过历史高分位且同时具备不可达 Commit 的事件升级。
同样的复合思路还适用于:
- Workflow 新增 OIDC 权限 + 直接 Push 默认分支 + 随后触发发布;
- Author 与 Pusher 不同 + 相同伪造邮箱跨组织出现 + Commit 未签名;
- 发布 Workflow 运行 + 日志随后删除 + Release Asset Hash 改变;
pull_request_target读取 Fork 代码 + 使用可写 Token + 命中共享 Cache Key。
8、检测规则必须经过事件回放和噪声实验
课件没有只展示规则,还给出了一套验证过程:把已知事件重建成仿真场景,再用 GH Archive、GitHub API 和 Git Inspect 评估字段可用性、正常环境出现频率和已知事件召回率。
图 9:候选规则先经过事件仿真、数据补全和正常流量测量
每条规则至少需要四组固定样本:
yaml
rule_id:github.mass_tag_to_unreachable_commitpositive_fixtures:-mass_tag_force_move_to_dangling_commit-temporary_tag_swap_then_restorenegative_fixtures:-release_tool_creates_new_version_tags-repository_migration_rewrites_historyrequired_enrichment:-tag_snapshot-commit_reachabilitymetrics:-incident_recall-findings_per_1000_repositories-enrichment_api_cost
如果公共事件流缺少规则字段,应先通过 API 或 Git 对象补全,再测噪声;不要用缺字段的样本得出“零误报”。候选规则的结果不止保留“启用/禁用”,还可以选择降级、加 Allowlist、只做 Correlator,或者仅在高价值仓库启用。
9、Hardening 仍然优先于 Detection
检测不能替代平台加固。课件列出了 GitHub 在 2026 年前后增加或强化的多项机制,包括不可变 Release、Workflow 执行保护、actions/checkout 对特权 Workflow 获取 Fork PR 代码的限制,以及非协作者运行使用只读 Cache Token 等。
无论具体功能状态如何变化,企业侧都应先完成这些基础控制:
- Release 使用不可变资产和签名证明;
- 默认分支和
.github/workflows/目录要求 Code Owner 审批; - OIDC Trust Policy 绑定 Org、Repo、Ref、Workflow 和 Environment;
- 第三方 Action 固定到完整 Commit SHA,而不是可移动 Tag;
- GitHub App 使用最小权限并定期复核安装范围;
- Webhook 配置、Branch Protection 和 Environment 进入配置漂移监控;
- 关键仓库保留外部事件副本,避免平台内证据被删除后无从恢复。
Detection 的作用,是发现控制被绕过、身份被合法滥用以及历史状态发生异常变化。不能把没有告警当成加固已经完成。
10、落地时先回答五个问题
准备建设 GitHub EDR 时,先确认:
- 是否同时采集 Webhook、API 快照和必要的 Git 对象,而不是只接 Audit Log;
- 是否保留历史状态,可以比较 Tag、Workflow、Release 和权限的前后变化;
- 是否区分 Git 元数据身份与 GitHub 认证身份;
- 是否能把 GitHub Workflow 关联到最终换取的云或制品仓库身份;
- 是否用仿真与正常数据共同验证规则,而不是看到 SQL 能返回结果就上线。
课件中的实现使用 PostgreSQL 保存统一视图,并通过 22 条规则和 12 条 Beta 规则覆盖多类供应链 TTP。这个数字本身不重要。更值得复用的是数据思路:实时事件负责告诉你“刚才发生了什么”,快照负责告诉你“对象变成了什么”,Git 图负责告诉你“这个对象从哪里来”。三者合起来,才具备接近 EDR 的调查能力。
资料来源
- Itay Weinberger、Yair Mizrachi,Black Hat USA 2026:《GitHub Can Tell You’re Being Hacked. You’re Just Not Listening: Building EDR for GitHub from Its Own Event Stream》
- Black Hat 官方 Session 页面
原始会议材料(仓库内)
- 演讲课件 PDF
开源资料与原始议题 PDF
本文对应的 Markdown 原稿、Black Hat 原始议题 PDF 与配图已整理到 GitHub,可按文章编号查找和下载。
https://github.com/cybermaxluo/black-hat-usa-2026-talks
也可以点击文末“阅读原文”进入仓库。欢迎 Star、提交 Issue 或参与勘误。
免责声明:
本文所载程序、技术方法仅面向合法合规的安全研究与教学场景,旨在提升网络安全防护能力,具有明确的技术研究属性。
任何单位或个人未经授权,将本文内容用于攻击、破坏等非法用途的,由此引发的全部法律责任、民事赔偿及连带责任,均由行为人独立承担,本站不承担任何连带责任。
本站内容均为技术交流与知识分享目的发布,若存在版权侵权或其他异议,请通过邮件联系处理,具体联系方式可点击页面上方的联系我。
本文转载自:白帽子罗棋琛 Max Luo Max Luo《Black Hat USA 2026:GitHub入侵检测》
版权声明
本站仅做备份收录,仅供研究与教学参考之用。
读者将信息用于其他用途的,全部法律及连带责任由读者自行承担,本站不承担任何责任。








评论