Technical details and scripts of the WordPress Timthumb.php hack

Big News [April 24th, 2012]: I’ve launched Wordfence to permanently fix your WordPress site’s security issues. Read this now.

UPDATE: WordThumb has now been merged into TimThumb and has become TimThumb 2.0. Please head over to the TimThumb site now for updates and to get the code.

As I mentioned yesterday my WordPress blog was hacked. The security hole has been picked up by hacker news and from there, The Register, ZDNet, PCWorld, and Geek.com among others. The publicity will hopefully get Theme developers to update timthumb.php or switch to a different thumbnail generator.

I’ve been contacted with requests for detailed info, so I’m going to post the technical details of how my site was hacked along with the scripts that the hacker used to get in. This is targeted at a technical audience.

The server that served you this web page is the one that was hacked. It runs Ubuntu 10.10 with all security updates installed. It is a virtual server hosted by Linode.

I also run the latest version of WordPress.org.

My WordPress root directory was writable, but making it read only would not have prevented the hack.

Timthumb.php in it’s default configuration allows site visitors to load images from a predefined set of remote websites for resizing and serving. Timthumb offers a caching mechanism so that it doesn’t have to continually re-process images. The cache directory lives under the wordpress root and is accessible by visitors to the website.

The ability for a site visitor to load content from a remote website and to make the web server write that remote content to a web accessible directory is the cause of the vulnerability in timthumb.php.

To be clear, timthumb.php does not actually execute any remote malicious code that causes this vulnerability. This was a point of confusion among some commenters in my blog post yesterday. It simply gets a remote file and places it in a web accessible directory.

Timthumb only allows remote content from a small range of websites to be loaded remotely. In it’s default configuration these included Blogger, WordPress.com and other sites that are writeable by the general public.

Timthumb’s verification that remote content was only being loaded by these domains was also broken. You could for example load content from hackersiteblogspot.com or from blogspot.com.hackersite.com.

I’ve submitted a patch that fixes the pattern matching and removed all default public hosting sites from the allowed sites list. The developer has opted to keep a small list in which I’m not in favor of.

In my case the hacker uploaded a script to my cache directory which timthumb.php stores as “external_<md5 hash>.php”. He/she then accessed this script directly in my timthumb cache directory as something like http://markmaunder.com/wp-content/themes/Memoir/scripts/cache/external_md5hash.php

The script uploaded was Alucar shell which is base64 encoded and decodes when it executes. That makes it a little harder to find it using grep or similar tool. You can see the encoded version of Alucar here and the decoded version of Alucar here (without the username and password preamble at the top).

Here’s a screenshot of the UI:

Alucar UI

This script which gives a web based shell access was then used to inject base64 code to one of my core wordpress files wp-blog-header.php which lives in the wordpress root directory. The file with injected code looked like this.

The decoded version of this base64 code is this. The code executes whenever a blog page is visited. It fetches a file from a URL and writes it to /tmp. Then it executes the php code that is contained in this file. In my case it simply echo’d some javascript code that would show ads. Here is the code contained in the file in /tmp.

Again, this file is periodically updated with new PHP code, so the attacker could have his way with my server until I found out about it. The code could be altered to instead become a spam system and work it’s way through a long list of spam emails.

The way I tracked this to conclusion was:

  • Heard audio on my blog telling me I’d won something.
  • Checked Chrome network tools and saw ad content loading and I don’t serve ads.
  • Grepped wordpress source and themes for hostname I saw in ad. Nothing.
  • Dumped mysql databases on server (all of them) and grepped for hostname. Nothing.
  • Confusion reigns.
  • Started working my way through nginx (which is my front end proxy to apache) and apache access and error logs.
  • Spotted lines in apache error log like this: “[Mon Aug 01 11:09:12 2011] [error] [client 127.0.0.1] PHP Warning: file_get_contents(http://blogger.com.zoha.vn/db/load.php): failed to open stream: HTTP request failed! in /usr/local/markmaunder/wp-content/themes/Memoir/timthumb.php on line 675″
  • Checked timthumb’s cache directory and found Alucar.
  • Realized base64 encoding is why I didn’t find anything with grep.
  • Regrepped wordpress source and database and found injection in wp-blog-header.php
  • Decoded base64 stuff and played with Alucar
  • Found tmp file in /tmp
  • Cleaned everything and fixed permissions. Ran chkrootkit and other utils on machine to see if anything else was compromised. Changed passwords, etc.

 

43 thoughts on “Technical details and scripts of the WordPress Timthumb.php hack

  1. My WordPress sites all got hacked in the last week in the same way. I haven’t installed any TimThumb plugin though, so I assume it’s a similar vulnerability that allowed them in.

    All index.php, default.html files and a few others had a long section of decode-base64 code in them.

    The first signs of a problem, a few days before I realized I was infected, were that calling the site without the “www.” gave a blank page.

    Also, the text in the header was an increased size, which messed up the layout. The text size (for the Blog & Description etc) is based on the browser text size (ie: in “em’s”), and it did not matter which pc or which browser I used, the problem was consistent.

    Only when I right clicked and saw a long string at the top of the web pages did I truly know I was hacked.

    I spend last Friday investigating and researching, most of Saturday patching the files that appeared to be infected in all sites, then Sunday heartbreak – all infected again.

    I am in the process of doing clean installs on a new host right now, hopefully losing the virus in the process.

  2. Mark,
    Thanks for the useful info. I was a victim of this attack just a few days ago, and it looks like my outdated version of timthumb was the culprit.

    The shell that the attacker uploaded to my cache was called BOFF. Here is a screenshot:

    http://dmitrybrant.com/images/boff_shell_screen.png

    It’s unbelievable how fully-featured these things are! It’s a miracle that the damage wasn’t more extensive. This was a real wake-up call!

  3. Hi,

    Great write up! However, the exploit loos different in the logs depending on the script that drops it. So, the best thing is to grep out all base64 stuff ( some use rot13 as well) and then decode. Its rather simple though. You just need to look for 1 byte gifs being POSTed to your uplods dir. From that point you can track back. I like to remove handlers using .htaccess myself. You see, the exploit depends on being able to drop a false image to uplods in most of the scripted attacks. So, if you remove oho based handlers .htaccess from your upload dir your half way there.

    I see other attacks that use the footer.php in many themes ( see twentyten) but the stuff out of china uses timthumb.Hope it helps.

  4. Interesting that a few people have posted in the past few days. I had 2 sites hacked and reported to me by Google. However, In reading your information, it looks like it was hacked a while back. I thought I had cleaned it up, but it was the early stages of this hack’s appearance and not a lot of information was available.

    I’m wondering if some hackers have recently unleashed some script intrusions that run malware and Google is notifying people of it?

    Also, is it possible that the changed code will allow a hacker access to the hosting directory? I am considering wiping the entire directory and reinstalling to make sure it’s clean. I guess my question is: have there been any reports of further intrusions after we go through the clean up process you described?

    Thanks!

    • I recently had 3 of my sites hacked. 2 of them WordPress and one Joomla. Same hack (Base64). I was able to clean the Joomla site, but both WordPress sites are jacked (for a lack of better terms). The IP addresses that were logged into my account actually belong to my web host, so I know that it wasn’t something that I uploaded. Since I pointed out that the IP address that they provided was theirs, they have not responded to any of my emails/tickets. Frustrated to say the least. (DREAMHOST)

        • @Christopher: I wouldn’t necessarily blame your host with the hack on yor WP site though. I had the same happen to my presumably safe WP sites on a reputable hosting giant and they were hacked by a Base64 hack. I was able to track it through some deep .php code searching and cleaned it up with a fresh reinstall of all wp-includes, wp-content and wp-admin folders and all .php files. I also looked through any of the thumnails that were uploaded or changed on or very close to the date of the suspected hack and removed them and replaced them with the originals I had. It took awhile but thankfully my sites (three of them) were salvageable. Then I added security checking tools. And made sure my backups were working as well. Hope this helps.

  5. Hi Mark, thx for the writeup! Quick question:

    If I hosted my wordpress site (with the pre 2.0 timthumb script) in the public/wordpress/ directory (i.e. not the home directory) on a shared hosting server, do you know whether it’s possible that the hacker affected directories (such as injecting code into existing files or creating a new file) outside of the /wordpress/ directory? I.e. in the /public/ directory?

  6. It has been quite a while since this vulnerability was exploited! I guess, m the last person to carry the vulnerable file though! As a result all my websites (around 7-8 of them) got hacked. Every header.php and wp-config.php file got infected with base64 encoded codes and html files with some &ltSCRIPT>eval… code… Thanks a lot for your help :). I have been able to restore all my sites either from my backup or from clean WP installation and also have updated the timthumb where necessary! :)
    Thanks again!

  7. I haven’t checked in here for some time as I thought it was getting boring, but the last handful of posts are really good quality so I guess I’ll add you back to my daily bloglist. You deserve it my friend. :)

  8. Wouldn’t obtaining the mime type of the file uploaded and only allowing through those which match a valid list of mime types have prevented this problem in the first place?

    Then you’ve got probably 90+% of the problem solved, then you just have to solve problems where people try to trick the mime type into saying something it’s really not.

    but if the file is a php script, surely it’s trivial to detect.

  9. Mark can u please let me know how the hacker was able to bypass the mime type check and the filetype check though i tried my best on my vm with tamper data and header checks but all a failure……i am pretty much sure that a .php cant be there it should have been something like .jpg inside which there was a shell

  10. i have some doubts hope u guys dont mind…On hearing about the timthunb.php vulnerability i got curious and tried it to see it myself on my VM but the point is the file Blogger.com.somedomain.something/badscript.php will never get through as it will give invalid mime source type on src=http://www.Blogger.com.somedomain.something/badscript.php and if the shell is via some image file like asd.jpg (containing a shell inside) it wont get executed as by default the file will be read as jpg not as a PHP unless there is a .htaccess file in the directory saying that to read .jpg as .php….please enlighten me with your knowledge or correct me if i m wrong

  11. Why not just add a .htaccess file that disallows file extensions other than jpg gif and png in the cache directory? this is what I did and have considered myself save because my theme is so heavily modified it would take me a week to move everything back into an updated with with timthumb removed etc..

    so how about it.. .htaccess wise?

    Thanks for the great article

    • Hey Josh. .htaccess could be created to deal with a bunch of scenarios. e.g. the web server might still execute php code that has a .jpeg extention if configured in a certain way so it’d have to deal with that too. My approach has been instead of anticipating hacks and building it into htaccess, to just get rid of the public cache entirely since it’s not needed. (See the WordThumb blog entry)

    • I maybe mistaken, but a script could be uploaded using a double extension, eg. badscript.php.jpg (or maybe even using a null byte). WordPress would of course upload it (if no proper htaccess or other security is in place). I may be wrong, but WordPress appends an underscore ‘_’ to the end of the first extension, making it badscript.php_.jpg after it is uploaded. Does timthumb do this as well?

      You could use tools like Live HTTP Headers to proceed, if the script was written accordingly. If you have owner permissions to write a .htaccess file than it could possibly overwrite any .htaccess rules you have in place, unless it didn’t have write permissions.

      Any upload directories that are user accessible, need to be secured initially. No if, ands, or buts. That’s why 80% of backdoors are through upload directories.

      • Well in timthumb.php,the file which is included (src=blah blah blah) gets $md5(src) so if u uploaded http://www.blogger.com.hackedsite.com/badscript.php(though u will get an error invalid mime type n it wont get uploaded) u will find the file as 283deef4695f841ff772ee140418e75f.php n if used badscript.php.jpg then u will get 221131d56ca6cd2349ba5a3c2db17ed7.jpg which is absolutely of no use unless there is an htaccess file asking the server to treat jpg as php n execute it

  12. why not use this :

    $url_info['host'] = strtolower($url_info['host']);
    if($url_info['host'] === $site || preg_match(“/[.]“.$site.”$/is”,$url_info['host'])){
    $isAllowedSite = true;
    }

    to replace:

    if (stristr($url_info['host'], $site) !== false) {
    $isAllowedSite = true;
    }

  13. I caught a mistake. The person didn’t access Alucar from http://markmaunder.com/wp-content/themes/Memoir/scripts/cache/external_md5hash.php
    He did from: http://markmaunder.com/wp-content/themes/Memoir/scripts/temp/external_md5hash.php

    The folder is stored first in “temp” and then is proccesed by GD and stored in the “cache folder”. The cached file is going to be broken. For remote execution to happen you have to use the uploaded copy in the temp folder.

    • Glad, he/she didn’t do further damage and only used to server ads. Imagine, he/she can done anything on the server ?

      Thanks for details track down. You definitely WIN.

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>

Notify me of followup comments via e-mail. You can also subscribe without commenting.