The WP-VCD malware for WordPress has existed for many years. It mainly spreads by injecting itself into legitimate plugins and themes after which it will spread itself on sites that offer downloads to (nulled) WordPress plugins and themes.
We noticed that during the corona-virus pandemic, the WP-VCD malware has also started injecting itself into plugins that can show statistics related to the coronavirus.
These plugins might be particularly popular at the moment which gives the maker of the WP-VCD malware an advantage.
We analyzed some samples that were downloaded from www[dot]downloadfreethemes[dot]co
. The samples taken from different themes and plugins offered on this site were all infected with this malware.
Similar sites that do the same kind of practices are:
- themesubmit[dot]com
- www[dot]downloadfreethemes[dot]space
- freesoft[dot]royalbeats[dot]in
- freedownloadthemes[dot]co
- raybans[dot]com[dot]co
- coursefree[dot]co
Analysis of the WP-VCD malware
Injection point
During the analysis of multiple samples, we noticed that all themes contained a file called class.theme-modules.php
and all plugins contained a file called class.plugin-modules.php
. Both files contained the exact same code.
In plugins, the class.plugin-modules.php
file would be loaded in the main file of the plugin on the first line by injecting the following (reformatted):
<?php
if (file_exists(dirname(__FILE__) . '/class.plugin-modules.php'))
include_once(dirname(__FILE__) . '/class.plugin-modules.php');
?>
In themes, the class.theme-modules.php
file would be loaded in the functions.php
file by injecting the following (reformatted):
<?php
if (file_exists(dirname(__FILE__) . '/class.theme-modules.php'))
include_once(dirname(__FILE__) . '/class.theme-modules.php');
?>
The injected file
Note that the domain name which the malware uses to communicate can change often, in this scenario it was set to www[dot]arilns[dot]com
. At the time of publication, it did not have a web server attached to it.
Initial execution flow
Refer to the overview of functions, constants, and variables at the bottom of the page to see what some of the variables mentioned in the execution flow do and contain.
1. It has to meet a few conditions
The initial malware is only executed when it meets a few conditions: the user is an administrator and any of the following conditions:
The user is on the themes.php
pages or the action
parameter in the URL is set to activate
or the plugin
parameter is present in the URL.
2. If the statement in 1. is true
If the statement in 1. is true, it will iterate through all folders in /wp-content/themes/
and inject $install_code
into functions.php
if the file exists.
If the functions.php
file does not exist in the root folder of the theme, it will go one level deeper and iterate through all sub-folders and attempt to find functions.php
yet again.
3. If it managed to inject the backdoor code into any file
If it managed to inject the backdoor code into any file, it will send a ping request to www[dot]arilns[dot]com/o.php
with 2 query parameters:
-> The host
parameter which contains $_SERVER['HTTP_HOST']
.
-> The password
parameter which contains $install_hash
.
4. Once the ping has been sent
Once the ping has been sent, whether it was successful or not, it will retrieve the path to the root directory of the WordPress site.
Then it will execute the WP_URL_CD
function which creates the /wp-includes/wp-vcd.php
backdoor containing $GLOBALS['WP_CD_CODE']
and also injects it into /wp-includes/post.php
.
5. Finally, it will get the code of itself and execute 2 calls
Finally, it will get the code of itself (and store that in $file
) and execute 2 calls to preg_replace
after which the only code left in the file is <?php error_reporting(0);?>
making it seem as if nothing happened.
-> preg_replace('!//install_code.*//install_code_end!s', '', $file)
will remove most initial injection code from itself. (The class.theme-modules.php
or class.plugin-modules.php
file.)
-> preg_replace('!<\?php\s*\?>!s', '', $file)
; will remove all <?php ?>
tags of which there is only white-space in-between.
Analyzing $install_code
$install_code
is injected into all functions.php
files that are present in the /wp-includes/themes/
directories. The flow of this code is described below.
1. If the action and password parameters are present
If the action
and password
parameters are present in the request ($_REQUEST)
and the password equals md5($_SERVER['HTTP_HOST'] . AUTH_SALT)
, it can execute 2 actions:
-> change_domain
Find the following in the code of the current file: $tmpcontent = @file_get_contents("http://(.*)/code.php
and replace the domain name with the new domain name that is present in the newdomain
request parameter.
-> change_code
Find the following in the code of the current file: //$start_wp_theme_tmp([\s\S]*)//$end_wp_theme_tmp
and inject PHP code supplied by the newcode
request parameter in-between the lines //$start_wp_theme_tmp
and //$end_wp_theme_tmp
.
2. If no actions were executed in step 1
If no actions were executed in step 1, it will check if the current script is not wp-cron.php
and not xmlrpc.php
.
If the current script does not equal these 2 values, it will send a request to any of the following domains, depending on if they respond properly to the request or not:
- www[dot]arilns[dot]com/code.php
- www[dot]arilns[dot]pw/code.php
- www[dot]arilns[dot]top/code.php.
If it cannot send a request to any of these URL’s, it will retrieve content from /wp-includes/wp-tmp.php
.
3. If the returning data contains the string
If the returning data from the request to code.php
on these domains contains the string held in the $wp_auth_key
variable, it will inject the PHP code into the /wp-includes/wp-tmp.php
file.
If it cannot be injected into this file, it will try to inject it into the root directory of the current theme. If that also fails, it will try to inject it into wp-tmp.php
in the current directory.
Analyzing wp-tmp.php
This file can contain pretty much anything defined by the malware creator. We have seen things from backdoors to advertisements that are injected into all pages of the site.
Since they can inject any kind of PHP code, there is pretty much no restriction as to what they are able to do on the site.
One sample that we have shows that it:
- Injected advertisements loaded from deloplen[dot]com and pushosubk[dot]com.
- It only performs any malicious activity if the referrer of the visitor is from a search engine and not a user who is logged in on the WordPress site.
- Loads advertisement code from /wp-includes/wp-feed.php
Definitions
Constants
-> MAX_LEVEL
: Defines how deep it should iterate through folders.
-> MAX_ITERATION
: Defines how many files it can read from a folder.
-> P
: The value of $_SERVER['DOCUMENT_ROOT']
.
Globals & variables
-> $GLOBALS['WP_CD_CODE']
: Contains a base64 encoded string that when decoded, is almost similar to the code of the malware file itself except that it doesn’t contain the function that spreads itself in the functions.php
files.
-> $GLOBALS['stopkey']
: Array of folders’ names. When the malware is scanning for folders/files to inject into, it will stop once it hits one of the folder names in this array.
-> $GLOBALS['DIR_ARRAY']
: All folders that are found to be in the $GLOBALS['stopkey']
array will be added to this array.
-> $search
: Array used in the SearchFile
function to find the location of wp-config.php.
-> $install_code
: Backdoor code that will be injected into multiple files.
-> $install_hash
: String containing an MD5 hash of $_SERVER['HTTP_HOST']
and AUTH_SALT
. AUTH_SALT
is taken from the wp-config.php file.
-> $install_code
: Replaces the string '{$password}'
with $install_hash
in $install_code
.
-> $wp_auth_key
: A MD5 string, most likely used so certain requests can only be executed by the malware creator.
Functions
-> getDirList($path)
: Get all folders in $path
.
-> WP_URL_CD($path)
:
-> If the file /wp-includes/post.php exists, it will create a file called /wp-includes/wp-vcd.php containing the base64 decoded value of $GLOBALS['WP_CD_CODE']
.
-> If /wp-includes/post.php does not contain the string ‘wp-vcd’, it will inject the inclusion of /wp-includes/wp-vcd.php into the first line of /wp-includes/post.php: <?php if (file_exists(dirname(__FILE__) . 'wp-vcd.php')) include_once(dirname(__FILE__) . 'wp-vcd.php'); ?>
-> SearchFile($search, $path)
: Given the $path
, keep searching the entire site until the file in $search
is found.
-> file_get_contents_tcurl($url)
: Send a request to a URL and return its contents.
-> theme_temp_setup($phpCode)
:
-> Creates a new temporary file in PHP’s temporary directory in which it will inject $phpCode
. Once the code has been injected, it will load the file and immediately delete it after which it will return all defined variables returned by the PHP function get_defined_vars()
.
-> If it cannot create a temporary file in PHP’s temporary directory, it will attempt to create the file in the current directory instead.
Keywords
- class.plugin-modules.php
- class.theme-modules.php
- wp-vcd.php
- wp-tmp.php
- WP_V_CD
- WP_URL_CD
- WP_CD_CODE
- arilns
- downloadfreethemes.co
- install_code
- install_hash
- theme_temp_setup
How to protect a WordPress site from the WP-VCD Malware?
To protect your site from the WP-VCD malware you should not download plugins and themes from sites that offer nulled versions of the software.
Not only is it often against the terms and conditions of the plugin, but they are also loaded with malware and other suspicious backdoors, that can cause a lot of damage to your site and your reputation.
Only download software from the official wordpress.org site, through the WordPress backend and legitimate sites that offer premium plugins.