17 Jun

Even WordPress Security Plugin With 800,000+ Installs Is Failing To Do Proper Security Checks

To make sure we are providing customers of our service with the best data on vulnerabilities in WordPress plugins they may be using we do various monitoring. One of the things we do is monitor our websites and third-party data for indications that plugins are being targeted by hackers. That leads to us noticing plenty of up to that point publicly undisclosed vulnerabilities in plugins that hackers probably are already aware of and are likely already targeting. But what also gets pulled in with that frequently are what look to be hackers trying to access malicious files that hackers have placed on other websites that happened to be in the directories of WordPress plugins. What looks to be a recent example of that involved sending a request to:


That would be a file located in the directory of the security plugin All In One WP Security (All In One WP Security & Firewall), though just looking at the file name, bkrijilt.php, it would seem unlikely that the plugin would actually contain that file. Checking the plugin, the current version doesn’t contain that file.

In some instances a request like that could indicate that the plugin contains a vulnerability that allowed a file to be added that location, though considering that this plugin has 800,000+ active installations according to wordpress.org that seemed unlikely. All the same we decided to do a few checks of the plugin after running across that. We didn’t find code that looks like it could have caused that, but what we did find is that the plugin is missing a fairly basic security check that it should have, though this doesn’t lead to a vulnerability.

A fairly common starting point for code that leads to exploited vulnerabilities in WordPress plugins is registering for a function to run during “admin_init” since that can run even for those not logged in, but is often used for functionality only intended to be run by those logged in to WordPress as the highest level users, Administrators.

This plugin registers for the function aiowps_csv_download() to be accessed through that:

add_action('admin_init', array(&$this, 'aiowps_csv_download'));

The code in that function has three similar groups of code, this is the first:

function aiowps_csv_download() {
	global $aio_wp_security;
	if (isset($_POST['aiowpsec_export_acct_activity_logs_to_csv'])) { //Export account activity logs
		$nonce = $_REQUEST['_wpnonce'];
		if (!wp_verify_nonce($nonce, 'aiowpsec-export-acct-activity-logs-to-csv-nonce')) {
			$aio_wp_security->debug_logger->log_debug("Nonce check failed for export account activity logs to CSV!", 4);
			die(__('Nonce check failed for export account activity logs to CSV!', 'all-in-one-wp-security-and-firewall'));
		include_once 'wp-security-list-acct-activity.php';
		$acct_activity_list = new AIOWPSecurity_List_Account_Activity();
		//Let's build a list of items we want to export and give them readable names
		$export_keys = array(
			'user_id' => 'User ID',
			'user_login' => 'Username',
			'login_date' => 'Login Date',
			'logout_date' => 'Logout Date',
			'login_ip' => 'IP'
		$this->aiowps_output_csv($acct_activity_list->items, $export_keys, 'account_activity_logs.csv');

That code allows downloading a CSV file containing activity on the website. What should be in that code is a capabilities check to make sure only the intended users have access to that, but it is missing. In this case it should normally check for the “manage_options” capability, which normally only Administrators have.

There is another security check done there, a nonce check to prevent cross-site request forgery (CSRF):

if (!wp_verify_nonce($nonce, 'aiowpsec-export-acct-activity-logs-to-csv-nonce')) {
	$aio_wp_security->debug_logger->log_debug("Nonce check failed for export account activity logs to CSV!", 4);
	die(__('Nonce check failed for export account activity logs to CSV!', 'all-in-one-wp-security-and-firewall'));

Since that nonce is only accessible on the page where this functionality is intended to be accessed from that would normally work to provide the equivalent of a capabilities check, but the WordPress documentation for those is clear that shouldn’t be relied on for that:

Nonces should never be relied on for authentication or authorization, access control. Protect your functions using current_user_can(), always assume Nonces can be compromised.

We could claim to be shocked that such a popular plugin, and a security plugin at that, isn’t being properly secured, but this is a common occurrence.

So what is the takeaway from that? There are two quick ones:

First, we wouldn’t recommend using security plugins unless there is evidence, preferably from independent testing, that they provide effective protection, since as this shows they often are not developed by people that necessarily have the best understanding of security, so they often provide little to no protection, while possibly introducing security vulnerabilities of their own.

Second, more plugins could use security reviews. Considering this plugin is one of the most popular, it would probably make sense for plugins like it to get check over for this sort of thing, as other plugins are probably following the lead of popular plugins, so if those are more secure it could help to make sure others also are properly secured. Considering that they are not doing even easier things that could improve security that seems unlikely to be taken up by the WordPress team responsible for plugins any time soon. In the meantime we can do security reviews of plugins for you.