Developer of WP Fastest Cache Obliquely Discloses SQL Injection Vulnerability, Fix Isn’t Generally Available
Yesterday, the developer of the 1+ million install WordPress plugin WP Fastest Cache committed a change to the plugin in the Subversion repository underlying the WordPress Plugin Directory that fixed a SQL injection vulnerability. Unfortunately, they haven’t released a new version of the plugin that makes the fix available to the public. If hackers haven’t already realized what is at issue, it shouldn’t take them long.
The commit message for the update was “Security Enhancements”, which suggests a vulnerability could have been fixed. Our machine learning (artificial intelligence (AI)) based system for catching fix vulnerabilities being fixed in updates to WordPress plugins flagged the change as fixing a vulnerability. Could hackers have a similar system? Who knows, but it isn’t too complicated to create what we have, so we wouldn’t want to be they don’t.
Looking at the change made, the developer had changed a SQL statement, which makes a request to the WordPress database. Part of what they did doesn’t make sense, as they are using the WordPress function for creating a prepared SQL statement, but didn’t actually separate the data from the statement, so the change serves no purpose.
The change was made to code in the function is_user_admin() in the file /inc/cache.php. Here is the previous code:
485 486 487 488 489 490 491 492 493 | if(isset($username) && $username){ $res = $wpdb->get_var("SELECT `$wpdb->users`.`ID`, `$wpdb->users`.`user_login`, `$wpdb->usermeta`.`meta_key`, `$wpdb->usermeta`.`meta_value` FROM `$wpdb->users` INNER JOIN `$wpdb->usermeta` ON `$wpdb->users`.`user_login` = \"$username\" AND `$wpdb->usermeta`.`meta_key` LIKE \"%_user_level\" AND `$wpdb->usermeta`.`meta_value` = \"10\" AND `$wpdb->users`.`ID` = `$wpdb->usermeta`.user_id ;" ); |
And here is the new code:
485 486 487 488 489 490 491 492 493 494 495 | if(isset($username) && $username){ $username = esc_sql($username); $res = $wpdb->get_var($wpdb->prepare("SELECT `$wpdb->users`.`ID`, `$wpdb->users`.`user_login`, `$wpdb->usermeta`.`meta_key`, `$wpdb->usermeta`.`meta_value` FROM `$wpdb->users` INNER JOIN `$wpdb->usermeta` ON `$wpdb->users`.`user_login` = \"$username\" AND `$wpdb->usermeta`.`meta_key` LIKE \"%_user_level\" AND `$wpdb->usermeta`.`meta_value` = \"10\" AND `$wpdb->users`.`ID` = `$wpdb->usermeta`.user_id ;" )); |
You can see $wpdb->prepare() is now used, but isn’t utilized.
The change that addresses the vulnerability, though not ideally, is the usage esc_sql() on the variable $username. That variable comes from user input.
The way to handle this with a proper prepared statement looks like this instead:
$res = $wpdb->get_var($wpdb->prepare("SELECT `$wpdb->users`.`ID`, `$wpdb->users`.`user_login`, `$wpdb->usermeta`.`meta_key`, `$wpdb->usermeta`.`meta_value` FROM `$wpdb->users` INNER JOIN `$wpdb->usermeta` ON `$wpdb->users`.`user_login` = \"%s\" AND `$wpdb->usermeta`.`meta_key` LIKE \"%_user_level\" AND `$wpdb->usermeta`.`meta_value` = \"10\" AND `$wpdb->users`.`ID` = `$wpdb->usermeta`.user_id ;", $username )); |
Here is the user input the variable comes from:
478 479 480 | foreach ((array)$_COOKIE as $cookie_key => $cookie_value){ if(preg_match("/wordpress_logged_in/i", $cookie_key)){ $username = preg_replace("/^([^\|]+)\|.+/", "$1", $cookie_value); |
As the code runs when accessing frontend pages of the website, it allows an attacker to cause arbitrary SQL code to run, which is otherwise known as SQL injection. That could allow an attacker to read the contents of the WordPress database. We created a proof of concept to confirm exploitation can occur.
The vulnerable code exist in the latest version of the plugin, 1.2.1, so it can’t be fixed by updating the plugin yet. Either the code needs to be manually replaced at this time or the plugin should be deactivated until a new version is released.
We have notified the developer of the problem with their lack of an update to make the fix generally available and the less than ideal fix.
Update (November 13): Version 1.2.2 of the plugin was released yesterday, which fixes the vulnerability.