CentrioleBlog
Back to blog

Threat Research

Inside a 105-Day Fake Baileys Ecosystem: Tracking Six Malicious npm Packages Across Three Waves

Six npm packages impersonating @whiskeysockets/baileys published 83 versions over 105 days. The campaign ends with a libsignal trojan that overwrites Baileys session files on disk.

Date

Reading time

15 min read

Author

Centriole Research
Share
Inside a 105-Day Fake Baileys Ecosystem: Tracking Six Malicious npm Packages Across Three Waves

Eighty-three versions. Two npm accounts. Six packages. All of them impersonate the Baileys WhatsApp Web library, and all of them were published across a 105-day window from 5 March 2026 to 18 June 2026. The campaign ends with @sauruslord/libsignal, a package that impersonates Open Whisper Systems’ Signal protocol library and uses it as a trojan horse to overwrite a file inside your legitimate @whiskeysockets/baileys installation on disk, wiring your authenticated WhatsApp session to an attacker-controlled newsletter channel without any user action.

Campaign Cluster Map

WavePackage(s)First PublishedPayload ClassPublisher Account
1@sauruslord/baileys, sauruslord-baileys2026-03-05Class 1 (credential / session stealer)sauruslord
2sauruslord-baileys (2.x, 3.x), zaldy-baileys, @sauruslord/libsignal2026-03-23Class 1 + filesystem implantsauruslord, zaldy
3@fhkry/baileys-v2, @fhkry/x-baileys2026-04-02Class 1fhkry

All six packages have been security-held by npm as of 15 July 2026 and are confirmed malicious under MAL-2026-10657, MAL-2026-10659, MAL-2026-10661, MAL-2026-10663, MAL-2026-10664, and MAL-2026-10665.

Wave 1: The @sauruslord Namespace Opens (5 March 2026)

Package Anatomy

The sauruslord account registered the @sauruslord npm scope and published @sauruslord/baileys at 15:18 UTC on 5 March 2026. Seventeen minutes later, sauruslord-baileys appeared as an unscoped variant under the same account. Both impersonate @whiskeysockets/baileys, the leading open-source WhatsApp Web library maintained by WhiskeySockets with over 50 published versions since May 2023.

The cross-reference we ran against the npm registry time field for sauruslord-baileys shows 13 versions published on launch day alone, from 1.0.2 at 15:40 UTC through 1.1.3 at 23:32 UTC. The operator continued iterating through 5 March and into the early hours of 7 March, then paused for six days before resuming on 13 March. By the time the 1.x series ended, sauruslord-baileys had accumulated 19 versions across three days of active publishing.

Version Timeline: Wave 1

PackageVersionPublished (UTC)Notes
@sauruslord/baileys1.0.02026-03-05 15:18@sauruslord scope created; scoped variant
@sauruslord/baileys1.0.12026-03-05 15:27Patch 9 minutes after initial publish
sauruslord-baileys1.0.22026-03-05 15:40Unscoped variant launched 22 minutes later
sauruslord-baileys1.0.3 through 1.1.32026-03-05 17:23 to 23:329 versions in 7 hours: rapid payload iteration
sauruslord-baileys1.1.4 through 1.1.82026-03-06 to 2026-03-07 02:38Day 2 iteration continues into early morning
sauruslord-baileys1.1.92026-03-13 17:406-day gap; activity resumes
sauruslord-baileys2.0.02026-03-15 05:35Major version bump; 2.x series begins

Eleven versions published in the first eight hours is not normal package maintenance. It is a payload tuning cycle.

Execution Trigger

The sauruslord-baileys package carries a preinstall lifecycle hook that executes engine-requirements.js before npm writes any package files to disk. The evidence file recovered from versions 3.0.0 through 3.0.2 and confirmed by Amazon Inspector (SHA-256: 9b0ac7033c5a7f5646f4bdbe08fe542a2d26031fd5f4ca1f082f6972ee383a45) shows the filename engine-requirements.js as the preinstall payload carrier. A file named lib/index.js (SHA-256: 69a6ca2dcaf905a690fbf65030aa486217975358a54077c7b05bce9cf2bfa1da) contains the primary module-level execution logic.

The scripts block from the registry metadata reads:

sauruslord-baileys package.json (scripts block, recovered from registry metadata)
{
  "preinstall": "node ./engine-requirements.js"
}

preinstall fires before the install phase, meaning the payload runs on the machine performing npm install regardless of --ignore-scripts at the dependency layer. Any project that lists sauruslord-baileys as a direct or transitive dependency executed this code.

Wave 2: Satellite Accounts and the libsignal Implant (23 March 2026)

Wave 2 is where the campaign scope expands and the most technically significant package appears.

zaldy-baileys: Same Session, New Account

On 23 March 2026, a new npm account named zaldy published zaldy-baileys at 11:03 UTC. The sauruslord account published sauruslord-baileys 2.0.1 at 11:45 UTC that same morning, 42 minutes later. Two accounts, one publishing session. The shared timing brackets a single operator coordinating multiple npm identities to expand coverage of the Baileys namespace across a broader set of user search queries.

zaldy-baileys carries a single version (2.0.0) and is confirmed malicious under MAL-2026-10663.

@sauruslord/libsignal: The Filesystem Implant

Three days after the zaldy/sauruslord coordinated publish, on 26 March 2026, the sauruslord account published @sauruslord/libsignal version 2.0.1 at 12:40 UTC. Four minutes and 19 seconds later, sauruslord-baileys 2.0.2 appeared at 12:43 UTC. The sub-5-minute gap establishes a coordinated publishing session linking @sauruslord/libsignal directly to the dropper package.

@sauruslord/libsignal names itself after Open Whisper Systems’ Signal protocol library (libsignal-node), a dependency used by @whiskeysockets/baileys for end-to-end encryption. The impersonation is targeted: developers auditing a Baileys-based project who see @sauruslord/libsignal in a dependency tree may assume it is a fork or wrapper of the legitimate Signal library.

What it actually does is documented in the Amazon Inspector analysis attached to MAL-2026-10659. The payload is recovered from the install.js file (SHA-256: 17ea4f900139649f6c1e9c082601673cf8d22c15019ebc265a48c3f4f47b4880).

Payload Analysis: @sauruslord/libsignal

On require() of index.js, after a 1-second delay, install.js executes. It traverses the installer’s node_modules directory to locate the @whiskeysockets/baileys installation and overwrites lib/Socket/newsletter.js with an attacker-supplied stub.

The replacement stub does two things. First, it schedules a setTimeout that fires 120 seconds after the host application starts and silently invokes:

@sauruslord/libsignal install.js: attacker-injected WhatsApp follow call
newsletterWMexQuery('120363425694844039@newsletter', QueryIds.FOLLOW)

This call uses the already-authenticated WhatsApp session belonging to the application’s user to follow an attacker-controlled WhatsApp newsletter channel with the identifier 120363425694844039@newsletter. The follow happens 120 seconds after startup on every subsequent run of the host application. There is no prompt, no notification, and no visible effect in the WhatsApp UI visible to the user at the moment the follow fires.

Second, install.js schedules process.exit(0) 20 seconds after import. The host application terminates 20 seconds after the package is loaded. This is not a side effect. It is deliberate: the exit gives the installer plausible cover (“the app crashed during setup”) while ensuring the payload ran and the file on disk was rewritten before anything else could interfere.

The original newsletter.js is destroyed. The restoreBackup export referenced in the replacement stub never writes any backup. There is no recovery path short of reinstalling @whiskeysockets/baileys from a clean source.

The package also declares crypto, fs, and path as npm package dependencies rather than referencing them as Node.js builtins. These identifiers do not exist as published npm packages with legitimate functionality. Listing them forces npm to resolve them from the registry, pulling in whatever is registered under those names at install time.

The behavior extracted from the Amazon Inspector sandbox analysis of version 2.0.2, combined with the publish timing pattern, establishes @sauruslord/libsignal as the payload-delivery hub of the Wave 2 cluster.

Wave 2 Version Timeline

PackageVersionPublished (UTC)Notes
sauruslord-baileys2.0.02026-03-15 05:352.x series opens; 11-day gap from 1.1.9
zaldy-baileys2.0.02026-03-23 11:03New account; same session as sauruslord-baileys 2.0.1
sauruslord-baileys2.0.12026-03-23 11:4542 minutes after zaldy publish
@sauruslord/libsignal2.0.12026-03-26 12:40libsignal impersonation introduced
sauruslord-baileys2.0.22026-03-26 12:434 minutes 19 seconds after libsignal; coordinated session
sauruslord-baileys2.0.3 through 2.1.02026-03-27 to 2026-04-142.x continues; 8 additional versions
@sauruslord/libsignal2.0.22026-06-10 19:5858-day gap; libsignal updated; Amazon Inspector confirmed malicious
sauruslord-baileys3.0.02026-06-10 20:034 minutes 45 seconds after libsignal 2.0.2; 3.x series launches
sauruslord-baileys3.0.12026-06-10 20:1815 minutes later
sauruslord-baileys3.0.22026-06-10 20:323.x complete within 29 minutes of libsignal update

The June 10 session is the most concentrated in the campaign. The sauruslord account updated @sauruslord/libsignal, then published three versions of sauruslord-baileys under a new major version scheme in under 30 minutes. The 3.x bump mirrors the version scheme used by @fhkry/x-baileys, which launched the following morning.

Wave 3: The @fhkry Account and 43 Versions in Eight Days (April and June 2026)

Package Anatomy

The fhkry account first appeared on 2 April 2026 with @fhkry/baileys-v2 version 1.0.0, published at 01:07 UTC. The early-morning timestamp for a lone single-version publish, combined with the 1.0.0 version bump while the sauruslord account was mid-campaign on its 2.x series, marks this as account infrastructure preparation.

The package.json metadata recovered from the @fhkry/baileys-v2 1.0.0 version record (still visible in registry metadata before the security hold wiped the tarball) shows the identical preinstall script fingerprint that links it to the sauruslord-baileys payload architecture:

@fhkry/baileys-v2 1.0.0 package.json (recovered from registry metadata)
{
  "scripts": {
    "preinstall": "node ./engine-requirements.js"
  }
}

The filename engine-requirements.js appears as an Amazon Inspector evidence file in the sauruslord-baileys 3.x advisory and as the preinstall target in @fhkry/baileys-v2. Shared payload filename across two different npm accounts is the binding technical fingerprint connecting the sauruslord and fhkry clusters as one operator.

The fhkry account registered the publisher email styjr13@gmail.com, extracted from the @fhkry/baileys-v2 1.0.0 version metadata. The domain is gmail.com, consistent with a throwaway registration. We checked the styjr13 identifier against the OSSF malicious-packages GitHub repository and the kmsec DPRK research feed and found no prior reporting of this email or username in either source.

@fhkry/x-baileys: 43 Versions, Eight Days

The fhkry account’s most aggressive package is @fhkry/x-baileys, which began publishing at 03:23 UTC on 11 June 2026, seven hours after the Wave 2 libsignal and 3.x sauruslord session closed. Forty-three versions were published across eight days, from 1.0.0 through 6.3.0, with the densest activity on 16 June when 15 versions shipped in a single day.

The version timeline shows the operator pushing through minor version increments in bursts of 10 to 20 minutes before jumping to a new major version. The jump from 2.9.0 to 3.0.0 took three minutes and 28 seconds. The jump from 4.9.0 to 5.0.0 took seven minutes. This is not release management. It is payload generation.

Wave 3 Version Timeline (Selected)

PackageVersionPublished (UTC)Notes
@fhkry/baileys-v21.0.02026-04-02 01:07Account infrastructure; engine-requirements.js preinstall confirmed
@fhkry/x-baileys1.0.02026-06-11 03:237 hours after Wave 2 June session; active iteration begins
@fhkry/x-baileys1.1.0 through 1.7.02026-06-11 to 2026-06-16Minor version chain
@fhkry/x-baileys2.0.02026-06-16 13:55Major bump mid-day
@fhkry/x-baileys2.1.0 through 2.9.02026-06-16 14:09 to 17:039 versions in 3 hours
@fhkry/x-baileys3.0.02026-06-16 17:063 minutes after 2.9.0
@fhkry/x-baileys4.9.0 to 5.0.02026-06-17 07:21 to 15:508-hour pause before 5.x
@fhkry/x-baileys6.0.0 through 6.3.02026-06-18 02:33 to 03:12Final session; last publish before detection

The last recorded publish across the entire cluster is @fhkry/x-baileys 6.3.0 at 03:12 UTC on 18 June 2026. The npm security hold across all six packages was applied on 15 July 2026.

OPSEC Failures

The engine-requirements.js filename appears verbatim as both the preinstall target in @fhkry/baileys-v2’s published package.json and as a confirmed malicious evidence file in the sauruslord-baileys 3.x Amazon Inspector report. The operator used the same payload filename across two different npm accounts, directly linking the sauruslord and fhkry identities through a single artifact name. If a different filename had been used for the fhkry packages, the cross-account attribution would have required infrastructure-level corroboration. The shared filename produced it from the package manifest alone.

The @sauruslord/libsignal package also declares crypto, fs, and path as npm dependencies, listing Node.js builtins as registry packages. This is a consistent attacker pattern when the dropper’s dependency tree is generated programmatically without correcting for builtin module names, and it surfaces immediately in any forward dependency audit.

IOC Table

IndicatorTypeValueWaveMethod
sauruslord-baileysnpm packageAll versions (1.0.2 through 3.0.2)1, 2Confirmed malicious in MAL-2026-10661; registry time field audited
@sauruslord/baileysnpm package1.0.0, 1.0.11Confirmed malicious in MAL-2026-10657; registry time field audited
@sauruslord/libsignalnpm package2.0.1, 2.0.22Confirmed malicious in MAL-2026-10659; Amazon Inspector sandbox detonation
zaldy-baileysnpm package2.0.02Confirmed malicious in MAL-2026-10663; registry time field audited
@fhkry/baileys-v2npm package1.0.03Confirmed malicious in MAL-2026-10664; preinstall script recovered from registry metadata
@fhkry/x-baileysnpm packageAll versions (1.0.0 through 6.3.0)3Confirmed malicious in MAL-2026-10665; registry time field audited
engine-requirements.jsMalicious preinstall payloadSHA-256: 9b0ac7033c5a7f5646f4bdbe08fe542a2d26031fd5f4ca1f082f6972ee383a451, 2, 3Recovered from sauruslord-baileys evidence files (Amazon Inspector); filename confirmed in @fhkry/baileys-v2 registry metadata
lib/index.jsMalicious module payloadSHA-256: 69a6ca2dcaf905a690fbf65030aa486217975358a54077c7b05bce9cf2bfa1da2Recovered from sauruslord-baileys 3.x evidence files (Amazon Inspector, MAL-2026-10661)
install.jsFilesystem implant dropperSHA-256: 17ea4f900139649f6c1e9c082601673cf8d22c15019ebc265a48c3f4f47b48802Recovered from @sauruslord/libsignal 2.0.2 evidence files (Amazon Inspector, MAL-2026-10659)
120363425694844039@newsletterAttacker WhatsApp newsletter channelWhatsApp channel identifier2Extracted from Amazon Inspector detonation analysis of @sauruslord/libsignal install.js; reported in MAL-2026-10659
lib/Socket/newsletter.jsOverwritten victim filePath within @whiskeysockets/baileys2Extracted from Amazon Inspector detonation analysis of @sauruslord/libsignal install.js
styjr13@gmail.comPublisher account emailfhkry npm account3Pulled from @fhkry/baileys-v2 1.0.0 version metadata during registry audit
sauruslordnpm publisher accountWave 1 and 2 operator identity1, 2Pulled from registry metadata _npmUser field across all sauruslord-prefixed packages
zaldynpm publisher accountWave 2 satellite account2Pulled from registry metadata _npmUser field for zaldy-baileys 2.0.0
fhkrynpm publisher accountWave 3 operator identity3Pulled from @fhkry/baileys-v2 1.0.0 version metadata

Affected Versions

PackageVersionPublished (UTC)Current StatusOSV Entry
@sauruslord/baileys1.0.02026-03-05 15:18Security hold (0.0.1-security)MAL-2026-10657
@sauruslord/baileys1.0.12026-03-05 15:27Security holdMAL-2026-10657
sauruslord-baileys1.0.2 through 1.1.92026-03-05 to 2026-03-13Security hold (0.0.1-security)MAL-2026-10661
sauruslord-baileys2.0.0 through 2.1.02026-03-15 to 2026-04-14Security holdMAL-2026-10661
zaldy-baileys2.0.02026-03-23 11:03Security hold (0.0.1-security)MAL-2026-10663
@sauruslord/libsignal2.0.12026-03-26 12:40Security hold (0.0.1-security)MAL-2026-10659
@sauruslord/libsignal2.0.22026-06-10 19:58Security holdMAL-2026-10659
sauruslord-baileys3.0.0 through 3.0.22026-06-10 20:03 to 20:32Security holdMAL-2026-10661
@fhkry/baileys-v21.0.02026-04-02 01:07Security hold (0.0.1-security)MAL-2026-10664
@fhkry/x-baileys1.0.0 through 6.3.0 (43 versions)2026-06-11 to 2026-06-18Security hold (0.0.1-security)MAL-2026-10665

Remediation

Determine whether any of the six packages appear in package.json, package-lock.json, or node_modules in any project or container image built between 5 March 2026 and 15 July 2026. Check yarn.lock and pnpm-lock.yaml for lockfile entries under the package names above. If any are found:

  1. Remove the packages with npm uninstall <package-name> and delete any cached versions from the npm cache (npm cache clean --force).

  2. If @sauruslord/libsignal was installed in an environment that also contained @whiskeysockets/baileys, treat lib/Socket/newsletter.js within the Baileys installation as compromised. Compare it against the canonical version from the WhiskeySockets repository at the version pinned in your lockfile. Reinstall @whiskeysockets/baileys from a clean npm install into an isolated directory and replace the file.

  3. Review all WhatsApp sessions authenticated from any application that loaded @sauruslord/libsignal. The session was used to follow the attacker-controlled newsletter channel 120363425694844039@newsletter. Audit active newsletter subscriptions from the affected WhatsApp account and remove any unfamiliar channels.

  4. The fhkry account registered under email styjr13@gmail.com. If this account name appears in any internal dependency audit tooling or allowlist, remove it.

  5. Scan pipeline logs and CI artifact caches for any npm install runs that pulled the affected packages. Treat any environment where these packages were installed as potentially compromised at the WhatsApp session layer, not merely at the package installation layer.

Baileys-based applications authenticate long-lived WhatsApp Web sessions that persist across application restarts. The @sauruslord/libsignal payload exploits this by abusing an already-established session rather than stealing credentials. Credential rotation in the traditional sense does not apply; what requires remediation is the ongoing session itself.

Attack Surface Analysis

@whiskeysockets/baileys has no verified publisher registry. Any npm account can publish a scoped package prefixed with a user-chosen handle (sauruslord, fhkry) and claim Baileys functionality in the package name or description. There is no mechanism short of OSSF scanning or manual review to distinguish a malicious Baileys fork from a legitimate community extension at install time.

The user profile compounds this: Baileys-based applications are typically automation bots or customer service integrations that run with long-lived WhatsApp sessions in unattended server environments. The developer who installs a malicious Baileys variant during local testing may never connect the subsequent unexpected process.exit() to a session compromise. The 120-second delayed follow in the @sauruslord/libsignal payload is calibrated for exactly this: it fires after startup is complete, when the developer has already moved on.

We found no prior reporting of the sauruslord, zaldy, or fhkry accounts in the kmsec DPRK research feed, the OSSF malicious-packages GitHub repository beyond the current advisories, or any Wiz Research, StepSecurity, Unit 42, or Aikido Security publications. The campaign is distinct from the Sha1-Hulud and Miasma worm-style clusters documented in 2025.