Steps to Take When you Know your PHP Site has been Hacked

This is a follow up post from my previous post “How to Tell if Your PHP Site has been Hacked or Compromised“. This post will discuss some the first steps you should take when you have identified that your site has been compromised. The first sections discuss a few points that are not relevant to everyone, the later sections will discuss how to fix the exploits.

Define What your Risk is! Read this First!

I am going to make some assumptions in this post and it’s your responsibility to make sure you understand this section. I am assuming that your compromised site is a content based website, a cms, a blog, a forum i.e an application where in most cases, you’re not putting other people in harms way etc. I am assuming that you are not running an e-commerce website or a website that if you were hacked, peoples’ privacy is at stake, if you hold personal information such as home addresses, phone numbers or other confidential information, the first steps you need to take is to disable public access to your website immediately.

Most top hosting providers have a kill switch like this, for example with SoftLayer you can disable the public network while still having access on the private network, a network you can connect to with a VPN. You would then use this connection to fix the vulnerabilities or in most cases a complete re-install.

If the above relates to you, this article will still provide some tips, however you need to take extra steps immediately.

To summarize, define what your risk is, for most people you will be able to fix the problem while your site remains online.

Where are you Hosted?

Managed Hosting Provider or Shared Host

If you use a managed hosting provider such as LiquidWeb, one of the numerous php cloud hosting services, specialty php hosting services or a shared host like dreamhost, they will take some responsibility on the server side of things. If you can take the advice from the previous article, identify the malicious scripts and simply report them, they will help you clean things up. There are still tips below that will help you remove the malicious scripts yourself and then your hosting provider can check the servers for further intrusion such as root or shell access.

Unmanaged Dedicated Server or VPS

If you are running your own server, the only way you will truly ever know if you fixed the vulnerability is by reinstalling your server. Thankfully this is trivial these days with tools such as puppet and chef and great hosting providers such as amazon aws and softlayer. If you have daily data backups (and you do have backups right?), this process can take as little as 30 minutes.

Note: If you don’t have a backup of your server config, packages used etc, take it as a lesson and do it properly during the reinstall with puppet or chef.

To some, the above is a drastic measure and I would agree in some cases. This is all about defining your level of risk, it’s very possible to clean up an attack without doing a re-install. However to make this decision, you would need to look at log files, running processes, shell logins, look for rootkits and more etc, I’m not going into these things in this article.

If your web server is not running as root (never do this), compilers are removed, php can only execute in one directory tree like /var/www and you have a firewall only allowing certain ports in and out, it’s highly unlikely an attacker would get root access from a php script alone. In most cases they will only be able to execute code as the same user that the web server runs as, usually www-data in apache and nginx. If you use something like php-fpm, php will execute as the user defined in the php-fpm configuration.

Cleaning up your Website

Note: If you deploy your code using version control, you can use these tools to check for changes to your code easier than the methods below, however the tips that I give are still useful for cleaning up user directories for your applications which will not be under version control.

If you followed the previous article and you found malicious files, the following sections will show you how to clean it up.

Optional: Redirect all Traffic to One Page, While you do the Cleanup.

Create a file named maintenance.html in your document root, this could be any html page informing your users of the situation. The benefit also means that any requests to uploaded attack files will be redirect here also.

Apache

In your .htaccess file

RewriteEngine On RewriteCond %{REQUEST_URI} !=/maintenance.html RewriteRule ^ /maintenance.html [L,R=302] 1 2 3 RewriteEngine On RewriteCond % { REQUEST_URI } ! =/ maintenance . html RewriteRule ^ / maintenance . html [ L , R = 302 ]

Nginx

in your sites conf file. (This is only partial, you still need the listen, server_name, root directives etc)

location / { if (-f $document_root/maintenance.html) { return 503; } } error_page 503 @maintenance; location @maintenance { rewrite ^(.*)$ /maintenance.html break; } 1 2 3 4 5 6 7 8 9 10 location / { if ( - f $ document_root / maintenance . html ) { return 503 ; } } error_page 503 @ maintenance ; location @ maintenance { rewrite ^ ( . * ) $ / maintenance . html break ; }

Cycle Through PHP Files in Writable Directories and Check for Malicious Files

Refer back to the previous post for examples of find commands. What you want to do is save the results in text files for the next section. You can do this in the shell like this:

find . -type f -name '*.php' > phpfiles.txt 1 find . - type f - name '*.php' > phpfiles .txt

This saves the results in a file called phpfiles.txt instead of showing on the screen.

I am going to assume you have run the following two commands:

Note: Commands such as search_for_php_in_writable were defined in: How to Tell if Your PHP Site has been Hacked or Compromised

./search_for_php_in_writable > php_in_writable.txt 1 . / search_for_php_in_writable > php_in_writable . txt

find . -type d -perm 0777 > writable_dirs.txt 1 find . - type d - perm 0777 > writable_dirs . txt

Finding all PHP tags inside files in your writable upload directories that are not PHP files

This may sound a little confusing so here is a brief rundown. writable_dirs.txt contains a list of directories in your document root that are writable. The following is going to search that directory for all files that are not .php files for any php tags. This will find php scripts masquerading as jpegs for example.

#!/bin/bash IFS=$'

' while read writable_dir do find "$writable_dir" -type f -not -iname '*.php' -print0 | xargs -0 egrep -i "(<\?php|<\?=|<\? *(?!(xml)))" --color done < writable_dirs.txt 1 2 3 4 5 6 7 8 #!/bin/bash IFS = $ '

' while read writable_dir do find "$writable_dir" - type f - not - iname '*.php' - print0 | xargs - 0 egrep - i "(<\?php|<\?=|<\? *(?!(xml)))" -- color done < writable_dirs .txt

Save the above script as find_non_php_in_writable and give it privileges to execute.

chmod +x find_non_php_in_writable; ./find_non_php_in_writable; 1 2 chmod + x find_non_php_in_writable ; . / find_non_php_in_writable ;

Note: php_in_writable.txt and writable_dirs.txt need to be in the same directory as these scripts.

You may find you get some false positives in PNG files as we are simply searching for text etc in these files. You can use the file command to see what linux thinks the file is for more confirmation. e.g here is a sample compromised file:

file ./uploads/profile/photo-8307.jpg ./uploads/profile/photo-8307.jpg: PHP script text 1 2 file . / uploads / profile / photo - 8307.jpg . / uploads / profile / photo - 8307.jpg : PHP script text

Dump the contents of the file or view it with a tool such as nano, vim or cat for more confirmation.

nano ./uploads/profile/photo-8307.jpg; vim ./uploads/profile/photo-8307.jpg; cat ./uploads/profile/photo-8307.jpg; 1 2 3 nano . / uploads / profile / photo - 8307.jpg ; vim . / uploads / profile / photo - 8307.jpg ; cat . / uploads / profile / photo - 8307.jpg ;

Here is what the file contained. Delete the file with:

rm ./uploads/profile/photo-8307.jpg 1 rm . / uploads / profile / photo - 8307.jpg

Remember you can use less for easier viewing or save the results to a file. If you use less, f key or space moves forward, q quits.

./find_non_php_in_writable | less ./find_non_php_in_writable > suspicious_files.txt 1 2 . / find_non_php_in_writable | less . / find_non_php_in_writable > suspicious_files . txt

Tip: In the above commands, the option/flag ‘l’ (lowercase L) in the egrep/grep command means that the results will only return the names of files matched, not the text matched in those files. You may wish to either add or remove this flag in your review. i.e grep -il

Tip. You can use grep -v to exclude results containing a keyword from your saved searches for easier viewing.

grep -iv 'binary file' suspicious_files.txt | less 1 grep - iv 'binary file' suspicious_files . txt | less

Finding Uses of Potentially Malicious PHP functions inside Writable Directories

The file php_in_writable.txt contains php files inside writable directories. We are going to search these files for potentially malicious php functions for further review.

#!/bin/bash IFS=$'

' while read php_file do egrep -i "(mail|eval|base64_decode|str_rot13|chmod|fwrite|exec|passthru|shell_exec|system|proc_open|popen|curl_exec|curl_multi_exec|show_source|fsockopen|pfsockopen|stream_socket_client) *\(" "$php_file" done < php_in_writable.txt 1 2 3 4 5 6 7 8 #!/bin/bash IFS = $ '

' while read php_file do egrep - i "(mail|eval|base64_decode|str_rot13|chmod|fwrite|exec|passthru|shell_exec|system|proc_open|popen|curl_exec|curl_multi_exec|show_source|fsockopen|pfsockopen|stream_socket_client) *\(" "$php_file" done < php_in_writable .txt

Note: The previous post talks about the preg_replace function with “e” modifier, I recommend searching for that as well. See the post for more information.

Save the above file as find_suspicious_php and chmod +x. Remember the two text files we made at the beginning need to be in the same directory.

./find_suspicious_php 1 . / find_suspicious_php

This is going to look for things like eval, base64_decode, curl, sockets, shell calls etc in .php files in your uploads directory.

You can use the same tips above to inspect each file further.

Use these two scripts to find suspicious php scripts in your upload directories and delete them with rm.

You May Need to Tweak the Above Commands

I have provided generic shell commands above for the average user, if you use different user permissions on your writable directories or ACLs then you need to make changes accordingly. For example you may use 770 permissions on your upload directory, If this is you, then you probably know what you are doing and can adjust. e.g

find . -type d -perm 0770 > writable_dirs.txt 1 find . - type d - perm 0770 > writable_dirs . txt

Other Considerations

The previous post has information on how to search your database, your html source for hidden iframes and web server config files such as .htaccess.

I recommend searching and cleaning your uploads directories and then completely replacing all other files with a clean copy either from the vendor of the script you use or from your source control system. Don’t just copy new files over, do a diff first in case completely new files have been added.

If you use some of the ideas and tools presented, you may find that your core files have not been modified making it unnecessary to re-install.

After the clean up

Find the Source of the Exploit

Check your access logs for the names of the malicious files you found, for example if you found a file called ejfyebh.php (I Did!)

grep -i "ejfyebh\.php" /var/log/apache/access.log | less 1 grep - i "ejfyebh\.php" / var / log / apache / access . log | less

Find the IP of the user who accessed that file and then search again for all records containing that IP address. This will lead you to the pages the attacker was on directly before the attack. There are sample access logs on the previous post.

If you are unable to determine the cause, ask someone with more experience, there is no point cleaning this mess up only to be exploited again.

Patch your Code

If you use a common scripts such as wordpress, make sure there are no recent security announcements. Ensure everything you are running is patched.

If you are running in-house, fix your code! Try to recreate the attack using the same files and requests the attacker used for a more detailed analysis.

Re-think your Permissions

This could be an entire series in itself but I will summarize briefly.

Make sure your web server is running as a regular user like www-data

Don’t use 777 permissions on upload directories or give the group your web server or php is running as write permissions to any files outside of your user upload directories. A simple approach is to assign www-data to the group owner of your files and give 644 permissions to files and 755 permissions to directories not in an upload directory. Have the files owned by another user.

Restrict the permissions of your upload directories, there are plenty of articles about this available, search for apache or nginx directory permissions. If you do this, even if an attacker manages to get something uploaded, they wont be able to modify your other files, this will make it easy to contain them.

Restrict PHP on a per domain basis to only the document root for that domain. In nginx with php-fpm you could add “ fastcgi_param PHP_ADMIN_VALUE open_basedir=$document_root:/tmp; ” to your configuration. This means that if you host several websites and one is exploited, they will not be able to affect other files outside of the document root of that website. On this subject, it’s a good idea to not use /tmp to hold session data stored in files, modern frameworks such as symfony2 create a personalized directory inside your application to hold this data – a good security practice.

” to your configuration. This means that if you host several websites and one is exploited, they will not be able to affect other files outside of the document root of that website. On this subject, it’s a good idea to not use /tmp to hold session data stored in files, modern frameworks such as symfony2 create a personalized directory inside your application to hold this data – a good security practice. Create a new user for every database that you need in production (some exceptions). e.g Never use the same user and database for three wordpress installations with different table prefixes. If one of those WP sites is exploited, the attacker can read wp-config.php and access your database, they can affect 3 of your sites instead of 1.

Never use the same user for two databases on different sites for the same reasons as above.

Monitor for more Activity

After an exploit, monitor your site every day for next week to ensure no suspicious files crop up, this will be easy using the find command and the mtime flag discussed in the previous article to find newly added/modified php files. Use some of the other tips as well for a more thorough inspection.

Conclusion

Over the last two articles, I’ve tried to give an overview of the tools available for tracking down malicious php. Don’t think that you need to use these scripts unmodified, there are many configuration options and flags to these commands that you can find using the man command on linux. i.e man find. Use them separately, use them together or use them to create your own.

People talk about security a lot in the PHP community, about cross site scripting, csrf, validating data etc. Server Administration is a job in itself and is a part of this puzzle.

I highly recommend learning more about the area yourself. Having a good understanding about the environment in which your code actually runs is beneficial to developers, not only from a security standpoint but from a performance one as well.

How to Tell if Your PHP Site has been Hacked or Compromised How to Fix vBulletin 4.x strict errors with PHP 5.4