Recently, Avast’s researchers Pavel Novák and Jan Rubín posted a detailed writeup about the “Parrot TDS” campaign involving more than 16,500 infected websites. Such massive infections don’t go unnoticed by Sucuri and we immediately recognized that the infection in their writeup belonged to the campaign we internally refer to as “ndsw/ndsx” malware. We’ve been tracking this particular campaign since February 2019 — however, some variations have even older reference dates.
The malware consists of several layers: the first of which prominently features the ndsw variable within JavaScript injections, the second of which leverages the ndsx variable in the payload. Our research findings show that attackers regularly change the obfuscation of their JavaScript injections while keeping this recognizable ndsw/ndsx pattern.
What’s more, this malware was one of the top infections Sucuri detected and cleaned in 2021. Last year, more than 61,000 websites scanned by SiteCheck contained the malicious “ndsw” JavaScript.
Since attackers usually inject this malware into every JavaScript file that they can find, a significant number of files are often impacted during infection. Our team removed this malware from almost 20 million .js files found on compromised sites during 2021 alone. The PHP part of this malware (what Avast calls a “proxied version”) was removed over 5,400 times by our remediation tools at an average rate of 1 or 2 files per infected website.
At the time of writing, this “ndsw” campaign is still active. During the first 5 months of 2022, SiteCheck has detected more than 11,000 infected websites — and we’ve already cleaned over 2,900 PHP and 1.64 million JavaScript files related to this malware campaign.
Analysis of Malicious NDSW JavaScript & Variations
Let’s take a look at the ndsw JavaScript and its modifications.
All variations contain the following statement “if(ndsw===undefined)” — hence the ndsw name.
The malicious script is normally found injected either inside HTML pages at the end of inline scripts or at the bottom of all .js files within the compromised environment, which can sometimes amount to thousands of infected files on a single site. In rare cases, this malware can also be found in the database (usually cached by some plugin).
A typical script looks like this:
You can clearly see the URL of the malicious PHP file, located on the same site. In this screenshot, it is //<redacted>/wp-admin/css/colors/blue/blue.php?id=’+token(), where <redacted> is the domain of the infected website.
Sometimes, the URL of the PHP file is obfuscated in an attempt to hinder its discovery.
Over time, we found some variants using multiple different types of obfuscation to hide critical strings, as seen below.
While other variations use the ndsj variable instead of ndsw.
The injected JavaScript may also be found well indented so that it looks less suspicious to a casual observer.
And, in the most unusual variations, we found one version loading a payload from the fake image file “pixelapn.adsprofitnetwork[.]com/apnpixel.png” using a combination of canvas’ getImageData and String.fromCharCode functions instead of the usual PHP proxy.
The function of these visible JavaScipt injections is to fetch and execute the second layer of the attack, which is typically loaded from a PHP file found within a random directory on the same compromised environment.
Analysis of NDSW PHP Malware
Now, let’s take a look at the PHP script that the JavaScript loads its payload from.
I’ve compiled a list of some of the most common location patterns and real world examples.
/wp-admin/css/colors/blue/blue.php (most commonly used) /random/<directory>/<directory>.php - /assets/images/about/about.php - /ajax/jquery.validate/1.11.1/1.11.1.php - /bat/phpmailer/phpmailer.php - /administrator/components/com_admin/helpers/html/html.php - /wp-content/plugins/affiliates-manager-wp-estore-integration/affiliates-manager-wp-estore-integration.php /random/directory/slicemap.php - /slicemap.php - /staging/slicemap.php - /system/Database/slicemap.php /random/directory/class.php - /wp-content/plugins/wp-sp/class.php - /wp-content/plugins/wp-sps/class.php /random/directory/.class.php - /wp-content/uploads/typehub/custom/dn46/.class.php wp-content/plugins/<fake-plugin>/clock.php - wp-content/plugins/wp-dumpme/clock.php - wp-content/plugins/wp-pimple/ciock.php wp-special.php
This list is not exhaustive, however. Attackers are using a wide range of different naming conventions and locations to conceal their PHP and we expect this to continue as new variants are developed.
Obfuscation Techniques
The PHP “proxy” usually looks like this.
The obfuscation within the PHP malware stays the same: two decoding helper classes with random names, followed by the code with encrypted values, visible curl requests, and a used combination of “base64_encode(json_encode” functions.
Just like we saw for some variants of the malicious JavaScript, we’ve also found modifications to help the file masquerade as legitimate PHP. The attackers use fake header comments and proper indenting in an attempt to disguise the malicious contents, as seen below.
While the functionality of the script changes slightly from version to version, the main idea remains the same.
This script, seen above, encodes information about a site visitor (IP address, browser and referer) and passes it to a script on a third-party server (seen above as “ad.syncadv[.]com”). If the response contains the “ndsx” keyword, it is injected and executed on the fly as an inline script on the infected web page.
Analyzing the NDSX Payload
Here is an example of a typical NDSX payload fetched from a third-party server (usually via the PHP proxy).
In this example, the script loads yet another script from: hxxps://stuff.bonneltravel[.]com/report?r=dj03ZDdlM2JjMjNlY2E3Mzc0OTQxYSZjaWQ9MjUw
In very rare cases, we’ve also seen such scripts without the “”var ndsx= true;” part injected by hackers directly into web pages and .js files.
Layers of NDSW/NDSX Scripts
There are a number of distinguishable layers to this attack.
1. NDSW JavaScript Injection:
The first layer of the attack is the injected ndsw/ndsj script which can be found either in the HTML code of infected web pages or in .js files explicitly loaded by the compromised website.
2. PHP Proxy Script:
The second layer (common but optional) of the attack is the script loaded from the PHP proxy on the same compromised environment. The main purpose of this proxy is to hide the location of the server with the NDSX payload. By loading the payload on the server side, its source remains invisible to traffic monitoring and static analysis performed on the client level. The payload is then injected as an inline script.
3. NDSX Script from TDS Server:
The third (or sometimes second) layer of the attack is the NDSX script from the server controlled by the attackers, which is used as a TDS (traffic direction system). Basing its behavior off the IP, browser, and referrer data, the TDS decides what payload to use for a specific user. Moreover, it constantly introduces new URLs for its malicious scripts to avoid sending traffic to already blacklisted resources.
4. Malicious Payload:
Once the TDS has verified the eligibility of a specific site visitor, the NDSX script loads the final payload from a third-party website.
As Avast’s researchers Pavel Novák and Jan Rubín explained in their latest post, the most prevalent final payload for Windows computers is the so-called “Fake Update” malware.
Malicious Domains
We’ve been tracking a number of malicious domains associated with this ongoing NDSW malware campaign.
adsprofitnetwork[.]com - Creation Date: 2019-10-22, IP:188.120.239.154 - pixelapn.adsprofitnetwork[.]com/apnpixel.png statclick[.]net - Creation Date: 2021-01-30, IP:145.239.23.7 - visit.statclick[.]net/ui_node.js - go.statclick[.]net/ui_node.js clickstat360[.]com - Creation Date: 2018-10-22, IP:145.239.23.7 - public.clickstat360[.]com/ui_node.js syncadv[.]com - Creation Date: 2020-11-18, IP:145.239.23.7 - ad.syncadv[.]com/ui_node.js webcachespace[.]net - Creation Date: 2021-09-22, IP:217.23.6.50 - webcachespace[.]net/ac-analytics.js - webcachespace[.]net/jquery.min.js cachespace[.]net - Creation Date: 2022-03-13, IP:217.23.6.50 - ping.cachespace[.]net/jquery.min.js staticvisit[.]net - Creation Date: 2021-08-17 IP:54.38.59.250 - go.staticvisit[.]net/ui_node.js webcachestorage[.]com - Creation Date: 2021-09-22 IP:217.23.6.22 - webcachestorage[.]com/sync.adv.min.js
Recent Layer 4 Domains
In a technique known as domain shadowing, we’ve also cataloged the following layer 4 scripts leveraging the subdomains of legitimate websites.
45.10.42.26 (VDSINA-NL, RU) - market.bluestonechiropractic[.]com - craft.cheesedome[.]com 109.234.35.249 (VDSINA-NL, RU) - design.lawrencetravelco[.]com - stuff.bonneltravel[.]com - rotation.ahrealestatepr[.]com - doors.vipveinsaz[.]com - patients.brannonsmiles[.]com 179.43.169.30 (PLI-AS, PA ) - contractor.thecaninescholar[.]com - staff.beeboykind[.]com - sdk.expresswayautopr[.]com - flowers.netplusplans[.]com - rotation.craigconnors[.]com - notify.aproposaussies[.]com 188.120.239.154 (RU-JSCIOT, RU) - pixelapn.adsprofitnetwork[.]com - mines.cajonsoul[.]com - bumpy.daniyalmedicaltech[.]com - trace.mukandratourandtravels[.]com - mamba.cpncredit[.]com
Most likely, the attackers compromised the domain owners’ accounts (DNS) and created additional A records for malicious subdomains pointing to their own servers.
Infection Vectors
Having analyzed multiple infected websites impacted by this campaign, we’ve found a wide range of malware and vulnerabilities on most of them.
Most websites contain various backdoors, black hat SEO scripts, and vulnerable or outdated components (e.g. plugins and themes). On many infected websites, we’ve also found either Japanese spam or malware belonging to the ongoing WordPress campaign responsible for injecting redirects to push notification scam pages — which is not very surprising, since all three campaigns dominated our detection and cleanup charts for 2021. .
The NDSW malware campaign is extremely successful because it uses a versatile exploitation toolkit that constantly adds new disclosed and 0-day vulnerabilities. Once the bad actor has gained unauthorized access to the environment, they add various backdoors and CMS admin users to maintain access to the compromised website long after the original vulnerability is closed.
Since this campaign uses multiple exploitation scenarios, we can’t give a simple universal answer about how attackers are breaking into websites or how the infection can be cleaned and prevented — it’s different for different sites. Even the injection itself depends on a specific vulnerability and what level of access to the server it provides.
Fake Plugins in WordPress Websites
For example, if the vulnerability provides admin access to a WordPress site, we’ve found that hackers usually install a fake plugin. Typical names of such plugins are wp-sp, wp-sps, wp-pimple, and wp-dumpme.
Here is how the installation of the wp-dumpme malicious plugin looks like in access logs:
194.87.216.247 - - [19/May/2022:10:42:32 -0400] "POST /wp-admin/plugin-install.php?wc-ajax=1 HTTP/1.1" 200 17807 "-" "Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:81.0) Gecko/20100101 Firefox/81.0" 194.87.216.247 - - [19/May/2022:10:42:34 -0400] "POST /wp-admin/plugins.php HTTP/1.1" 200 18995 "-" "Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:81.0) Gecko/20100101 Firefox/81.0" 194.87.216.247 - - [19/May/2022:10:42:34 -0400] "POST /wp-admin/plugins.php?wc-ajax=1&action=activate&plugin=wp-dumpme%2Fwp-dumpme.php&plugin_status=all&_wpnonce=f66ee79d9b HTTP/1.1" 302 0 "-" "Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:81.0) Gecko/20100101 Firefox/81.0" 194.87.216.247 - - [19/May/2022:10:42:35 -0400] "GET /wp-admin/plugins.php?activate=true&plugin_status=all&paged=1&s= HTTP/1.1" 200 18806 "https://redacted/wp-admin/plugins.php?wc-ajax=1&action=activate&plugin=wp-dumpme%2Fwp-dumpme.php&plugin_status=all&_wpnonce=f66ee79d9b" "Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:81.0) Gecko/20100101 Firefox/81.0" … 194.87.216.247 - - [19/May/2022:10:42:39 -0400] "GET /?dumpmecheck=1 HTTP/1.1" 200 8109 "-" "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.125 Safari/537.36"
In this example, you can see that this “plugin” creates the NDSW PHP proxy in the wp-dumpme/clock.php file. The content of this file is originally stored as a base64 encoded string in wp-dumpme/tasty.pot. In the case of other fake plugins, the name of the NDSW PHP proxy file is usually class.php.
This fake plugin also installs a backdoor in wp-dumpme/click.php, which allows attackers to execute arbitrary PHP code on the compromised site.
Of course, these plugins are just one of many possible infection scenarios used by this NDSW/NDSX campaign, so don’t expect to find them on every infected site. What you should focus on instead is the systematic approach to cleanup.
Cleanup & Mitigation Steps
If your website has been compromised by this malware, take the following steps to clean up the infection and harden your website.
- Change your CMS admin password and audit your CMS to ensure that there are no unwanted users with admin privileges.
- Inspect all themes, plugins, and other third-party components installed on your website. Delete (simple deactivation is not enough) anything that you don’t recognize or no longer use.
- Identify and clean all infected files and database records. Leveraging a clean fresh website backup and integrity monitoring systems can help a lot since the total number of infected files for this malware can be in the thousands. Manual cleanup may be extremely tedious when handling this many infected files, so if you don’t have a website back up available, consider an automated solution or refer to our hacked website guide.
- Make sure your CMS and all remaining third-party components are up to date. Don’t waste time. This attack adds new vulnerabilities into its toolkit very fast. Luckily by the time the vulnerability is disclosed, (as a result of cooperation with security researchers) the vendors of the vulnerable theme or plugin may have already released a security patch.
- Consider using a website firewall that will protect your site from most known attacks and virtually patch known vulnerabilities until you are able to update your software.
As always, if you need help identifying and cleaning up the malware on your website, we’re always happy to lend a hand.