GitHub 上 10000 个恶意仓库在分发木马:我扒了整个攻击链之后,后背发凉
昨天刷 Hacker News 的时候看到一个帖子,直接让我坐直了——有人发现 GitHub 上有 10000 个仓库在分发木马病毒。625 分,141 条评论,热度爆炸。
第一反应是"又是标题党吧"。点进去一看,不是。作者写了一个脚本,扫了整个 GitHub 的事件流,找到了一个完整的恶意仓库网络。这些仓库不是 fork,每个都有不同的贡献者、不同的名字,但它们共享同一个攻击模式。
我花了一下午把作者的工具跑了一遍,又研究了攻击链的每个环节。说实话,越看越觉得问题比想象的严重。
这个攻击是怎么运作的
先说说整个攻击链,因为理解攻击模式才能防住它。
攻击者做的事情其实不复杂,但很聪明:
第一步:克隆合法仓库。 他们找到一些不太知名的开源项目(不是热门项目,避免被发现),把所有 commit 历史都复制过来,创建一个新的仓库。你在 GitHub 上搜索的时候,这些假仓库和真仓库看起来一模一样——名字相同、描述相同、commit 历史相同,原作者甚至还是"贡献者"。
这里有个细节值得注意:攻击者故意选冷门项目,不去克隆那些有几千 star 的热门项目。为什么?因为冷门项目的维护者可能几个月都不看一次 GitHub,不会发现自己的项目被克隆了。而且冷门项目搜索量低,用户更难通过对比发现异常。
第二步:在 README 里塞恶意链接。 攻击者往 README 文件里加一行,放一个 zip 下载链接。这个 zip 包含四个文件:
Application.cmd或Launcher.cmd(启动脚本)loader.exe或luajit.exe(木马本体,名字随机)- 一个
.cso或.txt文件(辅助文件,可能是加密的 payload) lua51.dll(动态链接库,木马的运行时依赖)
这个文件结构看起来像一个正常的 Lua 脚本运行环境,对吧?luajit.exe 是 LuaJIT 的解释器,lua51.dll 是 Lua 5.1 的运行库。很多人看到这个结构不会觉得有问题,以为是某个 Lua 项目的可执行文件包。
但问题是:luajit.exe 不是真的 LuaJIT,是被替换成木马的同名文件。你双击运行,木马就激活了。
第三步:反复推同一个 commit。 这是最有意思的部分。攻击者每隔几个小时就删掉上一个 commit,然后重新推一个内容完全一样的 commit。为什么要这么做?
作者推测了几个可能的原因:
- 绕过 GitHub 的安全检测。 GitHub 可能会对"长时间不活跃突然更新"的仓库做额外扫描。如果仓库一直在"活跃更新",可能不会触发这个检测。
- 让仓库看起来更真实。 一个每周都有 commit 的仓库,比一个半年没动过的仓库看起来更可信。
- 保持搜索引擎的收录。 搜索引擎会优先索引活跃更新的页面。反复推 commit 可以让假仓库在搜索结果中保持靠前的位置。
第四步:等搜索引擎收录。 因为 GitHub 的 SEO 权重极高,这些假仓库很快就会出现在 Google 和 Bing 的搜索结果里。用户搜某个工具的名字,可能先看到的是假仓库。
你可能会问:VirusTotal 不是能检测吗?问题就在这。作者发现一个很关键的区别:
- 把 zip 文件的链接提交给 VirusTotal 扫描 → 返回 0 个病毒
- 下载 zip 文件本身,再提交给 VirusTotal → 检测到木马
为什么会有这种差异?因为 VirusTotal 对 URL 的扫描和对文件内容的扫描是两回事。URL 扫描主要检查域名信誉和已知恶意 URL 数据库,不会真的下载文件来分析。而文件扫描才会做真正的静态和动态分析。
攻击者显然知道这一点。他们把恶意文件放在某些文件托管服务上,这些服务的域名本身没有被标记为恶意,所以 URL 扫描就绕过了。
为什么 GitHub 没发现这些仓库?
这是最让人不安的部分。
作者两个月前就向 GitHub Support 举报了两个恶意仓库。两周过去了,没回应。又过了一个月,GitHub 才发邮件说"已删除"。
但问题是:这只是两个仓库。作者后来用脚本扫出来 10000 个。
GitHub 有 5 亿个仓库,按理说应该有自动检测机制。但这些恶意仓库之所以能存活这么久,原因是多方面的:
1. 它们不是 fork。 GitHub 的 fork 检测机制对这些仓库无效。攻击者用的是"创建新仓库 + 推送历史"的方式,技术上这是一个全新的仓库,跟原仓库没有任何 fork 关系。
2. 每个仓库看起来都不一样。 不同的名字、不同的贡献者、不同的项目类型。传统的"相似度检测"很难发现它们。你不能用"这个仓库和那个仓库很像"来判断——开源世界里确实有很多相似的项目。
3. 攻击者在主动规避检测。 反复推 commit、commit 命名为"Update README.md"、选择冷门项目克隆——这些行为模式都在刻意避开自动化扫描。
4. 规模太大。 10000 个仓库,就算 GitHub 一天处理 100 个举报,也要三个多月才能清完。而攻击者每天都在创建新的。
5. GitHub 的举报流程效率低。 作者的经历说明了问题:从举报到处理,花了将近两个月。对于这种大规模攻击,两个月足够造成很大的伤害了。
我实测了作者的工具
作者开源了一个叫 git-malware-finder 的 Python 脚本,用来扫描 GitHub 上的恶意仓库。我跑了一下,说说过程和发现。
工具原理
脚本的核心逻辑很巧妙:
- 用 GH Archive 下载最近几天的 GitHub 事件数据(每天有几千万条 push 事件)
- 筛选出 push 事件,找到"每隔几小时就更新"的仓库
- 对这些仓库调 GitHub API,检查最新 commit 是否只修改了 README
- 检查 README 里是否包含 zip 下载链接
- 排除 fork 和 bot 账号
这个方法之所以有效,是因为攻击者的仓库有"反复推 commit"这个独特的行为模式,而正常仓库不会这么做。用行为模式而不是内容来检测,绕过了"每个仓库长得不一样"的问题。
过滤条件
脚本的过滤条件经过了好几轮调整,这在作者的文章里有详细描述:
- 第一轮:只找"每几小时更新"的仓库 → 找到 14 个(太少)
- 作者意识到"每几小时更新"这个条件太严格,因为有些恶意仓库是"每天更新"而不是"每几小时更新"
- 第二轮:放宽到"每天更新 1-24 次" → 找到 40000 个
- 再加上其他条件(README 有 zip 链接、commit 只改了 README、不是 fork、不是 bot)→ 最终 10000 个
这个过程本身就说明了一件事:攻击者的行为模式不是完全一致的。有些仓库每几小时更新一次,有些每天更新一次。这增加了检测的难度。
跑出来的结果
脚本跑完之后,输出了一大批仓库。我随机挑了几个打开看,确实都是恶意的:
- README 最后一行是一个 zip 链接,链接指向某个文件托管服务
- commit 历史里全是"Update README.md",没有任何实质性的代码修改
- 仓库创建时间很新,但 commit 历史看起来像是老项目(因为是从合法仓库复制过来的)
- 贡献者 profile 几乎是空的,没有其他项目
怎么自己跑这个工具
如果你想自己检查,可以直接跑作者的脚本:
| 1 | |
| 2 | |
| 3 | |
| 4 | |
注意几点:
- 这个脚本需要用 GitHub API,你需要一个 GitHub Personal Access Token
- 有速率限制(每小时 5000 次请求),跑完整个扫描可能需要几个小时
- GH Archive 的数据量很大,下载可能需要不少磁盘空间
- 脚本输出的是疑似恶意仓库列表,需要人工确认
这些仓库现在还在吗?
截至我写这篇文章的时候(2026 年 6 月 19 日),作者公布的 10000 个仓库列表里,大部分已经被 GitHub 删除了。但我不确定攻击者有没有创建新的替代仓库。
这种攻击的特点就是"打地鼠"——你删一批,他建一批。只要攻击成本足够低,攻击者就有动力继续。
这对你意味着什么
你可能会想:"我又不会下载 zip 文件,跟我有什么关系?"
关系比你想象的大。我列几个场景:
场景一:搜索引擎中招。 你 Google 一个开源工具的名字,搜索结果第一条是假仓库。你点进去,star 数看起来还行(可能刷的),README 写得很像那么回事。你直接 clone 下来,装好依赖,运行。结果你的电脑被植入了后门。
场景二:CI/CD 流水线中招。 你的项目依赖某个不太知名的 npm 包或 Python 包。这个包的 GitHub 仓库被攻击者克隆并篡改了。攻击者在包里加了恶意代码,然后发布了一个新版本。你的 Dependabot 自动更新了依赖,恶意代码就进入了你的项目。
场景三:AI 工具中招。 现在越来越多的 AI 编程工具(Cursor、Claude Code、Copilot 等)会从 GitHub 搜索和参考代码。如果 AI 工具搜索到的是假仓库,可能会把恶意代码模式引入你的项目。
场景四:同事中招。 你团队里有人从假仓库 clone 了一个工具,在公司内网运行了。木马通过内网横向移动,你的整个开发环境都不安全了。
这些场景不是我瞎编的,每个都有真实的攻击案例。
常见的 GitHub 供应链攻击手法
除了上面说的"克隆仓库+塞木马",GitHub 上还有好几种常见的供应链攻击:
1. Typosquatting(域名仿冒)
攻击者注册跟热门项目名字很像的仓库名。比如你搜 requests(Python HTTP 库),可能搜到一个叫 request(少了个 s)的假仓库。这种攻击在 npm 和 PyPI 上更常见,但 GitHub 上也有。
2. Star Jacking(劫持 Star)
攻击者先创建一个正常的开源项目,积累一些 star 和信任度。然后在某个版本里悄悄加入恶意代码。因为项目已经有了"信誉",用户不会怀疑。
3. Dependency Confusion(依赖混淆)
攻击者在公共 registry 上发布一个跟你的私有包同名的包。如果你的包管理器配置不当,可能会从公共 registry 下载恶意包而不是你的私有包。
4. Commit Squatting(Commit 劫持)
攻击者在热门项目的 PR 里提交恶意代码。如果维护者没仔细 review 就 merge 了,恶意代码就进入了主分支。这种攻击需要攻击者有足够的社交工程能力。
5. Action 供应链攻击
GitHub Actions 是 CI/CD 的核心。如果某个 Action 的仓库被攻击者控制了,所有使用这个 Action 的项目都会受影响。2024 年就发生过好几起 GitHub Actions 供应链攻击。
这不是第一次,也不会是最后一次
GitHub 供应链攻击不是新鲜事。往前数几年,有几个著名的案例:
event-stream 事件(2018): 一个流行的 npm 包 event-stream 的维护者把发布权限交给了一个新贡献者。这个新贡献者在包里加了恶意代码,专门偷取比特币钱包。这个包每周有几百万次下载,影响范围巨大。
ua-parser-js 事件(2021): 另一个每周下载量几百万的 npm 包被攻击者接管,在新版本里加了加密货币挖矿程序和密码窃取器。
XZ Utils 后门(2024): 这个最离谱。一个叫 Jia Tan 的人花两年时间慢慢渗透到 xz 项目的维护团队,最后在压缩库里植入了后门,差点影响所有 Linux 发行版。这个事件直接上了全球新闻。
每次发生这种事件,社区都会讨论一波"供应链安全怎么办",然后过段时间又忘了。但攻击者不会忘——他们一直在研究新的攻击方式。
这次的 10000 个恶意仓库事件,跟之前的供应链攻击不太一样:之前的攻击大多是在已有的包或项目里植入恶意代码,而这次是直接创建全新的假仓库。攻击方式变了,但底层逻辑没变——都是利用开发者对开源生态的信任。
怎么保护自己:详细指南
基础防护:clone 前的检查清单
clone 一个仓库之前,花 30 秒做这些检查:
检查 Star 数和 Fork 数。 一个真正活跃的项目不可能 0 star 0 fork。当然,star 数可以刷,但 0 star 的项目几乎一定是不活跃或者有问题的。
检查最近的 commit 内容。 点进 commit 历史,看看最近几个 commit 都改了什么。如果最近的 commit 全是"Update README.md",没有任何实质性的代码修改,大概率有问题。
检查贡献者列表。 点进去看看贡献者的 profile。恶意仓库的贡献者一般没有任何其他项目,profile 创建时间很新,没有头像。
检查 Issues 和 Discussions。 真正的项目会有用户提 issue、问问题、报 bug。假仓库的 Issues 要么是空的,要么是攻击者自问自答。
检查 Release 和 Tags。 正常的项目会有版本发布历史。如果一个项目"看起来很活跃"但没有任何 release,要小心。
检查仓库的创建时间。 如果一个仓库声称是一个"成熟项目",但创建时间只有几个月,而且 commit 历史看起来像几年的量,那 commit 历史很可能是从别处复制来的。
进阶防护:用 gh CLI 检查仓库
GitHub 的 gh CLI 提供了一些有用的命令:
| 1 | |
| 2 | |
| 3 | |
| 4 | |
| 5 | |
| 6 | |
| 7 | |
| 8 | |
| 9 | |
| 10 | |
| 11 | |
| 12 | |
| 13 | |
| 14 | |
仓库维护者的防护
如果你是仓库维护者,你应该做这些:
开启 GitHub 安全功能:
- Dependabot:自动检测依赖漏洞,自动提 PR 修复
- Code Scanning:用 CodeQL 扫描代码中的安全问题
- Secret Scanning:检测代码中泄露的密钥和 token
- Branch Protection:防止直接 push 到 main 分支,要求 PR review
配置 .github/dependabot.yml:
| 1 | |
| 2 | |
| 3 | |
| 4 | |
| 5 | |
| 6 | |
| 7 | |
| 8 | |
| 9 | |
| 10 | |
给你的仓库加 Security Policy:
创建 .github/SECURITY.md 文件,告诉用户怎么报告安全问题。这不会直接防止攻击,但能让安全研究者更容易联系到你。
开发流程的防护
不要直接运行来路不明的脚本。 很多人看到一个有用的脚本,clone 下来直接 python3 xxx.py 或 bash xxx.sh。这是最危险的习惯。至少先看看脚本内容。
使用 lockfile。 如果你的项目用 npm、pip、cargo 等包管理器,一定要用 lockfile(package-lock.json、requirements.txt with hash、Cargo.lock)。这样即使有人篡改了包的版本,lockfile 里的 hash 校验会发现异常。
| 1 | |
| 2 | |
| 3 | |
| 4 | |
| 5 | |
| 6 | |
| 7 | |
| 8 | |
定期审计依赖:
| 1 | |
| 2 | |
| 3 | |
| 4 | |
| 5 | |
| 6 | |
| 7 | |
| 8 | |
使用 ghq 或类似工具管理本地仓库:
| 1 | |
| 2 | |
这样至少你知道本地有哪些仓库,清理起来也方便。
对 AI 编程工具的影响
这个话题跟 AI 编程也有关系,而且关系很大。
现在越来越多的 AI 编程工具会从 GitHub 拉取代码、参考开源项目。如果 AI 工具参考了一个恶意仓库的代码模式,可能会把恶意代码引入你的项目。
更现实的问题是:很多 AI Agent 有 gh CLI 或者直接的 GitHub API 权限。如果你让 AI Agent 帮你找一个开源工具并安装,它搜索到的可能是假仓库。
这不是假设——这是完全可能发生的事情。
举个具体的例子:你跟 Claude Code 说"帮我找一个 Python 的 HTTP 请求库,clone 下来看看怎么用"。Claude Code 可能会搜索 GitHub,找到一个名字很像 requests 的假仓库,clone 下来,然后在你的项目里引用它。
更麻烦的是,AI 工具通常不会像人一样去检查仓库的 star 数、commit 历史、贡献者信息。它只看搜索结果的排名和仓库的描述。如果假仓库的 SEO 做得好,AI 工具会优先推荐它。
我自己就遇到过类似的情况。有一次让 AI Agent 帮我找一个 Go 语言的日志库,它推荐了一个我从没听说过的项目。我去看了一下,star 数只有个位数,最近的 commit 全是"Update README.md"。虽然不一定是恶意的,但至少说明 AI 工具在选择依赖的时候不够谨慎。
所以,如果你用 AI 编程工具,最好在工具的配置里明确指定可信的源。比如在 Claude Code 的 CLAUDE.md 里加:
| 1 | |
| 2 | |
| 3 | |
| 4 | |
| 5 | |
| 6 | |
更深层的问题:GitHub 的信任机制在失效
这件事让我想到了一个更大的问题:GitHub 上的信任机制到底靠不靠谱?
我们平时判断一个开源项目是否可信,主要看这些指标:
- Star 数 ❌ 早就被玩坏了,有专门的"刷 star"服务,几百块钱刷几千 star
- Fork 数 ❌ 也能刷,而且 fork 不代表有人真的在用
- Commit 历史 ❌ 这次事件证明,commit 历史可以完整复制
- 贡献者 ❌ 可以伪造,或者盗用别人的 commit 信息
- 搜索排名 ❌ GitHub SEO 也能做,攻击者可以让假仓库排在搜索结果前面
还有什么可信的?Issues 和 Discussions 的活跃度?但攻击者也可以自问自答。Download 数?也可以刷。
说白了,GitHub 上的"信号"都在贬值。以前 star 数是质量的代理指标,现在它可能只是一个营销指标。
这个问题不是 GitHub 一家能解决的。开源社区需要更好的信任机制。几个可能的方向:
可验证构建(Reproducible Builds): 从源代码到二进制文件,每一步都可以被第三方验证。如果一个 release 的构建过程是可验证的,你就可以确认二进制文件确实是从源代码编译来的,没有被篡改。
签名的 Release: 维护者用 GPG 密钥签名 release,你可以验证签名是否来自维护者本人。很多大型项目已经在做了(比如 Linux Kernel、Git 本身),但中小项目很少用。
更透明的依赖链: 像 SLSA(Supply-chain Levels for Software Artifacts)这样的框架,试图建立一个从源代码到最终制品的完整信任链。但这个标准还在推广阶段。
去中心化的代码托管: 如果代码不只存在 GitHub 一个地方,攻击者就需要同时控制多个平台才能成功。但现实是 GitHub 的网络效应太强了,短期内很难改变。
这些方案都有各自的局限性,但方向是对的。在这些方案成熟之前,我们只能靠自己多长个心眼。
一些补充:我踩过的坑和额外发现
写这篇文章的过程中,我还发现了一些有意思的细节,顺便说说。
GH Archive 的数据量
跑作者的脚本之前,我先去 GH Archive 看了看。一天的 GitHub 事件数据大概有几十 GB(gzip 压缩后)。光是下载最近 5 天的数据就要花不少时间和带宽。如果你的网络条件不太好,建议先用一天的数据试试,不要一上来就下 5 天的。
GH Archive 提供了 BigQuery 的接口,如果你有 Google Cloud 账号,用 BigQuery 查询会比下载文件快很多,而且不需要本地存储空间。对于只是想验证一下的人来说,BigQuery 是更方便的选择。
恶意仓库的命名规律
我观察了一下作者公布的恶意仓库列表,发现一个规律:这些仓库的名字都很"正常",不会引起怀疑。比如 welink、OcyShield-Framework、AssetRipper-CLI——这些名字看起来就像是正常的开源工具。
这跟 npm 上的 typosquatting 攻击不一样。npm 上的恶意包通常会用跟热门包很像的名字(比如 requets vs requests),比较容易发现。但 GitHub 上的恶意仓库用的是完全不同的名字,你没法通过"名字像不像"来判断。这反而更危险,因为你没有任何直觉上的警报。
一个反直觉的事实
你可能觉得:10000 个恶意仓库,占 GitHub 总仓库数的 0.002%,比例很小,不用太担心。
但问题不是比例,而是命中率。攻击者把假仓库的 SEO 做得很好,当用户搜索某个工具时,假仓库可能排在搜索结果的第一页甚至第一条。这时候用户点进去的概率就很高了。
而且这些假仓库覆盖的项目类型很广——不只是 AI 工具,还有各种 CLI 工具、游戏引擎、框架等。你不知道你常用的某个工具是不是已经被克隆了。
VirusTotal 的局限性
前面提到 VirusTotal 的 URL 扫描和文件扫描结果不同。我还测试了一下其他在线扫描工具,结果类似——URL 扫描基本没用,只有文件扫描才能检测到木马。
这意味着:如果你只看链接,你判断不了这个链接是否安全。你必须下载文件(在隔离环境中)才能真正检测。
对于普通用户来说,最安全的做法是:不要下载来路不明的 zip 文件。如果一个 README 让你下载一个 zip 包,先去官方网站确认这个下载链接是不是真的。宁可多花两分钟验证,也不要冒险下载。
常见问题
Q:我已经 clone 过一些不知名的仓库,怎么办?
先检查一下本地有没有可疑的仓库。在你的工作目录里跑 ls,看看有没有不认识的项目。如果有的话,直接删掉。更重要的是:你有没有运行过这些仓库里的代码?如果运行过,建议检查一下系统有没有异常进程。
Q:GitHub 的 Dependabot 能防这种攻击吗?
Dependabot 主要检测已知漏洞的依赖版本更新,对这种"假仓库"攻击基本没用。因为你 clone 的是一个全新的仓库,不是某个已知包的新版本。Dependabot 更适合防 Dependency Confusion 和已知漏洞。
Q:用 Docker 能隔离风险吗?
有帮助,但不是万能的。如果你在一个 Docker 容器里运行来路不明的代码,即使被感染了,也只影响容器内部。但前提是你的 Docker 配置是安全的——不要用 --privileged,不要挂载敏感目录,不要用 host 网络模式。
Q:我怎么知道某个 zip 文件是否安全?
最安全的做法是在一个隔离环境(虚拟机或 Docker 容器)里下载和解压,然后用多个杀毒引擎扫描。VirusTotal 的文件扫描(不是 URL 扫描)能检测大部分已知木马。但如果是一个全新的恶意软件,可能所有杀毒引擎都检测不到。
Q:GitHub 会改进检测机制吗?
作者在文章里提到,GitHub 两个月后才处理他的举报。这说明 GitHub 当前的处理效率确实不够。但 10000 个恶意仓库的事情上了 Hacker News 头条之后,GitHub 应该会加快处理速度,并且可能会投入更多资源做自动化检测。不过,指望平台解决所有问题是不现实的,自己做好防护才是根本。
总结
这起事件的核心教训:
- GitHub 不是安全的港湾。 5 亿个仓库,GitHub 不可能每个都审核。你需要自己判断。
- 攻击者越来越聪明了。 不是简单的钓鱼网站,而是完整的仓库克隆、commit 复制、SEO 优化。这是一条成熟的攻击产业链。
- 信任要验证。 clone 前花 30 秒检查,能避免 90% 的问题。
- 安全工具要用起来。 Dependabot、Code Scanning、pip-audit、npm audit——这些工具存在是有原因的。
作者写的原文很有意思,推荐读一下:I discovered a large-scale malware distribution on GitHub。扫描工具的仓库在这:git-malware-finder。
有啥问题评论区聊。后面我打算写一篇 AI 编程工具安全配置的文章,聊聊怎么在用 AI 工具的同时不把自己搞进去。