The Safety research team's automated malicious package detection engine has identified a threat campaign targeting Asian banks with a new JavaScript RAT and C2 that we have dubbed "DHSollutionsBot". The name is based on a AI generated profile image the threat actor created for their NPM user profile. The threat actor's NPM account is named "ongtrieuhau861.001" and they use the full name "Ông Triệu Hậu".

So far, 94 packages are flagged in this attack
In total the ongtrieuhau861.001 user has 94 active NPM packages in the registry. Many, but not all of those packages are malicious.
The bulk of these packages where published between May and July of 2025, but some of them are more than 10 months old. This threat actor created their NPM account in 2019, or alternatly, it's possible that this account was sold or stolen by the threat actor.
The most recent versions of the NPM packages mostly use the naming construct dhhdbankxxxxx, dhpgemrdhsxxxxx, ldhemrdhsxxxxx, and ldhpgemrdhsxxxxx, where the "x" are numbers. So for example dhhdbank83600. All of the packages named in this way appear to be malicious in the same way, while many of the other, older packages are malicious, but use different, simpler payloads.
For the purposes of this blog we will focus on the newer packages.
This NPM attack represents a particularly gnarly example of supply chain malware, targeting Asian banks while operating as a full-featured Remote Access Trojan. The attack architecture is elegant in its simplicity—leveraging Firebase Realtime Database as a command and control server while exfiltrating stolen data through Discord webhooks. This dual-channel approach provides both resilience and stealth, as both services are legitimate platforms that rarely trigger security alerts.
Important information is found in the package.json file
While its common for threat actors to use the install scripts in package.json, it's less common to use the manifest file for storing important variables. However, with this malware, you can see that the threat actor has defined the "databaseUrl" key with a value of the Firebase URL "o23863162-02-default-rtdb.asia-southeast1[.]firebasedatabase[.]app". They have also defined a "discordUrl" key with the value "discord[.]com/api/webhooks/1396760653687029901/"

One of the other things the threat actor does is set what is effectively an alias that sets the command dhhdbank83600 to the pm2.js file. The pm2.js file in turn runs pm2.updater.js.
pm2.updater.js: Gathering sensitive data
The purpose of the pm2.updater.js file is to gather data about the compromised host. To do this it needs to know where its C2 and exfiltration endpoints are. In a relatively unusual twist, the threat actor sources those values from the package.json file:

I've seen this before when threat actors want to sneak past malicious package detection by putting important components like exfiltration URLs or C2 servers in markdown or other files, as they aren't analyzed in the same way. However, it means that these values are in plain site in the package manifest file.
How it starts
The payload's infection chain begins innocuously through npm installation, immediately establishing persistence through multiple redundant mechanisms. The malware identifies if its on a Linux host, and if so, installs itself as a PM2-managed process, ensuring automatic restart on failure or system reboot.
For Windows systems, it goes further by deploying a Windows service configuration via winsw.xml, embedding itself deep into the operating system's service layer:

Installing the Windows Service Wrapper
How it does this is sneaky. The package.json calls a dependency named "dhwinsw" which is another malicious package published by the threat actor. The dhwinsw package downloads and installs the legitimate tool "winsw" from GitHub. Winsw is an open source project that wraps applications and manages them as a windows service. After winsw is installed via the dependency, a number of node actions are executed:

Using pm2 for redundancy is smart, but adding the windows service wrapper is additional noise and complexity but it means the threat actors can standardize on using pm2 for peristence regardless of whether the package is installed on a Windows or Linux host.
Gathering data about the compromised host
The malware harvests information about the system it has compromised. Upon execution, the malware immediately collects the victim's public IP address through api.ipify.org, then enumerates all local network interfaces, hostnames, and system architecture details. It gathers CPU core counts, memory usage statistics, process uptime, and working directory paths—essentially creating a complete profile of the compromised system.

It then aggregates all the compromised data and creates a massive array named "embed":

Discord exfiltration
Discord has quickly become the preferred method to exfiltrate data, and this attack uses a Discord webhook with the username "svr-dh-badt". It uses the large JSON payload created in the embed action above to exfil details about the compromised host. This reconnaissance data is formatted into a detailed report and immediately transmitted to the attacker's Discord webhook, providing real-time notification of each successful compromise.

Remote Access Trojan
The main payload, housed in an obfuscated 1.9MB index.js file, contains the core RAT functionality. Buried within the minified code are database connection handlers and Socket.IO implementations that establish bidirectional communication channels with the command and control infrastructure. This allows the attacker to send arbitrary commands to compromised systems and receive responses in near real-time. The use of child_process.exec provides the capability to execute system-level commands, effectively granting the attacker shell access to the victim's machine.
At the heart of the malware's operational capability is its update mechanism, which polls the Firebase database every 60 seconds for new commands or payload updates. When an update is detected, the malware downloads a .tgz archive from a remote source, extracts it, and systematically overwrites its own files with the new version. This self-modifying behavior allows the attacker to deploy new capabilities, evade detection signatures, or pivot attack strategies without requiring any user interaction. The malware also checks the npm registry every five minutes for version updates, creating a secondary update channel that blends seamlessly with legitimate npm traffic.

What makes this malware particularly dangerous is its operational security posture. By using Firebase and Discord—both legitimate, widely-trusted services—the malware's network traffic appears benign to most security monitoring systems. The Firebase database URL (o23863162-02-default-rtdb.asia-southeast1.firebasedatabase.app) serves as the primary command channel, while the Discord webhook provides a convenient, API-based method for data exfiltration that requires no custom infrastructure. The attacker can simply monitor a Discord channel to receive system information from every compromised machine.
The malware's file operations demonstrate sophistication in maintaining operational integrity. It implements extensive logging infrastructure to track its own activities, likely for debugging purposes during development. The automatic binary download and code replacement mechanisms allow for modular payload delivery—the attacker can push new malicious components or remove forensic evidence dynamically. The PM2 process management integration ensures that even if the malware crashes or is manually terminated, it will automatically restart within moments.
From a threat intelligence perspective, this package exhibits characteristics of a mature, well-engineered attack platform rather than a proof-of-concept. The multiple persistence mechanisms, dual communication channels, self-update capabilities, and comprehensive system profiling suggest this is designed for long-term persistent access rather than quick data theft. Any system that has executed this package should be considered fully compromised, with the attacker potentially having complete administrative access to all system resources and data.
Indicators of Compromise (IOCs)
Based on my research this threat campaign has several IOCs you can look for:
NPM Packages - all versions:
dhchukyso
dhemrdhs51214
dhemrdhs60015
dhemrdhs60152
dhemrdhs60214
dhemrdhs79029
dhemrdhs83600
dhemrdhs84006
dhemrdhs92004
dhemrdhs92006
dhemrdhs92007
dhemrdhs920072
dhemrdhs92009
dhemrdhs92010
dhemrdhs92011
dhemrdhs92092
dhemrdhs94006
dhemrdhs94010
dhemrdhs95005
dhemrdhs95006
dhhdbank83600
dhhdbank87015
dhhdbank92013
dhhdbank92015
dhhdbank92086
dhkioshdbank
dhnas
dhpgemrdhs51214
dhpgemrdhs60015
dhpgemrdhs60152
dhpgemrdhs60214
dhpgemrdhs79029
dhpgemrdhs83600
dhpgemrdhs84006
dhpgemrdhs92004
dhpgemrdhs92006
dhpgemrdhs92007
dhpgemrdhs920072
dhpgemrdhs92009
dhpgemrdhs92010
dhpgemrdhs92011
dhpgemrdhs92092
dhpgemrdhs94006
dhpgemrdhs94010
dhpgemrdhs95005
dhpgemrdhs95006
dhpm2
dhqr92004
dhqr92010
dhrunner
dhrunner00
dhsvrkioshdbank
dhwinsw
env-app
ldhchukyso
ldhemrdhs51214
ldhemrdhs60015
ldhemrdhs60152
ldhemrdhs60214
ldhemrdhs79029
ldhemrdhs83600
ldhemrdhs84006
ldhemrdhs92004
ldhemrdhs92006
ldhemrdhs92007
ldhemrdhs92009
ldhemrdhs92010
ldhemrdhs92011
ldhemrdhs92092
ldhemrdhs94006
ldhemrdhs94010
ldhemrdhs95005
ldhemrdhs95006
ldhpgemrdhs51214
ldhpgemrdhs60015
ldhpgemrdhs60152
ldhpgemrdhs60214
ldhpgemrdhs79029
ldhpgemrdhs83600
ldhpgemrdhs84006
ldhpgemrdhs92004
ldhpgemrdhs92006
ldhpgemrdhs92007
ldhpgemrdhs92009
ldhpgemrdhs92010
ldhpgemrdhs92011
ldhpgemrdhs92092
ldhpgemrdhs94006
ldhpgemrdhs94010
ldhpgemrdhs95005
ldhpgemrdhs95006
npm-cli-00
o-pgdump
ongtrieuhau861.001
URL/Domains:
hxxps://o23863162-02-default-rtdb[.]asia-southeast1[.]firebasedatabase[.]app/dhhdbank83600-updater
hxxps://discord[.]com/api/webhooks/1396760653687029901/QH0qkmS3cNXmztClLUhNqWQLvzAS_oCDDi5VlsnDtW9kcTCIN2Tbb_P98FoYXCGH8nEh?thread_id=1396760381980147792
How can Safety help protect you from these attacks?
Traditional vulnerability scanning happens too late - after potentially malicious code is already in your system. Which means that ASPM and EDR solutions don't protect you from this type of threat.
But all is not lost, as the Safety Firewall protects develoeprs and CI pipelines proactively. Every package installation request is analyzed before reaching public repositories. Malicious, vulnerable, and policy-violating packages are automatically blocked before they can enter your systems, preventing rather than just detecting threats.Every package installation request is analyzed before reaching public repositories. Malicious, vulnerable, and policy-violating packages are automatically blocked before they can enter your systems, preventing rather than just detecting threats.Every package installation request is analyzed before reaching public repositories. Malicious, vulnerable, and policy-violating packages are automatically blocked before they can enter your systems, preventing rather than just detecting threats.
You can sign up for a free Safety account and try the Safety Firewall HERE. Feel free to reach out to me with any questions!
Let us know if this blog post helped you
I hope this blog post has helped you. Feel free to hit me up directly if you have any questions about this campaign.

Paul McCarty - Head of Research, Safety
You can find me on LinkedIn and BlueSky.




