Recently Closed WordPress Plugin with 60,000+ Installs Contains Multiple Vulnerabilities
The plugin Contact Form Submissions was closed on the WordPress Plugin Directory yesterday. That is one of the 1,000 most popular plugins with 60,000+ installs, so we were alerted to its closure. While we were looking in to the plugin to see if there were any serious vulnerabilities we should be warning users of the plugin that also use our service, we found that it contains a CSV injection vulnerability and an authenticated SQL injection vulnerability, which can also exploited through cross-site request forgery (CSRF).
The CSV injection vulnerability involves a lack of escaping when using the plugin “Export to CSV” feature, as can be confirmed with the proof of concept below.
The SQL injection vulnerability involves the plugin’s Submissions admin page. When viewing that the function set_columns() is set as a columns filter:
6 | add_filter('manage_wpcf7s_posts_columns', array($this, 'set_columns'), 999); |
That function, which is located in the file /Admin.php, will set the value of the GET input “wpcf7_contact_form” to the variable $form_id without sanitizing or validating it and then passes it to the function get_available_columns():
148 149 150 151 152 153 154 155 156 157 158 159 160 | public function set_columns($columns) { $columns = array( 'cb' => '<input type="checkbox" />', 'submission' => __('Submission', 'contact-form-submissions'), 'form' => __('Contact Form', 'contact-form-submissions') ); // dynamically add cols if the user selects a form if (isset($_GET['wpcf7_contact_form']) && !empty($_GET['wpcf7_contact_form'])) { $form_id = $_GET['wpcf7_contact_form']; $wpcf7s_columns = $this->get_available_columns($form_id); |
That function in turn uses the value in unprepared SQL statements, which permits SQL injection:
411 412 413 414 415 416 417 | public function get_available_columns($form_id = 0) { global $wpdb; $post_id = $wpdb->get_var("SELECT post_id FROM $wpdb->postmeta WHERE meta_key = 'form_id' AND meta_value = $form_id LIMIT 1;"); $columns = $wpdb->get_col("SELECT meta_key FROM $wpdb->postmeta WHERE post_id = $post_id AND meta_key LIKE '%wpcf7s_%' GROUP BY meta_key"); |
That page is accessible by Editor level users and above by default. Since there is no nonce check it can be exploited through CSRF.
WordPress Causes Full Disclosure
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:
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.
Proof of Concept for CSV Injection
- Set the name of a Contact Form 7 form submission to “=2-5-cmd|’ /C calc’!A0”.
- Export the form submission through the plugin’s “Export to CSV” feature.
- Opening the resulting file in Excel on a Windows computer will cause the Calculator program to open (there is warning shown first though).
Proof of Concept for Authenticated SQL Injection
The following proof of concept will take varying amounts of time for the page to fully load depending on how long you specify MySQL sleep function to run, when logged in as an Editor.
Make sure to replace “[path to WordPress]” with the location of WordPress, “[form id]” with the ID of a contact form with submissiosn, and “[sleep time]” with how many seconds you want sleep to occur for.
http://[path to WordPress]/wp-admin/edit.php?post_status=all&post_type=wpcf7s&wpcf7_contact_form=[form id]+and+SLEEP([sleep time])&filter_action=Filter&paged=1