While recently auditing my web server logs for 404 errors, I came across a new pattern of hacker attempts. There were a number of attempts at opening PHP files with a specific name. A quick search of the internet turned up an article, WordPress Security – Arbitrary File Upload in Gravity Forms by Rodrigo Escobar. The article does a fantastic job of explaining this particular exploit.
To summarize how the attack works, the hacker attempts to exploit a vulnerability identified in the Gravity Forms plugin in order to upload a malicious PHP file to the web site. The file is loaded into the wp-content/uploads path of WordPress. Upon successful upload, they can call the PHP file directly and the Apache server will execute the code. Luckily for me, I don’t have this plugin installed and the attempts on my site were all failed attempts.
As I researched this issue further, it dawned on me that a number of exploits against WordPress work in a similar fashion. The identify a means of exploiting a file upload mechanism within WordPress. These mechanisms are usually designed to place the uploaded file somewhere within the wp-content/uploads directory. The hacker then tries to call the file directly.
As far as I can tell, there should NEVER be a legitimate case where a PHP file needs to be uploaded to this directory and run directly from this directory. The directory was designed to be a place for storing images, videos and documents (Word, PowerPoint, PDFs, etc.). Therefore, if a PHP file does exist in this directory structure, a successful hacker attack is probably already underway.
Solution #1 – Fail2Ban
My first reaction was to setup a new rule in my Fail2Ban system. This software does a great job of monitoring system logs for recurring patterns and then taking automatic action to remedy the problem. In the case of my web applications, it directly manipulates the iptables (software firewall) to block the offender.
I wrote a new rule to identify the access of PHP files in the uploads directory. The regex expression was fairly straightforward:
I configured the service to block a source IP after just one attempt of this URL pattern. I restarted the Fail2Ban service and did some testing. The filter worked as designed.
The problem with this solution is that if the hacker is successful the first time, they will still be able to perform the hack before the software blocks them for good. It’s unlikely that the hacker will be successful in just one shot, but I don’t want to take that risk.
Solution #2 – Deny Access in Apache Configuration
I decided that I wanted to configure Apache to deny access to any attempt at a PHP file in that directory. A quick search of the internet gave me a couple of good resources. I could make use of the FilesMatch directive in the Apache configuration file to identify a file pattern and block access to that file. I did some testing and I developed the following solution:
<Directory "/path/to/wordpress/htdocs/wp-content/uploads"> ............. <FilesMatch "\.php$"> Order allow,deny Deny from all </FilesMatch> </Directory>
This information goes directly within the VirtualHost configuration for the WordPress web site in the Apache configuration files. After making the configuration change and restarting Apache, I dropped a test PHP file into various directory levels in wp-content/uploads. I tried accessing the PHP file and Apache always gave me a Forbidden access message.
Solution #3 – All of the Above!
The second solution is obviously the better approach. Even if the hacker is successful at uploading a file through an exploited upload mechanism, Apache will make sure that they never actually run the uploaded script. However, security works best in layers. Even though solution #2 is best, keep solution #1 in place too. Even though the hacker can’t succeed in running their script, allow the Fail2Ban software to recognize the attempt and block that hacker from any further exploit attempts on your site. They are obviously up to no good, so keep them from attempting any further harm.