Trusty is a free-to-use web app that provides data and scoring on the supply chain risk for open source packages.
Over recent days, Stacklok has identified a new wave of malicious NPM package activity from DPRK-aligned threat actors targeting developers and jobseekers in the cryptocurrency, NFT, and Web3 sectors. These packages are a key early stage component of a complex, layered attack chain designed to harvest cryptocurrencies and establish persistent access to compromised developer machines.
These objectives are achieved by embedding a cross-platform JavaScript information stealer and loader known as BeaverTail within copies of legitimate NPM packages. BeaverTail fetches InvisibleFerret, a multi-component Python payload responsible for further sensitive data exfiltration and remote control capabilities.
The attack chain is triggered when unsuspecting job applicants, often lured through fake recruitment efforts, are directed to clone GitHub repositories that include the malicious NPM packages as a dependency. This general form of social engineering via fake job interviews is a common initial access vector associated with North Korean threat actors, typically using LinkedIn to establish contact.
The TTPs and attack infrastructure involved are consistent with a continuation of the campaign previously dubbed Contagious Interview by PaloAlto Unit42 last year.
The threat actors behind the ongoing operation have recently experimented with delivering BeaverTail and InvisibleFerret via a MacOS disk image (dmg) imitating MiroTalk, a video call application.
However, this set of packages is largely in line with the earlier JavaScript-based attack variants, apart from utilizing different styles of obfuscation when compared to previous samples.
Malicious NPM packages detected
Stacklok’s package analysis platform, Trusty, alerted us to three suspicious npm packages without a verified claim to a source repository. All three were published by the same author, richard_dev
. Our static code analysis system had also flagged the presence of obfuscated JavaScript code within all 3 of the packages.
The three identified malicious NPM packages were designed to mimic popular NodeJS packages:
ethersscan-api falsely claimed to be associated with the legitimate etherscan-api repository, likely to typosquat unsuspecting users in the Ethereum community.
eslint-module-conf linked itself to eslint-plugin-import, a package with over 22 million weekly downloads.
eslint-scope-util claimed to be connected to eslint-scope, which had been deprecated in favor of a monorepo.
All three contained an additional, heavily obfuscated JavaScript source code file, sometimes hidden within subdirectories such as lib to evade detection.
Pivoting from the npm packages we discovered, we were able to find an example of a GitHub repository utilized in sophisticated social engineering attacks involving job interviews in the DeFi or Web3 space. The threat actors will encourage targeted developers to clone a repository as part of a coding challenge or technical assessment, which will either directly contain malicious code or be dependent upon a malicious package.
The NFT_Marketplace project lists an earlier version of ethersscan-api
(0.0.3, published 23rd August) as a dependency for the private NodeJS package nuron-nextjs
. Although newer versions of ethersscan-api
have since been released, the core functionality of the package remains largely unchanged. Hence it is assumed that this case is still relevant as an illustration of possible malware delivery through dependencies in open source projects.
Hidden within /backend/utils/apiFeatures.js
is a call to a function from the malicious NPM package.
Nothing looks egregiously out of place here, but checking the source code of the dependency package, we see that inside init.js
, hash-blob.js
is pulled in with require
and used as an argument in the exported function.
By contrast, the legitimate package does not contain such an import, and does not pass a hash parameter.
This means that whatever is contained in the injected code, hash-blob.js
, will be executed when the victim of the fake job process runs the Node project after cloning it from GitHub. This level of layering helps evade detection.
All three of the analyzed packages contain almost identical variants of BeaverTail distributed as heavily-obfuscated JavaScript. Taking the most recent package uploaded as an example, resolve.js
(view in full in our Jail repo), the following obfuscation techniques are evident:
Self-invoking functions (IIFE)
Hexadecimal encoding
Control flow obfuscation
String manipulation
These methods are characteristic of obfuscation via javascript-obfuscator
, a more basic option than those employed in earlier BeaverTail variants.
Removing the initial layers of obfuscation, the functionality of the script becomes more apparent.
The dual-purposes of stealing and loading subsequent stages were sufficiently visible enough to avoid fully deobfuscating the script.
After gathering some basic system information, the BeaverTail script dives into its cross-platform infostealing capabilities, targeting sensitive browser database files for credentials, and enumerating the machine’s browsers for cryptocurrency wallet extensions.
Extension ID | Extension Name |
nkbihfbeogaeaoehlefnkodbefgpgknn | Metamask Wallet (Chrome) |
ejbalbakoplchlghecdalmeeeajnimhm | Metamask Wallet (Edge) |
fhbohimaelbohpjbbldcngcnapndodjp | Binance Wallet |
hnfanknocfeofbddgcijnmhnfnkdnaad | Coinbase Wallet |
ibnejdfjmmkpcnlpebklmnkoeoihofec | TRON Wallet |
bfnaelmomeimhlpmgjnjophhpkkoljpa | Phantom Wallet |
aeachknmefphepccionboohckonoeemg | Coin98 Wallet |
hifafgmccdpekplomjjkcfgodnhcellj | Crypto.com Wallet |
jblndlipeogpafnldhgmapagcccfchpi | Kaia Wallet |
acmacodkjbdgmoleebolmdjonilkdbch | Rabby Wallet |
dlcobpjiigpikoobohmabehhmhfoodbb | Argent X - Starknet Wallet |
aholpfdialjgjfhomihkjbmgjidlcdno | Exodus Web3 Wallet |
It includes checks for MacOS-specific targets such as Solana ID files and iCloud Keychain.
The harvested files are then exfiltrated to a known North Korean C2 server, 95.164.17[.]24:1224
. This server has been associated with state-sponsored operations for several months.
The blob posted to the C2 is prepended with the campaign ID and the machine hostname.
The more critical aspect of the BeaverTail script is its ability to download and execute additional payloads.
In this case, a Python script with the extension .npl is downloaded from a remote server with a URL of the format http://<c2>:1224/client/<campaign_ID> (e.g., 3/525 here) and saved directly into the user’s home directory (referenced by the variable _0x10e868).
This is the first component of the multistage Python malware known as InvisibleFerret.
Execution of the script is ensured by the download of a Python binary if it is not already installed.
InvisibleFerret is a Python-based malware delivered in multiple stages:
Stage 1: Downloads and executes subsequent payloads based on the host OS.
Stage 2: Implements RAT (Remote Access Trojan) capabilities, including keylogging and system fingerprinting.
Stage 3: Executes browser-stealing operations, targeting stored credentials and sensitive data in the victim's browser.
Initial Installer
This first script, .npl
, is again heavily obfuscated.
It consists of an anonymous function that takes a single argument __
. It:
Reverses the string __
.
Decodes the reversed string from base64 format using base64.b64decode
.
Decompresses the base64-decoded data using zlib.decompress
.
Knowing this, we can iteratively extract the argument string and follow this decoding and inflation pattern to unwind 50 layers of encoding and compression, leaving us with the underlying script.
Once fully deobfuscated, the script fetches additional components from the attacker's C2 server and executes them, depending on the host operating system.
For all operating systems, http://<c2_server>/payload/<campaign_id>
is fetched and written to a hidden path, .n2/pay
under the home directory, before being executed with subprocess.Popen
.
If the OS is Darwin (MacOS), the script then exits after the first stage. For all other OS, a tertiary payload is retrieved from the /brow/
path, saved to .b2/bow
, and executed.
The second component, .n2/pay
, contains the core RAT-like functionality of InvisibleFerret.
Machine fingerprinting
Keylogging and clipboard logging
Remote command execution
Executing a tertiary component
Downloading AnyDesk
Regular check-ins with C2 server
The same compression and encoding routine used in the earlier stage has been applied here and can be removed in a similar fashion to extract the unobfuscated Python payload for analysis.
InvisibleFerret gathers detailed information on the local host OS and hardware attributes, along with the geographic location associated with the IP address, in order to fingerprint the victim.
The fingerprint is then crafted into JSON format and uploaded to the C2 server.
The libraries pyHook
and pyperclip
are utilized to continually log keystrokes and clipboard content upon copy and paste operations.
The other script downloaded by the first stage, bow
, is executed using the ssh_run
function.
The Shell
class, a snippet of which can be seen below, defines many functions to allow the operator to interact with the agent.
The backdoor waits for instructions from the C2 server, which are JSON formatted and contain one or more of the 8 available arguments.
Command execution
Closing the beaconing client session
Sending the logged keystrokes and clipboard data
Running the browser stealer
File upload to FTP
Kill browser processes
Download AnyDesk
Exfiltrate specific user folders
It uploads the results of these commands in JSON over a socket connection.
Whilst the tertiary component, .n2/bow
, is only downloaded if the host OS is not MacOS, the script itself contains comprehensive cross-platform support. Unlike earlier Python payload files, this final script was not hidden behind a compression routine, and is largely unobfuscated.
It consists of almost 500 lines of meticulous, documented data extraction functionality for Chrome, Edge, Brave, Opera, and Yandex browsers. It interacts directly with browser databases using sqlite3
, implementing password decryption tailored for each operating system.
Another key feature is the retrieve_web
function, which queries the browser databases for credit card information.
After we confirmed all 3 NPM packages to be malicious, we reported our findings to the NPM Security team and the OSV malicious packages database on 7th September. By 9th September they were removed from the NPM registry.
During the period they were live, the packages were downloaded a combined 341 times:
Package | Download count |
ethersscan-api | 91 |
eslint-module-conf | 107 |
eslint-scope-util | 143 |
It is likely a significant proportion of these downloads will have been from security tooling and automated tools, seeing as we expect the attacks to be reasonably targeted, but we cannot confirm this. As such, the full extent of the compromise remains uncertain.
During this investigation Stacklok uncovered a new variation of the combined BeaverTail and InvisibleFerret tooling used by DPRK-aligned threat actors in attacks abusing the open source supply chain.
The delivery mechanism - embedding JavaScript malware as a NodeJS dependency within a seemingly legitimate GitHub repository - highlights the vulnerability of open-source ecosystems to such attacks. The additional malicious code which kicked off the infection chain was abstracted away from inspection by the user and, in many cases, automated security tools.
While this incident involved the relatively simple case of a direct dependency - the complexity and resultant risk increases exponentially when considering transitive dependencies - indirect dependencies pulled in by third-party libraries. These nested dependencies increase the difficulty of identifying and mitigating security threats, expanding the attack surface.
Threat actors are increasingly exploiting this web of complexity. The security of the open-source supply chain relies on maintaining visibility and trust across every layer of the development process.
Name | SHA256 | ssdeep |
| b8a68c5c25e586319481603ddab11276f66965a4701f89abc181308edc1bdb53 | 96:I7XQcKxhwlRPKDU09c7RDXSi1z6V3821GppAqNMU00ELB:gXHKxKld50ed1z6Vd4rAquU01 |
| 2b7c7df496c6aff2f4339ad6b9dcc5bb43c81898d29332fd5378874f896a73dd | 384:mBQ4EMdjMqJvfZbjLTjcamTfSioCph5ZX2hmzc2h1pGNKKfNpjoNCEsY:meHMmqVBjL/YTfRpbZX2YIUGxfN9A3 |
| d141bc9b5664a906ec501781edf7b7af2f8640b067fd90c7f36876cba764807b | 192:HymQjtIkGN5V2kbeDA9rRbWfgjvG+LcIzfJ78pnS35lCz4218kG42RtnkLjVpiKt:WIk85VyAJs4jvG+/epSpMmtkXVpT |
C2 Server: 95.164.17[.]24:1224
Poppaea McDermott
Security Researcher