Malicious PHP Script Back on Stage?
It’s amazing how we can find old scripts coming back to life for some obscure reasons. If today, Powershell or JavaScript (and its derivations) are very common languages used to perform malicious actions, PHP remains also a good candidate. One of my hunting rules is to search for reversed PHP commands using the strrev() function. A few days ago I found a PHP script on pastebin.com that contained the following functions:
$rnfir_zap = strrev('edoced_46esab');$e_nn = strrev('etalfnizg');eval($e_nn($rnfir_zap(implode('',$ro_ie))));
This is a very common technique to obfuscate basic strings-based detection mechanisms. The variable ‘ro_ie’ was an array of concatenated strings:
$ro_ie = array('1TsJc9pI1n+FcaUmZpNJ1DrRe','pjPdgw+YnCwAWNmUhSHjMHiWA','5zZPLf9x3dLQlwEs9U7e6XSYT','U6n731a81qd79/k/382F71hsN',
'G8GyN51N918XjhrHr9PpL+pFC','gf2X/XSX141s0eTSXO1/7r8Ov','Xu9S1efBevzdvl9O4mY1z2j7o','fT4tPrdrxQ+cU34S9Fv6cVrzz',
'nCjf4H2l0sWfqw/2gn5XeD2uM','RwHfx5bVmd9nnMq5yd3+DwvfH','jMvn77um3mB/WqP2uZ1wQbhlo','WrQ5bod/Du7taMawTRp4BlxVM',
'a57RCqON15PR0+UiiwDh7/kq0','4efaAD/wqRLvCyyeM3Se7rD25','JmvoiXPl7qtWPjsrJ8wPvWAK8','dcXfbCa96QFpPCBxpE6OLbv3W’,
...
Easy to decode, rebuild the strings from the array, decode and decompress the data. Guess what you will find? Another obfuscated script. So, I started to work on it and found interesting behaviours.
Most of the strings used in the second script are Base64-encoded and placed in an array. They are used via a function which takes an index as parameter. Example:
function decode($i)( $array = Array('T' .'W' .'96' .'aWxsYS80LjAgKGNvbXBhdG' .'lib' .'GU7IE1TS' .'UUg' .'OC4w' .'Oy' .'BX' .'aW5' .'kb3dzIE5UIDY' .'uMCk=','c2FmZV9tb2R' .'l',’b3’ ...); return(base64_decode($array[$i])) } ... $_56 = decode(1);
The malicious script creates files in '/dev/shm/<directory>/'. That's clever: /dev/shm is writable by any user on a Linux system and is more stealthy than dumping files to /tmp or /var/tmp!
Many integers are replaced by the round() function with mathematical expressions. Example:
return preg_replace(MA_B(243),MA_B(244) .$_69,$_74,round(0+0.33333333333333+0.33333333333333+0.33333333333333));
The round() function above returns ‘1’.
The presence of many 'wp_xxxx' strings reveals that the script is targeting WordPress websites. There is indeed a hooking[1] function in place:
@add_action(‘wp_footer’,’check_wp_load',mt_rand(1,10));
This hook injects malicious code in the Wordpress footer.
Other interesting techniques are present. The script checks that the Wordpress website has a good reputation by querying the Google safebrowing API:
$_34=$_SERVER[HTTP_HOST];
$_0="http://google.com/safebrowsing/diagnostic?site=$_34”;
$_67=@cc($_0); // Perform the HTTP query
if ($_67 != ‘' && strpos($_67,'is listed as suspicious')!== false) {
...
There is an anti-bot / anti-analysis control in place. Here are the extracted strings from the array of Base64 strings mentioned above:
66\.249\.[6-9][0-9]\.[0-9]+ 72\.14\.[1-2][0-9][0-9]\.[0-9]+ 74\.125\.[0-9]+\.[0-9]+ 65\.5[2-5]\.[0-9]+\.[0-9]+ 74\.6\.[0-9]+\.[0-9]+ 67\.195\.[0-9]+\.[0-9]+ 72\.30\.[0-9]+\.[0-9]+ 38\.[0-9]+\.[0-9]+\.[0-9]+ 124\.115\.6\.[0-9]+ 93\.172\.94\.227 212\.100\.250\.218 71\.165\.223\.134 209\.9\.239\.101 67\.217\.160\.[0-9]+ 70\.91\.180\.25 65\.93\.62\.242 74\.193\.246\.129 213\.144\.15\.38 195\.92\.229\.2 70\.50\.189\.191 218\.28\.88\.99 165\.160\.2\.20 89\.122\.224\.230 66\.230\.175\.124 218\.18\.174\.27 65\.33\.87\.94 67\.210\.111\.241 81\.135\.175\.70 64\.69\.34\.134 89\.149\.253\.169 64\.233\.1[6-8][1-9]\.[0-9]+ 64\.233\.19[0-1]\.[0-9]+ 209\.185\.108\.[0-9]+ 209\.185\.253\.[0-9]+ 209\.85\.238\.[0-9]+ 216\.239\.33\.9[6-9] 216\.239\.37\.9[8-9] 216\.239\.39\.9[8-9] 216\.239\.41\.9[6-9] 216\.239\.45\.4 216\.239\.46\.[0-9]+ 216\.239\.51\.9[6-9] 216\.239\.53\.9[8-9] 216\.239\.57\.9[6-9] 216\.239\.59\.9[8-9] 216\.33\.229\.163 64\.233\.173\.[0-9]+ 64\.68\.8[0-9]\.[0-9]+ 64\.68\.9[0-2]\.[0-9]+ 72\.14\.199\.[0-9]+ 8\.6\.48\.[0-9]+ 207\.211\.40\.82 67\.162\.158\.146 66\.255\.53\.123 24\.200\.208\.112 129\.187\.148\.240 129\.187\.148\.244 199\.126\.151\.229 118\.124\.32\.193 89\.149\.217\.191 122\.164\.27\.42 149\.5\.168\.2 150\.70\.66\.[0-9]+ 194\.250\.116\.39 208\.80\.194\.[0-9]+ 62\.190\.39\.205 67\.198\.80\.236 85\.85\.187\.243 95\.134\.141\.250 97\.107\.135\.[0-9]+ 97\.79\.239\.[0-9]+ 173\.255\.233\.[0-9]+ 184\.168\.191\.[0-9]+ 95\.108\.157\.[0-9]+ 209\.235\.253\.17 80\.203\.168\.254 91\.121\.139\.153 65\.106\.217\.107 212\.227\.136\.64 216\.27\.40\.61 125\.212\.44\.207 118\.169\.43\.123 118\.169\.40\.20 http google slurp msnbot bot crawl spider robot httpclient curl php indy library wordpress charlotte wwwster python urllib perl libwww lynx twiceler rambler yandex trend virus malware wget
The next step was to find the code used to download another stage. Again, based on the same technique with Base64-encoded strings. The rebuilt URL is:
hxxp://net44net[.]net/net/2.php?u=aHR0cDovLzUyLjIwOS4yNDguMjglMkZ0cm9qYW4ucGhw
As in many cases, the domain does not resolve (and seems for sale according to domaintools.com)… Queries to some passive DNS databases revealed an IP address but it does not reply. Your last hope is often to Google for some strings. I found a blog post from 2012(!) which described exactly the same behaviour [2] but, with some differences in the code:
- Files were stored in /tmp instead of /dev/shm/.
- The hooked WP function is wp_head() instead of wp_footer().
- No call to Google safebrowsing.
But the domain used to download the second stage is the same. Why does the script come back to life?
Some possible scenarios:
-
The more recent script (posted on the 10th of July) has been revamped?
-
The script has been found on an old compromised server?
-
Did somebody find the script somewhere else? The pastie title is “PHP Trojan?” but it not referenced anywhere else according to Google.
Note that both hashes of both scripts are unknown on VT. If you have more information about this script or the technique used, feel free to share!
My next class:
Reverse-Engineering Malware: Malware Analysis Tools and Techniques | Amsterdam | Jan 20th - Jan 25th 2025 |
×
Diary Archives
Comments