Finding PHP Web Shells

Here are few PHP web shell scripts I found in a production server in late 20161. I’ll show you some of them, and my efforts for securing a production server.

This one is using straight-forward obfuscation.

Sample shell script
Found shell script – eval(gzinflate(base64_decode(...)));

This one is using 128/2 to hide base64_decode.

Sample shell script
Found shell script – $f="base64_decode"; $f(...);

This one used base64_decode and eval in a created function.

Sample shell script
Found shell script – eval(base64_decode(...));

This one hid in a common WordPress file.

Sample shell script
Found shell script – hiding as a WordPress file

There are some projects dedicated to finding web shells (e.g. PHP-Shell-Detector), and far more dedicated to creating and hiding web shells (e.g. Creating A Truly Invisible PHP Shell, An Introduction to Web Shells).

Common PHP web shell signs

  • eval
  • preg_replace
  • strrev
  • str_rot13
  • base64_decode
  • assert
  • md5
  • shell_exec / exec / system / passthru
  • ` (backticks)
  • @

My team doesn’t use these functions or even @ to silence errors, so they shouldn’t be found by grep. In finding them, I’d like to go a step further and monkey patch the PHP runtime to trap calls to some of these functions, but dormant code can remain long-term. I have a simpler prevention method anyway.

Finding infections involved searching for file and folder modified timestamps, files that start with a dot (.), files that don’t match my PSR naming conventions (i.e the class name and the file name are the same), and files with random names.

To make this easier, I run this command to show me all the PHP files in the current directory tree along with their file sizes:

Once a web shell is found, I can grep the file system for its telltale signature like part of its base64-encoded payload. This is how I found 47 files with shell script code from 4 web shell variants. Of course some injection methods are more insidious, for example

so going into suspect folders and noting timestamps of files is important. This is what I had to do before putting all the web server files under version control (see below).


In the end I migrated servers. I also ensured my team put all the source web files under git version control, locked down some attack vectors that were open, disabled some harmful functions in php.ini, disabled root SSH access, and downgraded folder permissions across the board. I also filter PHP’s $_REQUEST superglobal on every page request using a prepended script via php_value auto_prepend_file "..." in a vhost file. Why? Because some remote code exploits involves sending control code in request headers. For example,

One trick I use, pen testers be warned, is I watch and send the /root/.bash_history file to another server where the change history is preserved (not just rsync). This way I have an audit of what root is doing.

Git as a prevention method

The reason I really like git version control is because when performing git pull on a compromised server, any new or changed files should be caught when git asks you to “stash or commit” them. Win.


  1. I had just started with a new company at this time.