一次失败的国家支持攻击解剖
速览
文章深入剖析了一起疑似由国家支持的网络攻击事件。通过拆解攻击过程,揭示了攻击者使用的战术与防御机制的对抗。此次分析有助于理解高级持续性威胁的特征及防御策略。
AI 深度解读
一次失败的(国家级?)攻击解剖
背景
近期,一位居住在加拿大的 Rust 开发者遭遇了一起精心策划的“虚假面试”网络钓鱼攻击。攻击者伪装成一家名为 Lua Ventures 的(实际上已停止运营的)新加坡去中心化金融(DeFi)风险投资机构,试图通过技术测试环节将恶意软件植入受害者的开发环境。
该攻击者使用了虚构的身份,但其姓名与现实中存在的某些人相似,作者特意在文中省略了这些无关人员的姓名以避免混淆。值得注意的是,Reddit 上的其他 Rust 社区成员也报告称遭遇了类似的定向攻击。
由于事件具有时效性,作者已将相关信息报告给加拿大相关机构(如加拿大网络犯罪中心 CCCS 等)。值得注意的是,包含恶意负载的图像文件在 VirusTotal 上并未触发任何杀毒引擎。作者将此次发现的恶意软件命名为“PinpinRAT”,这一名称源于恶意软件内部字符串,但其在网络上的其他已知名称尚不明确。
核心内容
攻击初期:建立信任与误导
攻击始于两周前的一封电子邮件,发件人自称来自 Lua Ventures。邮件看起来非常逼真,甚至包含了一个看似合法的 LinkedIn 个人资料链接。为了增加可信度,攻击者还提到了两家其“投资”的公司:Lyrasing 和 Roadpay。这两家公司虽然网络存在感薄弱,但处于早期阶段,因此并未引起作者最初的警觉。
随后,双方通过邮件协调了面试时间。通话中,对方是一位带有德国口音、自称正在旅途中的人。尽管细节略显奇怪,但并未触发作者的警惕机制。
陷阱设置:TypeScript 仓库与“测试”
通话结束后,攻击者发送了一封后续邮件,要求作者完成一项“测试”。作者克隆了提供的代码仓库,此时真正的红色警报才真正响起。
作者感到幸运的一点是,攻击者发送的是一个 TypeScript 仓库。对于 Rust 开发者而言,这本身就不合逻辑,且任务说明更像是一份 TypeScript 的求职面试,而非架构分析。出于谨慎和懒惰,作者将仓库打包并输入给 Claude(由 Anthropic 开发的大型语言模型)进行快速扫描。
恶意代码分析:PinpinRAT 的发现
Claude 迅速识别出了异常:
- 根目录的
package.json没有postinstall或preinstall钩子,但这与项目使用patch-package(通常绑定在postinstall中)的做法相矛盾。 - 作者手动扫描发现存在大量
patches/目录,这通常是用于掩盖真实恶意负载的噪音。
Claude 进一步在 typescript+5.9.2.patch 文件中发现了恶意载荷。该文件伪装成对 TypeScript 模块说明符的常规补丁,但在 tsc.js 和 typescript.js 的顶部注入了一个自执行的混淆存根(stub)。
该存根代码逻辑如下:
- 解码 Base64 字符串。
- 使用密钥
73进行 XOR 解密。 - 通过
new Function(...)执行解密后的代码,并将require、Buffer、WebAssembly、process等关键对象传入。
这意味着,每当运行 tsc 或任何导入 typescript.js 的操作(如 npm run typecheck、构建或开发服务器启动)时,恶意代码就会执行。意识到风险后,作者立即停止在本地机器上进一步操作,将文件加密打包并在沙箱环境中继续分析。
恶意软件机制:三层混淆与持久化
该仓库以名为“Ticket Harbor”的渡轮票务应用为主题。任务文件指示用户在提交前运行类型检查、测试套件及构建命令,这正是触发恶意代码的陷阱。
执行链条如下:
- 隐藏补丁:四个独立的
postinstall钩子运行patch-package。其中一个钩子还对补丁文件执行git update-index --skip-worktree,使其在git status中隐藏。 - 载荷注入:
typescript+5.9.2.patch在typescript.js和_tsc.js顶部注入自执行存根。该存根避免使用eval以规避检测,而是使用new Function。 - 第二阶载荷:加载器读取附加在隐藏文件
operators/3.png中的数据块,运行一个嵌入的小型 WASM 存根,随后派生出一个静默的、分离的 Node.js 进程,携带一个 1.68 MB 的混淆第二阶载荷。 - 清理痕迹:
- 利用
git skip-worktree隐藏文件。 - 投放器在首次运行后重写补丁文件,删除自身注入的行。
- 第二阶临时目录在执行后自删除。
- 利用
PinpinRAT 的结构:
恶意软件经过三层混淆:首先是 obfuscator.io(声称具备 LLM 保护),随后是两层 Base64 编码。Claude 在沙箱中仅用约 5 分钟就逆向工程了解开这些混淆,速度远快于人工。
恶意功能:远程访问木马 (RAT)
该载荷是一个功能完整的远程访问木马(RAT),由具备专业知识的人员编写。它使用 RSA 密钥进行本地设置,并使用 AES-256-CBC 作为会话密钥。
启动行为:
- 主机指纹收集:通过 checkin 例程收集并外泄主机信息,包括所有非内部接口的 IP 地址、用户名、主机名、OS 类型/版本/平台/架构、进程 PID、完整的
process.argv以及 Node.js 版本。 - 加密通信:生成 RSA-2048 密钥对和随机 AES-256 会话密钥,后续所有通信均使用 AES-256-CBC 加密并附带 HMAC-SHA256 完整性标签。
支持的控制命令:
env:转储并发送process.env。upload:读取任意文件路径并外泄。download:将攻击者提供的字节写入任何可写路径。spawn:运行任意进程,支持可选的 Shell 扩展。ls/cd/pwd/cp/mv:基本文件系统操作。dns:通过指定解析器解析任意名称(可能用于 DNS 隧道)。dismantle:自我移除。
关键要点
- 社会工程学与技术结合:攻击者利用虚假的风投背景、LinkedIn 资料以及看似合理的“技术测试”任务,成功降低了开发者的警惕性。
- 供应链/依赖注入风险:攻击者利用
patch-package机制,通过修改node_modules中的核心库(TypeScript)来注入恶意代码。由于patch-package常用于修复依赖问题,这种手法极具隐蔽性。 - LLM 在安全分析中的价值:作者利用 Claude 快速识别出混淆代码中的异常模式,并协助逆向工程多层混淆的恶意载荷。这展示了 AI 工具在加速威胁情报分析和恶意软件逆向工程方面的巨大潜力。
- 多层混淆与反取证:恶意软件采用了 Git 隐藏技巧、代码自修改、临时文件自删除以及多层混淆(WASM、Base64、JS Obfuscator)来逃避检测和清理痕迹。
- 高权限与持久化:一旦执行,RAT 即可获取主机完整控制权,包括文件读写、进程执行和网络通信,且通信经过强加密,难以被网络监控轻易识别。
意义与影响
此次事件揭示了针对软件开发者的定向攻击日益复杂化。攻击者不再仅仅依赖简单的钓鱼链接,而是深入技术工作流,利用开发者对代码审查的信任和对构建流程的依赖(如 npm install 和 postinstall 钩子)进行渗透。
对开发者的启示:
- 验证来源:对于来自陌生“招聘方”或“投资方”的代码仓库,尤其是涉及构建脚本和依赖补丁的,应保持极高警惕。
- **沙
