Website security: Finding PHP backdoor scripts

Removing unauthorised scripts from your site can be tricky. If you want to have a go at finding it read on!

For my own sites using WordPress or Joomla I have found it easier to reinstall the software from a known source. If you have time and the interest here's a method from http://25yearsofprogramming.com/blog/2010/20100315.htm (this link no longer works) to find the hacker's script or scripts.

When hackers get access to your website, they sometimes install a backdoor shell script designed to allow them to regain entry even after you've cleaned up the site, repaired the original security hole that allowed the hack to be successful, otherwise improved site security, and even installed measures to try to lock the hackers out.

A backdoor script can be called from a browser like any other web page. It gives its user a web page interface where they can download and upload, view or modify files, create directories, and otherwise manage the site using PHP's ability to read and write files and pass operating system commands through to the operating system.

Back-doors can be difficult to find because they are usually hidden in files that are already part of the site or uploaded as new files with innocent looking names, often in a directory with many files in it.

1) Detect backdoor scripts by searching website access logs

One way to find these scripts is by searching website access logs for suspicious entries generated when the hacker uses the scripts to modify site files.

Here is a good example, from an Apache HTTP log, of a backdoor script in actual use by a hacker, to edit the /public_html/.htaccess file:

200 4795 "http://website/path/footer.inc.php?act=filemanager" "Mozilla/5.0..."

  • footer.inc.php is the innocently-named file containing the backdoor script.
  • Notice the "act"(action)=edit and file=.htaccess. The backdoor has a built-in file editor.
  • The referrer (previous page visited) was the same backdoor, which also has a built-in file manager.

Nobody should be able to edit your .htaccess file from a web page! A log line like this is a huge warning flag, and points directly to the file where the backdoor script is hidden.

The above example suggests a few obvious things to search for in your log file:

Search Text Comment
htaccess Unless you write articles about .htaccess and the word is in your search-engine-friendly filename URLs, there is no reason for the word htaccess to appear in your HTTP access logs.
From the example above, and a reasonable variation.
filemanager Another word that probably won't normally appear in your log file.

If you use legitimate web-interface editors like CKEditor or TinyMCE, they might conceivably produce log lines like this when you edit files yourself, but the pages being called as seen in your log file should be recognizable as scripts belonging to those editors, not weird names like footer.inc.php.

The website security article linked at the top of this page has a section (12b) that describes how to obtain your website access logs.

2) Detect backdoor scripts by searching site files with grep or findstr

Backdoor scripts often use PHP commands that most legitimate scripts don't, so you can search the files in your site for those commands. There are search utility programs you can use for finding text in files. The two described below are ones that you run from a command line (prompt), not graphical GUI programs.

Suspicious PHP code to search for

Here is a list of some PHP functions used by malicious scripts and less commonly used by legitimate ones, but false positives are certainly a possibility, especially for the last three in the list. Don't delete a script or a file just because you find one or more of these functions in it. Use your judgment while inspecting the surrounding code. On the other hand, if a header at the top of the script says it's a "web shell" written by a hacking team (some of them do say that), you can be reasonably certain you don't want that script in your site.

edoced_46esab (base64_decode used backwards to avoid detection by string searches like this)
`` (backticks with an operating system command between them)

Text search in Linux with grep

On a Linux server, the grep program is already installed as part of the operating system. The only problem is figuring out how to launch it.

If you have command line access to your server (SSH), there's no problem. You can run it from the command line and have the results displayed to you. However, for security reasons, most shared webhosts do not give their customers shell access.

In that case, the solution may be to use crontab to schedule and run your command. Crontab is allowed by most webhosts and is provided in cPanel as Cron Jobs and in Plesk as Scheduled Tasks. Everything that would normally be displayed to you in your terminal window (the output from your command and any error messages) is sent to you by email instead.

Old versions of grep have limited capabilities. Newer ones allow the more useful PCRE Perl regular expressions and other options. Before doing searches, we need to discover what capabilities you have available, and some other information about your server.

Run these test commands either from a shell command line or by the Cron Job method, whichever you discovered you could use. You'll probably find that you can only enter one command in each cron job you create, so this procedure feels clunky until you get used to it:

  1. Run this simple command to determine the default directory that crontab starts in:
    The result in the email should be something like: /home/userID
  2. Run this command to get a listing of the files and folders in that directory:
    ls -aFlq
    Examine the listing to make sure public_html is there. It should look something like this:
    drwxr-x--- 39 xxxxxxxx xxxxxxxx 4096 Jan 12 18:30 public_html/
  3. Determine your server's version of grep:
    grep --version
  4. Determine which options your version of grep supports:
    grep --help
    If the output has a line like this, you have a newer version that supports Perl regular expressions:
    -P, --perl-regexp PATTERN is a Perl regular expression

You're now familiar with the method of entering these commands, and you have information that will help you make judgment decisions in the next steps, such as whether you can use the more complex regular expressions or must use the simple ones.

We're going to restrict the searches to the public_html folder and everything inside it. When you got your directory listing in Step 2 above, you probably saw that there are many files and folders outside public_html. It's not impossible that malicious code might have been placed there, but we're going to ignore them for these automated global searches.

One reason is to avoid grep searching all your stored emails, website access logs, and other potentially massive files that could take a lot of time and generate many false positives.

The other reason is that there are some types of files that can cause grep to stop searching but not quit running, leaving it in basically a hung state. When you're running it from cron, you aren't able to type Control-C to make it stop. For now, we'll try to make this situation as unlikely as possible by restricting the search to the files that are actually part of your website.

We'll also use the grep -Dskip (or --devices=skip) option, which tells grep not to process "device" files, one of the types of files that can cause grep to hang.

On a dedicated server, it is best to avoid searching the following directories with grep: /dev/, /lib/, /media/, /proc/, /sys/. On a shared server, you don't normally have access to those.

One notable exception to ignoring files outside public HTML is that if you see an .htaccess file in the same folder where public_html is (that is, outside public_html), you should examine it manually for malicious code. It's a text file.

Sample text searches for suspicious PHP code

This simple syntax should work with older grep versions. Do the search once for each of the suggested PHP keywords listed above. To understand the options used in the lines below, refer to the Help text you obtained in a preliminary step above. The space-asterisk combination in the regular expression allows for zero or more spaces between the end of the command and the opening parenthesis, two different styles of PHP coding:

grep -RnDskip "passthru *(" public_html/

Your result email will contain a list of the files where matching lines were found, the line numbers where the matches occurred, and the text of the lines that matched.

If your grep Help text showed that the -P flag (Perl regular expressions) is available, you can combine all the searches into one command like this, although it could generate a multi-megabyte result email if it turns out that any of the functions are used extensively by many of your legitimate scripts (false positives):

grep -RPnDskip "(passthru|shell_exec|system|phpinfo|base64_decode|chmod|mkdir|fopen|fclose|readfile) *(" public_html/

Grep is a powerful and flexible search program that is well worth taking some time to become acquainted with. You can also use it for searching your access logs as discussed in section 1 of this article. If you'll be doing that search on your local PC and it uses Windows, see Note 1, below.

Text search in Windows with findstr

A built-in Windows text search utility similar to grep is called findstr. It should exist on any Windows computer, whether it's your local PC or your remote Windows server. You can get help for it (such as on your Windows PC after opening a Command Prompt) with the command: help findstr, or findstr /?.

I am not familiar enough with Windows shared hosting plans to know if they usually provide SSH (I doubt it) or some other way to run system commands like findstr. Plesk Scheduled Tasks is probably the best first place to look for that capability.

Sample search

*.* means All files, DOS-style wildcards.

Use this form of the command to search for one keyword at a time:

findstr /r /s /n /c:"passthru *(" *.*

Use this form of the command to search for all keywords at once, with a risk of false positives or a large amount of output. It appears that when using this form you can't allow for any spaces between the command and the parenthesis. I added a parenthesis to system because it's such a common word:

findstr /r /s /n "passthru shell_exec system( phpinfo base64_decode chmod mkdir fopen fclose readfile" *.*

3) Detect backdoor scripts by searching site files with a PHP script

As an alternative to the above searches, this PHP script searches your website files for the suspicious function calls listed above, searches for other suspicious text snippets, can report files modified between two date/times, and can be modified to perform other actions, as well.


  1. An excellent Windows version of grep can easily be found by searching for "grep for windows", or install the app "Bash on Ubuntu on Window" that you can get with Windows 10.

For server use, it's unlikely your web host would allow you to install grep on a shared Windows server if it isn't already there, so you'd probably have to use findstr.

On a dedicated Windows server, I imagine the grep installation would be as straightforward as on a PC. It's not a single .exe file, though. It has 4 associated .dll files that install with it.

  • Here is what you don't want to see when you visit one of your website pages. This is a screenshot of a backdoor web shell's menu page. The page is all gray except for this menu. The items in Russian are: Full Information, File Manager, phpinfo(), Run a PHP command, Execute Linux Command. Screenshots of the more common and complex shells c99 and r57 can be easily found with a search engine image search. The PHP code in the script of this web shell fails to validate incoming GET and POST data before using it, so it has security vulnerabilities. Oh my!

  1. Names of some common backdoor web shells: c99 or any variation such as c99madshell, r57.php or any variation, gifimg.php.

Leave a Reply

linkedin facebook pinterest youtube rss twitter instagram facebook-blank rss-blank linkedin-blank pinterest youtube twitter instagram