microsop npm Cluster: Dependency-Confusion Campaign Targeting Apple Internal CI/CD (2026)
npm publisher microsop pushed 36 versions across 6 Apple-themed packages between May 4–11, 2026, fingerprinting Apple internal CI and exfiltrating npmrc, env vars, and git origin to 12 rotating webhook.site endpoints.
Summary
Between May 4 and May 11, 2026, the npm publisher account microsop released 36 versions across 6 packages whose names mimic Apple internal tooling (ac-sasskit, apple-mycelium-fix, apple-mycelium-recon-tool, @microsop/apple-mycelium-integrity-check), generic CI utilities (rsflows-pexml), and a PayPal-themed payments package (paypal-payouts-bridge). Every version ships a preinstall or postinstall script that runs at install time, performs targeted reconnaissance against the host, and exfiltrates the result over HTTP to one of 12 distinct webhook.site UUID endpoints. The recon stages observed so far are consistent with dependency confusion against Apple's internal build pipeline, not generic credential theft — the payloads test for Apple-specific DNS, environment variables, and registry configuration before any secondary action.
Timeline
- 2026-05-04 10:25 UTC — First package published:
paypal-payouts-bridge@100.3.3. The version number (100.x.x) is the dependency-confusion tell; the legitimate@paypal/payouts-bridgeprivate package, if it exists internally, would have a far lower version. - 2026-05-09 12:56 UTC — Apple-themed cluster begins:
@microsop/apple-mycelium-integrity-check@1.0.1778331373, immediately followed byapple-mycelium-fixandapple-mycelium-recon-toolover the next 3 hours. - 2026-05-10 14:48 UTC —
rsflows-pexml@99.9.9published, followed by 12 more versions in 2 hours, each carrying a different recon payload to a different webhook endpoint. - 2026-05-10 20:15 UTC —
ac-sasskit@100.0.0published. 7 more versions over the next 9 hours. - 2026-05-11 05:38 UTC — Most recent observed publish:
ac-sasskit@100.1.3. Campaign still active at time of writing. - 2026-05-12 — Cremit's live monitor groups all 36 versions under publisher
microsop. Incident page published; npm Trust & Safety notified separately.
Attack Vector
The campaign uses dependency confusion: registering public npm packages whose names match (or appear similar to) packages a target organization uses privately, then publishing versions with very high version numbers so npm's resolver prefers the public copy over the private one. Three signals make Apple's internal build farms the most plausible target:
- Version numbers are dependency-confusion canonical:
100.0.0,100.1.3,99.9.42. No legitimate v1.x package legitimately jumps to v100 in a week. - Package names target specific Apple-internal namespaces:
ac-sasskit— described in itspackage.jsonas "Critical Security Patch for Apple Internal Sass Toolkit". Theac-prefix andSassKitframing match Apple's internal naming conventions.apple-mycelium-*—myceliummatches an internal Apple distributed-systems / mesh project name.@microsop/apple-mycelium-integrity-check— scoped variant, suggesting the actor probed both unscoped and scoped name resolution.
- The install-time payloads explicitly fingerprint Apple infrastructure before doing anything else, indicated by DNS lookups for
apple-beta.apple.com,internal.apple.com, andcerts.apple.com.
The malicious code lives in preinstall (most versions) or postinstall (some), so it executes during npm install without the developer ever importing or running the package.
Tokens & Credentials Exposed
Across the 36 versions, the install-script payloads collect, in different combinations:
.npmrccontents (cat .npmrc ~/.npmrc /root/.npmrc 2>/dev/null | grep -v _password | head -c 150) — includes npm publish auth tokens, private registry URLs, and proxy configuration.- Environment variables matching
APPLE|BETA|STAGE|TOKEN(env | grep -iE "BETA|STAGE|APPLE" | head -c 100) — captures CI-injected secrets, build environment markers, and target deployment context. - SSH private-key paths (read attempt against
~/.ssh/contents). - Git remote origin (
git remote -v | head -n 1) — identifies which internal Apple repo, if any, triggered the install. - Internal DNS resolution (
getent hostsagainst Apple internal domains) — confirms the host is inside Apple's network perimeter and not, for example, a researcher's workstation. - Hostname + npm registry URL — host fingerprinting.
Each version sends a different subset of the above to a different webhook UUID. The pattern is staged reconnaissance: the actor wants to know which version was installed on which class of host before deciding what to do with secondary access. No second-stage payload has been observed in the captured versions.
Confirmed Impact
As of publication, Cremit has direct evidence of:
- 36 malicious package versions reaching the public npm registry under a single publisher.
- 12 distinct
webhook.siteexfiltration endpoints actively receiving HTTP POST traffic (any installation hits one of them). - Naming and payload design that only makes operational sense if installed inside Apple's build infrastructure.
What is not yet directly observed:
- Confirmation that any Apple internal CI actually pulled these packages.
- Secondary payloads (file theft, lateral movement, code modification). The static payloads end after the first
curltowebhook.site.
Apple's internal pipelines using strict private-registry pinning, package-lock.json with npm config set registry, or scoped private namespaces would be unaffected. Pipelines that resolve unscoped names against the public npm registry before falling back to a private one are the primary risk.
Mitigation & Lessons
For organizations that fear they may be in scope:
- Audit your private package names against the public npm registry for any name that resolves both privately and publicly.
npm view <name> versionswill show if a public copy exists. - Force private-only resolution for internal namespaces: use scoped packages (
@your-org/foo) with@your-org:registry=https://internal.example.com/in.npmrc. Unscoped private package names are the surface area dependency confusion exploits. - Block
preinstall/postinstallin CI:npm install --ignore-scriptsis a hard mitigation against this entire class. If you need scripts, allowlist them per-package. - Egress filtering for
webhook.site: legitimate code never POSTs towebhook.site/<uuid>in production. Block at firewall or proxy. - Audit npm publish tokens stored in
.npmrcor environment. If any CI runner pulledac-sasskit,rsflows-pexml,apple-mycelium-*,@microsop/apple-mycelium-integrity-check, orpaypal-payouts-bridgesince May 4, 2026, rotate every credential reachable from that runner.
The 12 webhook.site UUIDs observed:
10844949-8a75-4998-bb9a-8ed79b681833
4521430f-e618-4d4b-8f13-abd81b74bcf2
49abe75c-2b38-498f-b08f-5f910bbd1f47
4dcec135-8ea0-42dd-bb3a-adef891539a3
53b04b32-06a9-416e-8897-d54dfb7e0373
6e268c68-febc-4f38-9cff-22f9152e9023
9c1e60d7-a1f0-48fd-afaf-ae16b73981b7
cb8d0670-33e0-46e2-8d6f-66a5e2d3df5d
d9ff4a88-0f9f-4f28-a8a0-c283d7e06b44
e442364e-c876-4536-a0c2-4edd3fc75935
f27e815b-2671-4dc3-9785-cc8d36b0ade1
fcd796ad-5e1b-46a6-87e5-e8d6705f23d4
Use these as IOC matches in proxy logs, EDR, or CI artifact scanning.
Cremit Analysis
NHI Severity Index: 7.5 (High)
| Dimension | Value | Rationale |
|---|---|---|
| Blast radius | cross-platform | Apple's build pipeline reaches macOS, iOS, watchOS, and tvOS artifacts. If exploited, downstream blast surface is product-wide. |
| Reachability | production | Install-time execution lands inside the runner that builds production artifacts. There is no staging "safe zone" — npm install is the same code path on every runner. |
| Privilege level | variable | Recon payloads steal .npmrc publish tokens (high privilege within npm), env vars (variable), and SSH keys (variable). The actor harvests whatever is reachable. |
The score sits at 7.5 rather than 9+ because we observed reconnaissance only — no confirmed secondary exfiltration of build artifacts, code signing material, or internal repo content. If a second-stage payload appears, the score moves to critical.
NHI Kill Chain mapping:
- Ghost Key — npm publish tokens harvested from
.npmrcare the textbook ghost-key target: long-lived, broadly-scoped credentials that exist in build environments but rarely show up in any inventory. - Drifted Key — environment variables captured by the
env | greppayload are drifted-key candidates: secrets injected into CI runners that outlive the build, leak into logs, and sit in CI variable stores months after rotation cycles forgot them. - Unattributed Key — the 12
webhook.siteendpoints themselves are unattributed: no legitimate organization owns them, no IR process can call the operator, andwebhook.siteretains POST bodies indefinitely by design. Any data sent here is permanently outside the defender's control.
Why publisher-clustering caught this and per-package scanning would not
Each of the 36 versions individually scores moderately on heuristics (small tarball, single install script, single network call). 11 versions were auto-published by Cremit's heuristic+LLM pipeline; the other 25 sat in low-signal even though the LLM flagged them malicious. What pulled the campaign into focus was the publisher-clustering pass: 36 versions from the same npm account in 7 days, all install-time network egress, all webhook.site. The publisher signal is what survived the noise — individual packages can hide, a sustained publishing pattern from one account cannot.
This is the case Cremit's campaign detector is built for: most npm threat actors operate at the publisher level over weeks, not the single-package level over hours.
References
- [1]ac-sasskit on npm (publisher: microsop)primary·npmjs.com
- [2]rsflows-pexml on npm (publisher: microsop)primary·npmjs.com
- [3]paypal-payouts-bridge on npm (publisher: microsop)primary·npmjs.com
- [4]Cremit live monitor — microsop publisher campaignanalysis·incidents.cremit.io
