https://opensourcemalware.com/blog/introducing-weaselbiscuit WeaselBiscuit Strips BeaverT Essentials By c0a15726-c5b1-4b0d-85e6-fe15553df9e2 Published: 2026-09-17 · Archived: 2026-09-25 02:01:34 UTC The OpenSourceMalware team spends a lot of time looking at malicious npm packages, and after a while you start seeing the same patterns over and over. So when something new and genuinely different shows up, it’ stopping for. That happened this week when we found 11 npm packages secretly hiding a brand new JavaScript stealer. It looks and feels like the DPRK’s BeaverT lighter, and stripped down, with many of the heavier functions removed entirely. We're calling it WeaselBiscuit. It isn’t an established family name. I made it up, and I think it’ Our automation flagged these packages as malicious and grouped them together because they share IOCs. That’s when we recognized an emerging Node.js infostealer architecture worth investigating, one that reads as a possible new or lightly documented DPRK-linked strain, or as a simplified branch or fork of the BeaverT ecosystem. You can see all the threat reports under the #weaselbiscuit tag. Package @biz44/id10-client @biz44/id12-client @biz44/id44-client @biz44/id79-client @biz44/id95-client @biz44/id99-client ail and OtterCookie Down to ail and OtterCookie, but it isn’t quite either one. It’ First seen 2026-09-12T13:51:46Z 2026-09-12T13:51:47Z 2026-09-12T08:03:37Z 2026-09-12T13:51:48Z 2026-09-12T13:51:46Z 2026-09-12T13:51:46Z Page 1 of 16 s worth s smaller, s dope. ail/OtterCookie Package @biz44/process-runtime-utils @biz44/runtime-utils @railone/image-utils @vibecheck-polid/process-runtime- utils engin1 id79-client laycot process-lhpm process-mite process-tailwind We're tentatively attributing this malware to DPRK but it's important to be clear that it's an analytic hypothesis, not a confirmed attribution. The recovered code has meaningful overlap with DPRK-associated Contagious Interview tooling, but we haven’t recovered operator infrastructure, victimology material, or a unique-code comparison sufficient to name a new family or conclusively attribute it to DPRK. https://opensourcemalware.com/blog/introducing-weaselbiscuit First seen 2026-09-14T04:41:47Z 2026-09-14T03:39:58Z unknown unknown 2026-09-16T03:44:28Z (version 1.3.99 still live on NPM) 2026-09-12T13:35:09Z 2026-09-16T13:51:46Z (version 1.3.10 still available on NPM) 2026-09-15T00:44:34Z 2026-09-16T23:57:52Z 2026-09-15T00:20:01Z , campaign metadata, signing Page 2 of 16 https://opensourcemalware.com/blog/introducing-weaselbiscuit What WeaselBiscuit does Lands via an npm import and auto-runs a detached background Node process Page 3 of 16 https://opensourcemalware.com/blog/introducing-weaselbiscuit Pulls its real payload from an Npoint URL and runs it in memory Beacons to a shared HTTP C2 at 103.170.217.184:8787 Profiles the host: hostname, user, OS, CPU/RAM, local and public IP Steals Chrome extension storage on Windows, macOS, and Linux, which is exactly where wallet extensions keep their signing state Captures clipboard contents, on operator command Logs Windows keystrokes, on operator command Tags each install with a numeric campaign ID ( 10 , 12 , 44 More on exactly what gets uploaded, and what doesn’t, in the Capabilities section below. What it doesn’t do, compared to BeaverTail and OtterCookie: No wallet-draining code, no hardcoded wallet extension ID list No Chrome password decryptor, no seed-phrase regex sweep No InvisibleFerret Python second stage No screenshot module No Socket.IO, WebSocket, or remote shell, just plain polling HTTP No persistence beyond the detached Node process and a .pid Why it’s notable The implant combines a package-borne Node.js loader, Npoint dead-drop resolution, Chrome extension-storage theft, clipboard collection, Windows keylogging, and a small custom HTTP control plane. Operationally simpler than commonly documented OtterCookie deployments: npm import -> detached Node loader -> Npoint-delivered JavaScript -> Npoint C2 configuration -> plain HTTP C2 polling and exfiltration There’s no Socket.IO, no remote shell, no screenshot capability password decryptor, no hardcoded wallet list, and no server-delivered follow-on payload in the recovered client. That absence could reflect an early-stage build, a deliberately reduced operational module, or a distinct actor copying familiar tradecraft. , no Python InvisibleFerret handoff, no browser- Page 4 of 16 , never touching disk , geolocation , 79 , 95 , 99 ) for server-side sorting file , it’s https://opensourcemalware.com/blog/introducing-weaselbiscuit Confirmed infection chain 1. An application imports process-tailwind@1.1.99 . 2. index.js automatically calls initialize() . 3. init.js starts a detached node loader.js process and stores its PID in /.pid . 4. loader.js retrieves JSON from: https://api.npoint.io/24c25d5f5fcbb0992a4f 5. The loader Base64-decodes the JSON code field and executes it through new Function . 6. The retrieved response is an exact match for the supplied second-stage artifact, SHA-256: 7b15605f23b131b3eeea57e031ae7cb32fc4b78c7bbb2025aa7a561ea5ae5159 7. The second stage obtains the C2 configuration from: https://api.npoint.io/37c0a0c68bf7a94ed731 8. The configuration resolves the C2 to: http://103.170.217.184:8787 Page 5 of 16 https://opensourcemalware.com/blog/introducing-weaselbiscuit Campaign structure and identifiers WeaselBiscuit isn’t an isolated sample. Static analysis and user-authorized inert retrievals identified a cluster of cloned npm loaders that pull near-identical WeaselBiscuit stages from distinct Npoint URLs. The stages differ Page 6 of 16 https://opensourcemalware.com/blog/introducing-weaselbiscuit materially only in a numeric identifier field included in system-information and Chrome-extension uploads, which strongly suggests a server-side campaign, tenant, operator, or victim-group label. That clustering is easier to see than to describe. The graph below maps the shared infrastructure our platform recovered across the cluster, with process-tailwind resolver URLs, and the 03.170.217.184 C2 to id79-client the tables below. It contains nineteen nodes, eight of them tied together by shared indicators rather than naming similarity alone. Every edge in this graph is a shared hash, resolver URL, or C2 address, not an assumption First-stage resolver https://api.npoint.io/24c25d5f5fcbb0992a4f https://api.npoint.io/641d37178a880b1e8b8f at the center, connected through shared file hashes, Npoint , engin1 , and the Embedded stage identifier 99 10 Page 7 of 16 @biz44 packages covered in Status Confirmed process-tailwind payload Confirmed swnwall payload https://opensourcemalware.com/blog/introducing-weaselbiscuit Embedded stage First-stage resolver Status identifier https://api.npoint.io/33e8d008c334b060adad 79 Confirmed WeaselBiscuit clone https://api.npoint.io/ddae72efbb6714fae922 12 Confirmed WeaselBiscuit clone https://api.npoint.io/933a731a5e97f4b45249 95 Confirmed WeaselBiscuit clone Hard-coded by id79-client ; https://api.npoint.io/24c12c4b66a29747764f Unknown returned HTTP 404 when retrieved All five recovered stages use the same second Npoint configuration resolver, https://api.npoint.io/37c0a0c68bf7a94ed731 , which supplied the same C2: http://103.170.217.184:8787 . The confirmed stage-level identifiers are 10 , 12 , 79 , 95 , and 99 . They’re useful as campaign-hunting markers, but their precise meaning isn’t yet known. The code doesn’t label them as campaign IDs. Package Package label First-stage resolver process-tailwind@1.1.99 ID-99 Client Module 24c25d5f5fcbb0992a4f engin1@1.3.99 ID-99 Client Module 24c25d5f5fcbb0992a4f swnwall@1.2.10 ID-10 Client Module 641d37178a880b1e8b8f id79-client@1.1.79 ID-79 Client Module 24c12c4b66a29747764f All four package loaders share the same detached Node process, .pid marker, Npoint JSON retrieval, Base64 decoding, and dynamic new Function execution template. process-tailwind and engin1 have byte-identical loader, init, and index source files. The ID-79 label in id79-client is consistent with the separate recovered stage carrying identifier: "79" , but the package’s current resolver response was unavailable, so that direct delivery link isn’t proven. Page 8 of 16 https://opensourcemalware.com/blog/introducing-weaselbiscuit Capabilities The WeaselBiscuit infostealer uploads the following to the IP-hosted C2: Hostname, username, OS details, CPU, memory public IP, and public-IP geolocation Every readable, nonempty file under Chrome profiles’ Windows, macOS, and Linux Changed clipboard contents, when the server enables monitoring Windows keyboard events, when the server enables monitoring While this malware does not have the same crypto wallet stealer functions as its big siblings, the Chrome extension-storage capability is financially relevant: it can expose wallet-extension state or other extension-held sensitive data. It uploads every readable, nonempty file directory — a raw LevelDB key/value store — wholesale. That store legitimately contains a mix of: The victim's own account address(es) — typically a small number Wallet extensions like MetaMask and their internally cached token/contract address list (used for balance display, price feeds, swap routing) Address-book/contact entries (other people's addresses, not the victim's) Various other cached metadata Meanwhile, clipboard and keylogging can capture credentials, entered or copied during normal use. However, the code does not password decryption, seed-phrase searching, or cryptocurrency transaction functionality. C2 design The C2 is a plain Express HTTP service, and its client-facing interface is small: Route /api/system-info /api/upload-local-extension-settings Page 9 of 16 , home/temp paths, local interface IP/MAC addresses, Local Extension Settings directories on under the extension's Local Extension Settings API tokens, wallet addresses, or recovery phrases contain direct wallet draining, browser- Method multipart POST multipart POST Function Host reconnaissance report Chrome extension storage https://opensourcemalware.com/blog/introducing-weaselbiscuit Route Method /api/clipboard-status/ GET /api/clipboard-data JSON POST /api/keyboard-mouse-status/ GET /api/keyboard-mouse-data JSON POST The client polls the two status routes every five seconds. Recorded responses for a synthetic host were Express JSON responses with isMonitoring: false . No payload, command, redirect, or next-stage URL was observed in those responses. That’s a real departure from documented OtterCookie variants, which use Socket.IO and can support broader command execution. This is a polling infostealer control plane, not a full interactive RA code. How it compares to BeaverTail and OtterCookie The overlap supporting the DPRK hypothesis is behavioral, not dispositive: Node.js delivery and execution in a developer/package ecosystem Browser extension-storage theft with potential wallet relevance Native clipboard collection using Get-Clipboard and pbpaste Keylogging, host profiling, and HTTP exfiltration Use of lightweight external configuration to decouple the loader from C2 infrastructure The divergences matter just as much: No Socket.IO, WebSocket, or remote-shell client No screenshots No explicit crypto-wallet extension IDs, browser credential databases, or broad file-targeting rules No InvisibleFerret/Python downloader Page 10 of 16 Function Clipboard collection switch Clipboard exfiltration Keylogger switch Keystroke exfiltration 200 OK T, at least in the recovered https://opensourcemalware.com/blog/introducing-weaselbiscuit No observed persistence beyond a detached process and a local PID marker Cisco Talos has reported that the line between BeaverT including Node.js keylogging, clipboard monitoring, extension and wallet data targeting, and shifting C2 architectures. That makes a lineage relationship plausible, but it doesn’t prove it. See BeaverT evolve with a new JavaScript module. Capability comparison Capability Runtime Delivery Staging C2 channel Host reconnaissance Public IP + geolocation (nested ipify to ip- api ) Chrome extension storage theft Hardcoded crypto-wallet extension ID list BeaverTail JavaScript (also ported to Qt/native) Fake-interview lure + malicious npm packages Loaded directly by lure package HTTP POST to hardcoded C2 Yes Yes Yes Yes Page 11 of 16 ail and OtterCookie has blurred in recent campaigns, OtterCookie Node.js Malicious npm packages Loaded directly by lure package Socket.IO (bidirectional) Yes Yes Yes Yes ail and OtterCookie WeaselBiscuit Node.js Malicious npm packages Npoint dead-drop, Base64, in-memory new Function Polling HTTP (Express routes) Yes Yes Yes No Capability Browser credential DB decryption Seed-phrase / wallet- address regex sweep File exfiltration (documents, keystores, profiles) Clipboard capture Keylogging Screenshot capture Remote shell / arbitrary command execution InvisibleFerret / Python second stage Persistence Per-install campaign tagging in code https://opensourcemalware.com/blog/introducing-weaselbiscuit BeaverTail Yes Yes Yes Variant-dependent Not typical No Via InvisibleFerret handoff Yes LaunchAgent / registry / startup entries Not observed publicly Page 12 of 16 OtterCookie Partial (variant- dependent) Variant-dependent Yes Yes Yes (recent variants) Yes (recent variants) Yes (over Socket.IO) Yes (some variants) Detached process + variant-specific Not observed publicly WeaselBiscuit No No No (extension storage only) Yes (operator-gated) Yes, Windows only (operator-gated) No No No Detached Node process + .pid marker only Yes, numeric identifier ( 10 , 12 , 44 , 79 , 95 , 99 ) Confidence and gaps Claim Package is a malicious staged Node.js loader Second stage is an infostealer 103.170.217.184:8787 the C2 used by this stage The implementation is a simplified or new branch Linked to BeaverTail/OtterCookie lineage DPRK attribution A wholly new malware family Three of those signals are worth walking through in more detail. Tradecraft signals worth flagging Npoint.io as the dead drop. long time, and WeaselBiscuit’ containing a Base64 pattern. https://opensourcemalware.com/blog/introducing-weaselbiscuit Confidence High High is High Moderate Moderate Low to moderate Low DPRK npm crews have been using s usage is nearly identical to prior DPRK samples: hardcoded UUID, JSON blob code field, in-process new Function Basis / limitation Auto-start, detached loader, remote Base64 code execution, and exact recovered payload match Static collection and upload logic Retrieved configuration and observed client routes Distinct architecture and reduced capability set, though it could also be a commodity copy Npoint dead-drop pattern is near-identical to prior DPRK npm samples; nested api.ipify.org to ip-api.com lookup matches DPRK stealer convention; Express polling C2 reads as a lightweight port of OtterCookie’s Socket.IO control plane Consistent DPRK tradecraft signals, but no exclusive infrastructure or shared code recovered Requires code-cluster, infrastructure, and victimology comparison api.npoint.io as a first-stage dead-drop for a execution. Same service, same shape, same execution Page 13 of 16 Nested public-IP feeds that IP to ip-api.com stealers too. It isn’t a common commodity design. Express HTTP C2 as a lightweight port of OtterCookie’s Socket.IO plane. plain Express routes, polling-based, no bidirectional socket. But its shape, with per-host status endpoints gating live collection and separate exfiltration endpoints for clipboard and keyboard, reads as a stripped-down re- implementation of the same control model OtterCookie runs over Socket.IO. Campaign markers resemble PolinRider. baked into both package names and stage uploads mirror the campaign-marker convention seen in the PolinRider cluster. The operator is tracking installs at the same granularity Indicators of compromise Type C2 Npoint resolver Npoint resolver Npoint resolver Npoint resolver Npoint resolver Npoint resolver https://opensourcemalware.com/blog/introducing-weaselbiscuit and geolocation lookup. The second stage queries api.ipify.org for the public IP, then for geolocation. That two-step nested lookup shows up in other DPRK-linked npm The C2 architecture itself is new: The numeric per-install identifiers ( 10 , 12 , 44 , 79 , 95 , 99 ) , using the same “ID in the package name” pattern. Value 103.170.217.184:8787 https://api.npoint.io/24c25d5f5fcbb0992a4f (identifier 99 ) https://api.npoint.io/641d37178a880b1e8b8f (identifier 10 ) https://api.npoint.io/33e8d008c334b060adad (identifier 79 ) https://api.npoint.io/ddae72efbb6714fae922 (identifier 12 ) https://api.npoint.io/933a731a5e97f4b45249 (identifier 95 ) https://api.npoint.io/24c12c4b66a29747764f (identifier unknown, hard-coded by id79-client , HTTP 404 on retrieval) Page 14 of 16 https://opensourcemalware.com/blog/introducing-weaselbiscuit Type Value Npoint C2- config https://api.npoint.io/37c0a0c68bf7a94ed731 resolver Second-stage 7b15605f23b131b3eeea57e031ae7cb32fc4b78c7bbb2025aa7a561ea5ae5159 SHA-256 npm package @biz44/id10-client npm package @biz44/id12-client npm package @biz44/id44-client npm package @biz44/id79-client npm package @biz44/id95-client npm package @biz44/id99-client npm package @biz44/process-runtime-utils npm package @biz44/runtime-utils npm package engin1 (v1.3.99) npm package id79-client npm package process-lhpm Page 15 of 16 https://opensourcemalware.com/blog/introducing-weaselbiscuit Type Value npm package process-tailwind (v1.1.99) npm package swnwall (v1.2.10) Recommended investigation priorities 1. Preserve the npm tarball, package publication metadata, maintainer account, dependency graph, download history, and source repository references. 2. Search public and internal telemetry for all six Npoint UUIDs, the C2 IP and port, the stage identifiers 10 , 12 , 79 , 95 , and 99 , and the API route strings. 3. Compare the loader and decoded stage against known BeaverTail/OtterCookie samples for shared functions, comments, string conventions, package names, and C2 patterns. 4. Identify the importing parent application. The package itself has no npm lifecycle hook, so execution requires an import or a manual start. 5. Review affected endpoints for .pid files, Node child processes, PowerShell processes, and %TEMP%\kb- monitor\keyboard-monitor-*.ps1 . Bottom line Treat process-tailwind as a high-confidence malicious supply-chain package, and treat it as a potentially important data point in how DPRK-linked JavaScript infostealers are evolving. Treat the “new DPRK strain” label as a working investigative hypothesis, not a conclusion, until it’s backed by corroborating code, infrastructure, or campaign evidence. Source: https://opensourcemalware.com/blog/introducing-weaselbiscuit Page 16 of 16