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.
This one is using
128/2 to hide
This one used
eval in a created function.
This one hid in a common 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
- 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:
find . -type f -name "*.php" -printf '%s %p\n' > php_files.txt
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,
/root/.bash_historyfile to another server where the change history is preserved (not just
rsync). This way I have an audit of what
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.
- I had just started with a new company at this time. ↩