StepSecurity 威胁情报团队率先发现并报告了一场持续进行的攻击活动——我们将其追踪命名为 ForceMemo——攻击者正在攻陷数百个 GitHub 账户,并向数百个 Python 仓库注入相同的恶意程序。最早的注入始于 2026 年 3 月 8 日,该活动仍在活跃,新仓库持续遭到攻陷。
StepSecurity 威胁情报团队发现了一个持续进行的攻击活动,攻击者正在攻陷数百个 GitHub 账户,并向数百个 Python 仓库注入相同的恶意程序。最早的注入可追溯至 2026 年 3 月 8 日,该活动仍在活跃,新仓库不断被攻陷。攻击针对 Python 项目——包括 Django 应用、机器学习研究代码、Streamlit 仪表板和 PyPI 包——通过向 setup.py、main.py 和 app.py 等文件追加混淆代码来实施攻击。任何从被攻陷的仓库运行 pip install 或克隆并执行代码的人都会触发恶意程序。
amirasaran/django-restful-admin(70 星)中被攻陷的 setup.py——混淆的恶意程序被追加在合法文件的末尾。pip install. 或 python setup.py install 会执行恶意程序。
攻击者获取开发者账户的访问权限,取每个仓库默认分支上的最新合法提交,用追加了恶意代码的版本进行变基,然后强制推送——这使得一切看起来好像没有发生变化。提交信息、作者和作者日期都从原始提交中保留了下来。我们已在最受影响的仓库上提交了安全问题,以通知维护者。
这是一场持续进行的攻击活动。我们正在积极追踪此次攻击,并将随着新信息的出现继续更新这篇博文。其中一些仓库可能仍包含恶意代码。如果您使用了直接从 GitHub 安装的 Python 包,请检查默认分支上的代码是否与原始作者的最后一次合法提交相匹配。
受影响仓库的 GitHub 搜索
受影响的仓库完整列表可通过在 GitHub 上搜索该恶意程序的标记变量获得:
amirasaran/django-restful-admin 上的变基提交——提交信息和作者均从合法合并中保留,但提交者日期暴露了实际攻击日期(2026 年 3 月 10 日)。
攻击如何运作
阶段 1:通过 GlassWorm 凭证窃取进行账户劫持
账户劫持机制已被确认:被攻陷的开发者通过恶意的 VS Code 和 Cursor 扩展感染了 GlassWorm 恶意程序。GlassWorm 的第三阶段有效载荷包含一个专用的凭证窃取模块,从多个来源收集 GitHub 令牌:git credential fill、VS Code 扩展存储、~/.git-credentials 和 GITHUB_TOKEN 环境变量。一旦攻击者获取了这些凭证,他们就会用其将恶意程序强制推送到受害者的所有仓库。
一位 Reddit 用户报告称发现"null"已提交到他们的大部分仓库——并将感染追溯到一个被攻陷的 Cursor 扩展。对 GlassWorm 有效载荷的独立分析证实,它会窃取 GitHub 和 npm 凭证,验证 GitHub API,并将其渗透到攻击者控制的服务器。
账户级别攻陷的证据是明确的:当一个拥有多个仓库的账户被攻占时,该账户下的每个仓库都会被注入。例如,用户 BierOne 有 6 个仓库被攻陷,组织 wecode-bootcamp-korea 有 6 个仓库被攻击,HydroRoll-Team 有 6 个。
阶段 2:通过强制推送进行隐蔽注入
注入方法非常复杂。攻击者不是打开拉取请求或创建新提交(这两种方式在仓库的活动动态中都是可见的),而是:
- 取默认分支上的最新合法提交
- 对其进行变基,向关键 Python 文件(
setup.py、main.py、app.py等)追加混淆的恶意程序 - 强制推送到默认分支
提交信息和作者日期均从原始提交中保留——只有提交者日期暴露了篡改行为。在许多恶意提交中,提交者邮箱也被设置为字符串 "null",这似乎是攻击者工具的一个指纹。
以下是我们在最受关注的仓库中发现的时间差异:
amirasaran/request_validator— 作者日期:2017-04-24,提交者日期:2026-03-10(9 年差距)BierOne/relation-vqa— 作者日期:2019-04-11,提交者日期:2026-03-13(7 年差距)BierOne/bottom-up-attention-vqa— 作者日期:2021-06-01,提交者日期:2026-03-13(5 年差距)biodatlab/siriraj-assist— 作者日期:2024-03-19,提交者日期:2026-03-13(2 年差距)amirasaran/django-restful-admin— 作者日期:2024-11-15,提交者日期:2026-03-10(16 个月差距)uknfire/tsmpy— 作者日期:2025-06-04,提交者日期:2026-03-08(9 个月差距)KeithSloan/ImportNURBS— 作者日期:2025-08-28,提交者日期:2026-03-10(6 个月差距)BierOne/ood_coverage— 作者日期:2024-10-25,提交者日期:2026-03-12(5 个月差距)KeithSloan/GDML— 作者日期:2026-02-06,提交者日期:2026-03-11(33 天差距)
GitHub Events API 捕获带有 before 和 after 提交 SHA 的推送事件。对于 amirasaran/django-restful-admin,我们可以看到默认分支被替换的确切时刻:
amirasaran/django-restful-admin 的 GitHub Events API——2026-03-10T21:58:02Z 的 PushEvent 显示强制推送将干净的提交(260ca63)替换为恶意提交(17849e1)。
before SHA(260ca635)是来自 PR #16 的合法合并提交。after SHA(17849e1b)是攻击者的变基提交,恶意程序被追加到 setup.py。由于攻击者使用被攻陷账户的凭证,推送看起来像是来自仓库所有者。
amirasaran/django-restful-admin 上的正常活动——PR #16 被正常合并(提交 260ca635)。攻击者随后用恶意代码对这个合并提交进行了变基,并强制推送到 master。
阶段 3:Solana 区块链 C2
注入的代码被追加到攻击者针对的任何 Python 文件的末尾。它通过三层进行混淆:base64 解码、zlib 解压缩和 XOR 解密(密钥:134)。变量名是随机生成的 15 字符字符串,但 base64 有效载荷 blob 在所有被攻陷的仓库中是相同的,存储在名为 lzcdrtfxyqiplpd 的变量中。
反混淆后,恶意程序执行以下序列:
恶意程序首先检查系统是否为俄语系统——通过检查区域设置、时区和 UTC 偏移量。如果系统是俄语的,则完全跳过执行。这是东欧网络犯罪行动中一个众所周知的模式,目的是避免针对本国系统。
恶意程序不是连接到可能被关闭的传统 C2 服务器,而是从 Solana 区块链读取其指令。它查询特定的 Solana 地址,获取包含带有有效载荷 URL 的 JSON 数据的交易备忘录:
恶意程序尝试 9 个不同的 Solana RPC 端点作为备用,这使得它对任何一个端点被屏蔽都具有极高的适应性:
api.mainnet-beta.solana.comsolana-mainnet.gateway.tatum.iogo.getblock.ussolana-rpc.publicnode.comapi.blockeden.xyzsolana.drpc.orgsolana.leorpc.comsolana.api.onfinality.iosolana.api.pocket.network
使用区块链作为 C2 渠道意味着攻击者可以随时通过发布新交易来更新有效载荷 URL——而且一旦指令上链,任何人都无法删除或审查这些指令。
跨 BierOne 账户搜索标记变量的 GitHub 代码搜索——所有仓库都包含相同的恶意程序,表明存在账户级别的攻陷。
阶段 4:有效载荷执行
恶意程序一旦从 Solana 备忘录中检索到有效载荷 URL,就会:
- 从
nodejs.org下载 Node.js v22.9.0 到用户的主目录(跨平台:Windows/macOS/Linux、x64/ARM) - 从该 URL 获取加密的 JavaScript 有效载荷,在 HTTP 响应头中接收 IV 和密钥
- 编写一个 JS 文件(
i.js),通过下载的 Node.js 解密并执行有效载荷 - 创建一个持久化文件(
~/init.json),并带有 2 天的重新检查计时器以避免重复执行
最终的 JS 有效载荷使用 AES 加密,使得在没有服务器端密钥的情况下无法对第二阶段进行静态分析。基于基础设施模式(Solana C2 + Node.js 执行 + AES 加密 + CIS 排除),这与已知的加密货币钱包窃取器/信息窃取活动一致。
Harden-Runner 分析:在行动中捕获恶意程序
为了确认恶意程序的行为,我们在受控的 GitHub Actions 环境中运行了来自 amirasaran/django-restful-admin 的被攻陷的 setup.py,并使用 StepSecurity Harden-Runner 监控所有网络活动。结果确认了上述描述的完整攻击链。
在执行 python3 setup.py 的几秒钟内,Harden-Runner 从 python3.12 进程中捕获了以下网络活动:
- Solana C2 查询(T+10s)——
api.mainnet-beta.solana.com(208.91.111.195:443)的 DNS 解析——恶意程序正在查询区块链以获取其 C2 指令 - 有效载荷 URL 获取(T+20s)——连接到
217.69.0.159:80——从 Solana 交易备忘录中解码出的 URL - Node.js 下载(T+21s)——
nodejs.org(172.66.128.70:443)的 DNS 解析——下载 Node.js v22.9.0 以执行加密的 JavaScript 有效载荷 - Node.js 已提取(T+24s)——
/home/runner/node-v22.9.0-linux-x64/bin/node已部署——Harden-Runner 自动检测到新二进制文件并附加了 TLS 监控
受控执行的 GitHub Actions 构建日志——在 setup.py --version 输出 1.1.3(合法)之后,恶意程序立即从 nodejs.org 下载 Node.js v22.9.0 并提取到运行器的主目录。完整的工作流运行。
受控执行的 Harden-Runner 网络洞察——恶意程序对 Solana RPC 端点和 nodejs.org 的出站连接清晰可见,同时还有正常的 GitHub Actions 基础设施流量。
这些连接中没有一个属于 Python 项目的 CI/CD 管道。setup.py 没有正当理由联系 Solana RPC 端点、下载 Node.js 或连接到未知 IP。Harden-Runner 的网络出口监控正是标记这种异常活动的工具。将 Harden-Runner 添加到您的工作流中,以在受攻陷的依赖项能够渗透数据之前检测它们。
带有 Harden-Runner 洞察的完整工作流运行可在以下位置查看:StepSecurity Insights Dashboard。
保护您的 CI/CD 管道
StepSecurity 监控来自您 GitHub Actions 运行器的出站网络连接,检测像 ForceMemo 这样的异常活动,并保护您的软件供应链。
Solana C2 链上分析
由于攻击者使用 Solana 区块链作为 C2 渠道,C2 指令的完整历史被永久记录在链上,可供分析。我们查询了 C2 地址 BjVeAjPrSKFiingBn4vZvghsGj9KCE8AJVtbc9S8o8SC 并解码了所有交易备忘录。
C2 地址是一个钱包,而非智能合约
攻击者拥有该地址的私钥,并使用 Solana 的 Memo 程序发布 JSON 指令。受害者机器上的恶意程序通过 Solana RPC 查询该地址,读取最新的备忘录,解码 base64 link 字段,然后连接到结果 URL。区块链只记录攻击者一方——受害者的连接发生在链外,指向有效载荷服务器。
该活动早于 GitHub 注入
C2 地址上最早的交易可追溯至 2025 年 11 月 27 日——距离 2026 年 3 月 8 日的第一次 GitHub 仓库注入已超过三个月。该地址共有 50 笔交易,攻击者定期更新有效载荷 URL,有时每天多次。这表明攻击者在转向 GitHub 仓库之前是针对其他感染途径的。
解码的有效载荷 URL 揭示了 6 个 C2 服务器 IP
每个备忘录包含一个指向当前有效载荷服务器的 base64 编码的 HTTP URL。解码所有备忘录揭示了攻击者在整个活动期间轮换了 6 个不同的服务器 IP:
45.32.151.157— 2025 年 12 月活跃(最早)45.32.150.97— 2026 年 2 月活跃217.69.11.57— 2026 年 2 月活跃217.69.11.99— 2026 年 2 月至 3 月活跃217.69.0.159— 2026 年 3 月 13 日活跃(当前——由 Harden-Runner 确认)45.76.44.240— 2026 年 3 月 13 日活跃
45.x IP 位于 Vultr 托管范围内。217.69.x IP 位于俄罗斯托管范围内——与恶意程序中的 CIS 排除行为和俄语代码注释一致。
链上暴露的直接 C2 配置
2026 年 2 月 25 日的一笔交易包含一个直接 C2 配置,而非有效载荷链接——可能是在测试或重新配置阶段发布的:
这揭示了攻击者基础设施的三个组成部分:端口 5000 上的一个 C2 服务器、一个受害者指纹识别端点(可能在提供有效载荷之前检查受害者的 IP 和地理位置)以及端口 10000 上的一个 DHT(分布式哈希表)节点——这是一个点对点的备用方案,使 C2 基础设施对查封更具抵御能力。
资金追踪
C2 地址于 2025 年 11 月 27 日由钱包 G2YxRa6wt1qePMwfJzdXZG62ej4qaTC7YURzuh2Lwd3t 注资,该钱包目前持有约 495 SOL。C2 地址本身仅持有 0.006 SOL——刚好够支付发布备忘录更新的交易费用。
与 GlassWorm 活动的关联
ForceMemo 使用的 Solana C2 地址——BjVeAjPrSKFiingBn4vZvghsGj9KCE8AJVtbc9S8o8SC——与 GlassWorm 恶意程序活动使用的钱包地址相同,后者是一种自传播蠕虫,自 2025 年 10 月起一直针对 VS Code 和 OpenVSX 扩展。这首次由 Koi Security 在其对 GlassWorm 第四波(2025 年 12 月)的分析中记录,该波转向 macOS 并使用特洛伊化的加密货币钱包。
重叠的不仅仅是钱包。Aikido Security 报告称,GlassWorm 攻击者在 2026 年 3 月 3 日至 9 日期间使用不可见 Unicode 字符隐藏有效载荷攻陷了 151 多个 GitHub 仓库——解码后的有效载荷从同一个 Solana 钱包获取其 C2 指令。这意味着 2026 年 3 月上旬,同一攻击者正在通过两个并行的浪潮冲击 GitHub 仓库:
- GlassWorm 浪潮(3 月 3 日至 9 日):151 多个仓库,不可见的 Unicode 注入——Aikido 报告
- ForceMemo 浪潮(3 月 8 日至 13 日):240 多个仓库,账户劫持 + 使用混淆 Python 进行强制推送——StepSecurity 报告(此博客)
这两个活动使用不同的传递方法和不同的代码混淆(Unicode 隐写术与 base64/zlib/XOR),但共享相同的 Solana C2 基础设施。这强烈表明 ForceMemo 是 GlassWorm 威胁参与者运营的一个新的传递向量,从 VS Code 扩展扩展到大规模 GitHub 账户劫持。
攻击者可能正在窃取什么
虽然我们无法看到最终的 JavaScript 有效载荷(它是使用通过 HTTP 头传递的密钥进行 AES 加密的),但基础设施模式与已知的加密货币钱包窃取器/信息窃取活动一致:
- CIS 国家排除——经典的东欧网络犯罪操作安全
- Node.js 运行时——非常适合访问浏览器扩展数据(加密货币钱包如 MetaMask、Phantom)、存储的凭证和 cookie
- 有效载荷的 AES 加密——防止静态分析和基于签名的检测
- 受害者 IP 指纹识别(
checkIp端点)——允许攻击者按受害者过滤或自定义有效载荷 - 2 天持久化计时器——设计用于持续访问,而非一次性渗透
最可能的目标是浏览器加密货币钱包扩展(助记词、私钥)、存储的凭证和会话 cookie以及 SSH 密钥。
活动范围
到目前为止,我们已经识别了数百个 GitHub 账户中的数百个 Python 仓库被注入了相同的恶意程序,而且这个数字还在继续增长。被针对的仓库包括 Django Web 应用程序、机器学习研究代码、Streamlit 仪表板、Flask API 以及可通过 pip install 从 GitHub URL 安装的 Python 包。若干被攻陷的仓库有 setup.py 文件——这意味着直接从仓库执行 pip install 会在安装期间执行恶意程序。
受影响的仓库完整列表可通过在 GitHub 上搜索该恶意程序的标记变量获得:
搜索恶意程序标记变量 lzcdrtfxyqiplpd 的 GitHub 代码——跨 Python 仓库的数百个结果。
账户级攻陷:重复受害者
账户级攻陷的最有力证据是每个账户有多个仓库被攻击的模式。随着活动的继续,这些数字还在增长:
wecode-bootcamp-korea(组织)——6 个仓库被攻陷HydroRoll-Team(组织)——6 个仓库被攻陷BierOne(用户)——6 个以上仓库被攻陷gnlxpy(用户)——6 个仓库被攻陷Fo2sh88(用户)——6 个仓库被攻陷watercrawl(组织)——4 个仓库被攻陷tavasolireza(用户)——4 个仓库被攻陷BishalBudhathoki(用户)——4 个仓库被攻陷iperformance(用户)——4 个仓库被攻陷amirasaran(用户)——3 个仓库被攻陷KeithSloan(用户)——2 个仓库被攻陷
被针对的文件类型
攻击者的工具在每个仓库中选择最突出的 Python 入口点:
main.py— 最常见的目标(约 70 个仓库)setup.py— 在pip install .上触发(约 25 个仓库)app.py— Flask/Streamlit 应用(约 25 个仓库)manage.py— Django 项目(约 20 个仓库)app/__init__.py— 包初始化文件(约 8 个仓库)- 其他:
streamlit_app.py、run.py、config.py、cli.py、noxfile.py
时间线
活动时间线
- 2025 年 11 月 27 日 — Solana C2 地址上最早的活动。注资钱包
G2YxRa...转移 SOL,第一个有效载荷 URL 指向45.32.151.157。攻击者可能在 GitHub 仓库之前针对其他感染途径。 - 2025 年 12 月至 2026 年 2 月 — 攻击者轮换有效载荷服务器(
45.32.151.157、45.32.150.97、217.69.11.57、217.69.11.99),发布约 40 笔带有更新有效载荷 URL 的备忘录交易。 - 3 月 8 日 — 检测到最早的 GitHub 仓库注入:
metalogico/issued、uknfire/tsmpy、gnlxpy/*仓库,wecode-bootcamp-korea/*仓库 - 3 月 10 日 — 主要浪潮:
amirasaran/*仓库(包括 70 星的 django-restful-admin)、KeithSloan/ImportNURBS、watercrawl/*仓库 - 3 月 11 日 — 持续:
KeithSloan/GDML - 3 月 12 日 —
BierOne/ood_coverage(34 星 ICLR 论文) - 3 月 13 日 — 最新浪潮:
BierOne/bottom-up-attention-vqa、BierOne/relation-vqa、biodatlab/siriraj-assist、HydroRoll-Team/HydroRoll - 3 月 14 日 — 第一个仓库开始恢复(例如,
KeithSloan/GDML在 14:05 UTC 恢复)。StepSecurity 发布初步发现并在受影响的仓库上提交安全问题。 - 持续进行中 — 活动仍然活跃。我们继续监控并将更新此帖子。
攻陷指标
- Solana C2 地址:
BjVeAjPrSKFiingBn4vZvghsGj9KCE8AJVtbc9S8o8SC - Solana 注资钱包:
G2YxRa6wt1qePMwfJzdXZG62ej4qaTC7YURzuh2Lwd3t - 代码中的标记变量:
lzcdrtfxyqiplpd - XOR 解密密钥:
134 - 提交者邮箱(指纹):
"null"(字符串) - 下载的 Node.js 版本: v22.9.0
- 持久化文件:
~/init.json - JS 有效载荷文件:
i.js(在脚本目录中) - 代码注释语言: 俄语
- CIS 排除: 俄语区域/时区跳过执行
C2 有效载荷服务器 IP(来自链上分析)
联系过的 Solana RPC 端点
为何称之为 ForceMemo
我们将此活动追踪为 ForceMemo,源于其两个最独特的技术特征:
- Force(强制) — 攻击者通过强制推送到被攻陷仓库的默认分支来注入恶意程序。这项技术重写了 git 历史,保留了原始提交信息和作者,并且在 GitHub 的 UI 中没有留下拉取请求或提交轨迹。没有其他有记录的供应链活动使用这种注入方法。
- Memo(备忘录) — 恶意程序使用 Solana 区块链交易备忘录作为其命令与控制渠道,从附加在特定 Solana 地址交易上的备忘录数据中读取有效载荷 URL。这使得 C2 指令不可变且抗审查。
如何检查您是否受影响
如果您直接从 GitHub 安装 Python 包(例如 pip install git+https://github.com/...)或克隆并运行 Python 仓库:
- 在您克隆的任何 Python 文件中搜索标记变量:
grep -r "lzcdrtfxyqiplpd" . - 检查您系统上的
~/init.json——这是恶意程序的持久化文件 - 检查您主目录中下载的 Node.js:
ls ~/node-v22* - 在任何最近克隆的项目目录中检查
i.js - 审查您克隆的仓库的 git 提交历史——寻找提交者日期明显晚于作者日期的提交
披露
我们已在最受影响的仓库上提交了安全问题,以通知维护者:
- amirasaran/django-restful-admin #17
- amirasaran/request_validator #3
- BierOne/bottom-up-attention-vqa #9
- BierOne/ood_coverage #6
- metalogico/issued #7
- biodatlab/siriraj-assist #2
- KeithSloan/ImportNURBS #15
StepSecurity 威胁情报
StepSecurity 威胁情报是首个发现并公开报告此活动的团队。该团队正在积极监控情况,并将继续更新此帖子。StepSecurity 持续监控开源和 CI/CD 生态系统中的新兴威胁——包括供应链攻击、被攻陷的 GitHub Actions、恶意包以及像这样的账户劫持活动。
StepSecurity 客户在其仪表板中直接接收威胁情报警报,并获得关于他们是否受影响以及如何补救的可操作指导。
Harden-Runner 如何检测此次攻击
Harden-Runner 监控 GitHub Actions 运行器上的网络流量、文件系统更改和进程活动。它旨在捕获像 ForceMemo 这样的供应链攻击产生的完全相同类型的异常行为。
当我们用 Harden-Runner 在审计模式下运行被攻陷的 setup.py 时,它捕获了恶意程序发出的每个出站连接:
api.mainnet-beta.solana.com:443— Solana 区块链 C2 查询217.69.0.159:80— 从 Solana 备忘录获取的有效载荷 URLnodejs.org:443— Node.js v22.9.0 下载
所有三个连接都被标记为**"不在基线中"**——这意味着它们从未在任何先前的工作流运行中出现过。Python setup.py 没有正当理由联系 Solana RPC 端点、下载 Node.js 或连接到未知 IP。
一旦建立了基线,Harden-Runner 就可以阻止任何不在允许列表中的出站连接。这将阻止恶意程序到达 Solana C2、下载 Node.js 或渗透数据。
保护您的 CI/CD 管道
StepSecurity 监控来自您 GitHub Actions 运行器的出站网络连接,检测像 ForceMemo 这样的异常活动,并保护您的软件供应链。