@marketfront: 25 npm Packages Reuse a Known Lure
Table of Contents
TL;DR
On July 1, 2026, the npm user marketfront batch-published 25 packages into the @marketfront
scope within a roughly three-minute window, all at version 7.0.0. Every package carries a
postinstall credential harvester and an identical README line: Internal package — Platform Engineering Team. That line is not a signature of one account. It is a reusable dependency
confusion lure template SafeDep has tracked across a series of disposable npm accounts
(mr.4nd3r50n, pik-libs, t-in-one, emcd-vue), documented in the
oob.moika.tech campaign report, and marketfront
is the latest to reuse it. The marker repeats across every wave, but the payload behind it does
not. It has shifted from a broad process.env beacon into a targeted credential-file harvester
with an RC4-hidden command-and-control host.
Paste or upload a lockfile, parsed locally against 25 packages.
Impact:
- On install, the payload reads roughly 20 credential and secret files including
~/.ssh,~/.aws/credentials,~/.kube/config,~/.docker/config.json,~/.npmrc,~/.netrc,~/.pgpass,~/.git-credentials,~/.env, and shell history, then exfiltrates them. - Any developer workstation or CI runner that resolves an internal-looking
@marketfrontname from the public registry executes the harvester before control returns to the build. - The 25 packages use e-commerce and marketing frontend component names as cover, which points at a specific organization’s internal package namespace rather than a generic squat.
Indicators of Compromise:
- npm scope:
@marketfront, 25 packages at version7.0.0, npm usermarketfront([email protected]), published 2026-07-01 - README marker:
Internal package — Platform Engineering Team package.jsonauthor:<Scope> Platform Engineering <platform@<scope>.io>- Install hook:
postinstall: node scripts/postinstall.js, obfuscator.io-style single-line payload - Exfiltration: gzip-compressed HTTPS POST with an
X-Secretheader to the path/api/v1/events, plus a DNS resolver beacon - The command-and-control host sits behind an RC4 plus XOR layer and was not statically resolved. No C2 domain is published here because none was recovered without executing the payload.
The reused marker
SafeDep first documented this lure template in the
oob.moika.tech campaign, which tied together four
npm accounts (mr.4nd3r50n, pik-libs, t-in-one, emcd-vue) publishing across scopes such as
@cloudplatform-single-spa, @t-in-one, and @emcd-vue. Microsoft and Sonatype reported
overlapping waves of the same dependency-confusion activity, describing the same author strings
ending in “Platform Engineering” and the same X-Secret header convention.
The @marketfront scope, published July 1, is the next rotation. The registry has already pulled
the packages, so their pages return 404. The lure template survives in a place that is harder to
take down: the still-live @emcd-vue scope, the direct predecessor, republished with fresh
version numbers (@emcd-vue/[email protected], @emcd-vue/[email protected], @emcd-vue/[email protected]).
The @emcd-vue artifacts are byte-for-byte instances of the same template, so the metadata and
payload snippets below are pulled from that live source and cross-referenced against SafeDep’s
static analysis of the @marketfront batch.
The @marketfront wave
The 25 package names read like the internal frontend modules of an online marketplace:
@marketfront/header, @marketfront/footer, @marketfront/navbar, @marketfront/bannerpopup,
@marketfront/customdealsfeed, @marketfront/fashiononboardingpopup,
@marketfront/livestreampreviewpopup, @marketfront/designsystemdevtool. This is the same
targeting logic as the earlier waves. The names mirror what a specific organization’s private
package registry would contain, so a build that is missing the private version resolves the public
malicious one instead.
SafeDep’s analysis confirmed that the decoded payload is identical across the sampled packages in
the batch, consistent with a scripted publish rather than per-package development. The scope was
created on the same day at 2026-07-01T22:59:33Z and populated in one burst.
The metadata fingerprint
The lure lives entirely in the package metadata and README. Here is the manifest from the live
predecessor, unchanged in structure from what SafeDep analyzed in @marketfront:
// @emcd-vue/[email protected] — package/package.json{ "name": "@emcd-vue/auth", "version": "7.1.0", "description": "Internal structured logger with log levels, context and remote drain support", "main": "dist/index.js", "scripts": { "build": "tsc --noEmit || true", "test": "node test/index.test.js", "postinstall": "node scripts/postinstall.js", "prepublishOnly": "echo 'Building...'" }, "license": "UNLICENSED", "private": false, "repository": { "type": "git", "url": "git+https://github.emcd-vue.io/platform/auth.git" }, "bugs": { "url": "https://jira.emcd-vue.io/projects/PLATFORM" }, "homepage": "https://docs.emcd-vue.io/platform/auth"}The author, repository.url, bugs.url, and homepage fields are parameterized by scope name.
Swap emcd-vue for marketfront and the fields regenerate. The description strings are drawn
from a fixed pool that the earlier waves used verbatim, for example Internal structured logger with log levels, context and remote drain support and Internal configuration loader with env, vault and remote config support.
The README does the social engineering:
# @emcd-vue/auth — package/README.md> **Internal package** — Platform Engineering Team> Docs: https://docs.emcd-vue.io/platform/auth> Issues: https://jira.emcd-vue.io/projects/PLATFORM
## Installation# Make sure .npmrc points to the internal registry:# registry=https://npm.emcd-vue.io
## TelemetryOn install, this package sends anonymous telemetry to telemetry.emcd-vue.iofor environment compatibility monitoring.The .npmrc comment and the telemetry paragraph do the social engineering. The .npmrc comment
tells the developer to point at a private registry, which is the correct security practice, so the
package reads as a legitimate internal artifact that is already published where it should be. The
telemetry paragraph pre-explains the outbound network request an alert reviewer might notice during
install, framing exfiltration as authorized monitoring. The decoy domains (docs.emcd-vue.io, jira.emcd-vue.io,
npm.emcd-vue.io) do not resolve. They exist only to make the metadata look like real internal
tooling.
The shipped dist/index.js is a stub. It re-exports a source file the package does not include:
// @emcd-vue/[email protected] — package/dist/index.js'use strict';module.exports = require('../src/index.js');Requiring the package at runtime would throw, because ../src/index.js is not in the tarball. The
library is a decoy, and the payload runs entirely from the install hook.
The payload
Every active package declares the payload through the postinstall lifecycle hook shown in the
manifest above. npm runs scripts/postinstall.js immediately after the package is placed on disk,
before the developer or CI runner regains control.
The script is a single line of roughly 160 KB of obfuscator.io-style output. It opens with the standard string-table rotation preamble and a custom base64 alphabet:
// @emcd-vue/[email protected] — package/scripts/postinstall.js (excerpt)'use strict';(function(a,b){var a0b3={a:0x58,b:0xae,c:'\x28\x66\x31\x77',...};...while(!![]){try{var d=-parseInt(Y(0x135,'\x38\x35\x5a\x46'))/... if(d===b)break;else c['push'](c['shift']());}catch(e){c['push'](c['shift']());}}}(a0d,-0x22f4b+...));The alphabet embedded in the string decoder is abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRST UVWXYZ0123456789+/=, a lowercase-first reordering of standard base64. Statically base64-decoding
the string table with that alphabet recovers primitives such as charCodeAt and fromCharCode,
which confirms the runtime char-code string building, but the sensitive strings do not fall out.
They sit behind a second RC4 layer keyed per string, which is why the credential-file paths and
the exfiltration endpoint are not visible to a plain string dump. This matches the obfuscation
class SafeDep flagged on the @marketfront batch.
SafeDep decoded the behavior statically. The payload dynamically requires fs, os, http,
https, zlib, path, and dns, reads roughly 20 credential and secret files, and exfiltrates
them:
- SSH keys (
~/.ssh) - Cloud credentials (
~/.aws/credentials,~/.kube/config,~/.docker/config.json) - Registry and package tokens (
~/.npmrc) - Network and database secrets (
~/.netrc,~/.pgpass) - Git credentials (
~/.git-credentials) - Environment files and shell history (
~/.env,~/.bash_history)
The collected data is gzip-compressed and sent over an HTTPS POST with a custom X-Secret header
to the path /api/v1/events, alongside a DNS resolver beacon. The command-and-control host is
concealed behind an RC4 plus XOR layer around an embedded configuration blob. SafeDep did not
resolve it without executing the code, and this post does not fabricate one.
How the payload evolved under a constant lure
The lure template is the stable element across the campaign. The payload is not. Across the four waves, what runs behind the same README changed in a deliberate progression:
| Wave | Scope(s) | Version pattern | Payload behavior |
|---|---|---|---|
| May 27, 2026 | @cloudplatform-single-spa, @mlspace, @car-loans and others | 99.99.99 | Cleartext constants, downloads a second stage, POSTs full process.env to oob.moika.tech/report |
| May 29, 2026 | @t-in-one, @capibar.chat, @sber-ecom-core | 5.7.1, 99.5.x | Same behavior, three-layer obfuscation, functional kill switch |
| June 1, 2026 | @emcd-vue | 6.4.8, 7.1.7 | WaCk/JScrambler obfuscation, home-directory persistence, FUSION_ second-stage handshake |
| July 1, 2026 | @marketfront, @emcd-vue (republished) | 7.0.0, 7.x | Targeted credential-file harvester, gzip POST to /api/v1/events, RC4-hidden C2 |
The version numbers also matured. The first wave used the unmistakable 99.99.99. The
@marketfront wave uses 7.0.0, and the republished @emcd-vue packages use 7.1.0 and 7.2.0,
which read like ordinary releases from a maintained project. Version-anomaly heuristics that catch
triple-nines do not fire on these.
Two things held constant through all four waves: the Internal package — Platform Engineering Team README marker with its scope-parameterized decoy domains, and the X-Secret header on the
exfiltration request. The README string is the cheapest part of the operation to change, and the
actor never bothered with it, while the payload, the endpoint, the persistence strategy, and the
version scheme all rotated. That makes the marker a durable pivot for hunting even as everything
technical underneath it moves.
Detection guidance
The README marker is worth hunting on, but it is a weak primary detector. npm’s search index does not index README bodies, and it deprioritizes brand-new zero-download packages, which is how a freshly batch-published scope looks in its first hours. A full-text search for the marker will miss a scope published minutes ago.
The dependable detector is the metadata and behavioral fingerprint, which survives the payload rotation:
postinstall: node scripts/postinstall.jsshipping a single-line obfuscator.io payload, next to adist/index.jsthat re-exports a source file absent from the tarball- an
authorfield ending inPlatform Engineeringwithgithub.<scope>.io,jira.<scope>.io, anddocs.<scope>.iodecoy domains - a
descriptiondrawn from the fixed “Internal … loader / logger / client” pool - outbound install-time traffic carrying an
X-Secretheader
Mitigations
- Lock every internal scope to a private registry in
.npmrc. Without a scope-locked registry, npm resolves to the public version when the private one is unavailable, which is the entire mechanism this campaign depends on. - If any
@marketfrontpackage at7.0.0was installed on a workstation or CI runner, treat the host as credential-compromised. Rotate SSH keys, cloud credentials (~/.aws,~/.kube,~/.docker), npm tokens, git credentials, and any secrets present in~/.envor the shell environment. - Check network logs for install-time HTTPS POSTs carrying an
X-Secretheader to a/api/v1/eventspath, and for unexpected DNS resolver activity duringnpm install. - Add the metadata and behavioral fingerprint above to standing triage rules so the next scope rotation is flagged automatically rather than rediscovered by hand.
- Run
vetagainst your lockfiles to surface malicious packages before the next install cycle.
Affected packages
All 25 @marketfront packages were batch-published at version 7.0.0 and carry the credential
harvester. Use the checker above or the list below.
| ecosystem | name | version | npm_user | has_postinstall_payload | published | |
|---|---|---|---|---|---|---|
| 1 | npm | @marketfront/actualordersnippetpopup | 7.0.0 | marketfront | yes | 2026-07-01 |
| 2 | npm | @marketfront/advertisingdevtool | 7.0.0 | marketfront | yes | 2026-07-01 |
| 3 | npm | @marketfront/bannerpopup | 7.0.0 | marketfront | yes | 2026-07-01 |
| 4 | npm | @marketfront/baobabtech | 7.0.0 | marketfront | yes | 2026-07-01 |
| 5 | npm | @marketfront/basemarkettemplate | 7.0.0 | marketfront | yes | 2026-07-01 |
| 6 | npm | @marketfront/blenderdevtool | 7.0.0 | marketfront | yes | 2026-07-01 |
| 7 | npm | @marketfront/captchaservice | 7.0.0 | marketfront | yes | 2026-07-01 |
| 8 | npm | @marketfront/changefilter | 7.0.0 | marketfront | yes | 2026-07-01 |
| 9 | npm | @marketfront/commonecommerce | 7.0.0 | marketfront | yes | 2026-07-01 |
| 10 | npm | @marketfront/customdealsfeed | 7.0.0 | marketfront | yes | 2026-07-01 |
| 11 | npm | @marketfront/designsystemdevtool | 7.0.0 | marketfront | yes | 2026-07-01 |
| 12 | npm | @marketfront/devtoolsloader | 7.0.0 | marketfront | yes | 2026-07-01 |
| 13 | npm | @marketfront/digitalherobannercarousel | 7.0.0 | marketfront | yes | 2026-07-01 |
| 14 | npm | @marketfront/dynamicpageparams | 7.0.0 | marketfront | yes | 2026-07-01 |
| 15 | npm | @marketfront/errorcounter | 7.0.0 | marketfront | yes | 2026-07-01 |
| 16 | npm | @marketfront/fashiononboardingpopup | 7.0.0 | marketfront | yes | 2026-07-01 |
| 17 | npm | @marketfront/fingerprint | 7.0.0 | marketfront | yes | 2026-07-01 |
| 18 | npm | @marketfront/footer | 7.0.0 | marketfront | yes | 2026-07-01 |
| 19 | npm | @marketfront/gotoauthpopup | 7.0.0 | marketfront | yes | 2026-07-01 |
| 20 | npm | @marketfront/header | 7.0.0 | marketfront | yes | 2026-07-01 |
| 21 | npm | @marketfront/infopopup | 7.0.0 | marketfront | yes | 2026-07-01 |
| 22 | npm | @marketfront/livestreampreviewpopup | 7.0.0 | marketfront | yes | 2026-07-01 |
| 23 | npm | @marketfront/madvpopup | 7.0.0 | marketfront | yes | 2026-07-01 |
| 24 | npm | @marketfront/mychatspreloader | 7.0.0 | marketfront | yes | 2026-07-01 |
| 25 | npm | @marketfront/navbar | 7.0.0 | marketfront | yes | 2026-07-01 |
- npm
- malware
- supply-chain
- dependency-confusion
Author
Kunal Singh
safedep.io
Share
The Latest from SafeDep blogs
Follow for the latest updates and insights on open source security & engineering
The Polymarket Trap: A Fake Arbitrage Bot, Ten npm Accounts, and Four Ways to Deliver an Infostealer
A GitHub repository posing as a Polymarket arbitrage bot accumulated 53 forks before anyone flagged the malicious npm package buried in its dependencies. Behind that repo: ten coordinated npm...
The wshu.net npm Campaign Delivers a Multi-Stage Infostealer
One actor seeded 15 npm packages across 13 throwaway scopes in a single morning, each shipping a ~270KB obfuscated downloader behind a postinstall hook. The downloader pulls a Rust infostealer from...
Miasma Worm Infects Multiple LeoPlatform npm Packages
A Miasma worm variant compromised a single maintainer account and used it to publish infected versions of 20 LeoPlatform npm packages within a 3-second window. The worm also pushed weaponized GitHub...
MYRA: A Full Linux RAT Distributed via npm
The npm package apintergrationpost is a red team RAT called MYRA with native C rootkit, triple persistence, fileless execution, live screen streaming, and process masquerade. This analysis documents...
Ship Code.
Not Malware.
Start free with open source tools on your machine. Scale to a unified platform for your organization.