CryptoNetHub

npm Stealer Reads Its C2 From an Ethereum Contract

Netskope Threat Labs identified and analyzed 28 malicious npm package versions published across four unrelated enterprise namespaces (@servicetitan,@or-sdk,@onereach, and@umacloud) on 2026-08-04. The packages. . .

By Gianpietro Cutolo · · 5 min read

npm Stealer Reads Its C2 From an Ethereum Contract

Netskope Threat Labs identified and analyzed 28 malicious npm package versions published across four unrelated enterprise namespaces (@servicetitan,@or-sdk,@onereach, and@umacloud) on 2026-08-04. The packages arrived in two rapid bursts: the@or-sdkand@onereachpackages at 10:39 UTC, the@servicetitanpackages two minutes later at 10:41 UTC, and@umacloud/knowledgenearly three hours after that at 13:18 UTC. All 28 carry an identical or functionally equivalent payload, confirmed by hash match for@umacloud/[email protected], the one tarball still live when we retrieved it. The operator behind the campaign calls it Shai-Hulud, consistent naming with similar attacks we have tracked over the past year [1], [2], [3]. At install time, the packages fetch a signed Bun runtime release from GitHub, execute an obfuscated JavaScript stealer under it, and delete the runtime. Detonating@or-sdk/[email protected]in the Netskope Advanced Threat Protection sandbox produced the full chain. Measured from the start of npm install to the outbound POST, the payload read SSH private keys, AWS credentials, Docker registry authentication, Jenkins secrets, and/etc/shadow, requested a GitHub CLI token, enumerated AWS Systems Manager Parameter Store and Secrets Manager across 17 regions, queried the EC2 instance metadata service, attempted a HashiCorp Vault AWS login against localhost, and posted an encrypted 4. 8 KB bundle tonpm-cache. com/router. That domain is not written into the payload. Deobfuscating the recovered stealer showed that the exfiltration host is resolved at runtime via an eth_call to an Ethereum smart contract, with three public Ethereum RPC providers tried in turn until one responds. The operator can re-point exfiltration to new infrastructure by updating contract state, without republishing a single package. The payload we recovered from@umacloud/[email protected]hashes identically to the sample used against thekeyvpackage family the same day,as reported by Aikido Security, confirming a single campaign toolset. The four namespaces we document here are additional to that reporting. Key findingsOne payload, four unrelated namespaces, two publishing bursts. The@or-sdkand@onereachpackages (12 packages) landed at 10:39 UTC and the 15@servicetitanpackages two minutes later, consistent with separate automated publish pipelines firing in close succession. @umacloud/knowledgearrived nearly three hours after. The payload SHA256 matches thekeyvfamily compromise. The publishing pattern is consistent with multiple stolen organization tokens, worm propagation from a compromised maintainer machine, or theft of GitHub Actions OIDC credentials that can be exchanged for short-lived npm publish tokens scoped to a namespace, which our deobfuscation confirms is one of the payload’s named collection categories. We cannot yet distinguish among vectors. The payload ships no binary and hardcodes no C2. The install hook downloads Bun v1. 3. 13 from the officialoven-sh/bunrelease URL, runs the payload under it, and then deletes the runtime. The exfiltration domain is not in the file either: aneth_callto an Ethereum smart contract returns it at runtime. Detection has to fire on behavior, because there is no malicious executable in the tarball and the executable that does run is legitimately signed. The credential sweep is built to maximize yield wherever npm install runs. Developer workstation paths (~/. aws,~/. ssh,~/. docker, GitHub CLI tokens) and CI/CD-specific targets (Jenkins secret directories, EC2 IMDS, Vault AWS IAM auth, 17-region Secrets Manager enumeration) are swept in the same pass. The CI/CD paths only pay off on build agents, but the package will run on developer machines too, and the operator collects from both. IOCsAll publication timestamps are 2026-08-04 UTC, taken from npm registry metadata. Malicious package versionsPackageVersionPublished (UTC)@or-sdk/accounts2. 3. 510:39@or-sdk/auth0. 38. 110:39@or-sdk/adapters0. 3. 610:39@or-sdk/billing-internal27. 2. 110:39@or-sdk/content-request0. 2. 610:39@or-sdk/providers0. 3. 610:39@or-sdk/permissions-lambda2. 5. 110:39@or-sdk/druid1. 4. 710:39@or-sdk/base0. 44. 410:39@or-sdk/sdk-api0. 29. 210:39@onereach/types-contacts-api9. 0. 810:39@onereach/billing-shared27. 2. 110:39@servicetitan/error-boundary38. 1. 110:41@servicetitan/line-item-editor1. 5. 110:41@servicetitan/ko-bridge38. 1. 110:41@servicetitan/data-query41. 3. 110:41@servicetitan/feature-spotlight3. 9. 110:41@servicetitan/carto-react-kit0. 8. 410:41@servicetitan/react-hooks7. 7. 110:41@servicetitan/temporal-lite3. 4. 110:41@servicetitan/anvil23. 9. 110:41@servicetitan/form-state41. 3. 110:41@servicetitan/stylelint-config38. 1. 110:41@servicetitan/json-render-react0. 4. 610:41@servicetitan/datadog-rum38. 1. 110:41@servicetitan/web-components38. 1. 110:41@servicetitan/ajax-handlers38. 1. 110:41@umacloud/knowledge1. 0. 7413:18Note: burst timestamps are per-namespace group. The@or-sdkand@onereachpackages published within two seconds of each other at 10:39, and the@servicetitanpackages within one second of each other at 10:41. PayloadIndicatorTypeRole9fc2570b7cef51c1b8df116d144d11ff4096357be7d2c4c6367cfc2509cf1bccSHA256Stealer payload (math_init. js), identical to the keyv-family samplemath_init. jsFile nameStealer payload, executed under the downloaded Bun runtimeMath_Symbol. jsFile nameAlternate payload file name for the same stealersetup. mjsFile nameDropper, invoked by the preinstall hookbun-linux-x64-baseline. zipFile nameBun runtime archive fetched from GitHub on Linux hosts, deleted after unpacking. The dropper selects the equivalent darwin-arm64, darwin-x64, or win32 archive on other platformsbun-dl-*Drop pathRandomized runtime directory (/tmp/bun-dl-*/bun on Linux and macOS, temp path equivalent on Windows), binary deleted after payload executionNetworkIndicatorTypeRolenpm-cache. comDomainExfiltration endpoint (POST /router), registered 2026-05-22, Cloudflare-fronted. Resolved at runtime from contract state, so treat as perishable104. 21. 35. 216IPv4npm-cache. com resolution (Cloudflare)172. 67. 179. 231IPv4npm-cache. com resolution (Cloudflare)eth. llamarpc. comDomainEthereum RPC provider used for C2 domain resolutiongo. getblock. ioDomainEthereum RPC provider used for C2 domain resolutioneth-mainnet. nodereal. ioDomainEthereum RPC provider used for C2 domain resolution0x02d1e413Ethereum call selectorFunction selector for the C2-resolving eth_call against the smart contract. Durable indicator, survives C2 domain rotation. Full call argument pending complete deobfuscationNote on the Ethereum RPC endpoints: these are legitimate public RPC providers that the payload abuses for C2 resolution. Use them for detection and alerting, not for blind blocking.

Source: kadiska.com

More Ethereum news