Social Warfare Is Still Insecure
On Monday we noted that WordPress team had missed that the plugin Easy WP SMTP still contained vulnerabilities due to code related to the code that has been widely exploited this week. We tried to notify the developer of the plugin of that through a message on the WordPress Support Forum, but the moderators blocked that, so the WordPress team has been aware of that they missed those vulnerabilities for four days and yet they haven’t done anything about that. Using the moderation of the Support Forum to hide that there are problems with WordPress’ handling of security instead of working to resolve them is exactly kind of thing that led us back in September to start full disclosing vulnerabilities until the moderation of the forum is cleaned up.
You might think that someone on the WordPress side of things would have gotten the moderation cleaned up by now, since that by itself would make things better for the WordPress community, but would also stop those full disclosures, but that still hasn’t happened. That had dire consequences yesterday as a vulnerability we found in the plugin Social Warfare through our proactive monitoring of changes made to WordPress plugins in the Plugin Directory to try to catch serious vulnerabilities was widely exploited after we disclosed it.
The plugin was pulled from the Plugin Directory after we had left a message for the developer on the Support Forum, which was also blocked by the moderators. The plugin then returned with the vulnerability removed, but the insecure code that allowed anyone to have previously accessed it still unfixed, which probably shouldn’t be surprising considering what happened with the other plugin. What is hard to understand is how that could happen since our post clearly walked through the insecure code, so it shouldn’t have been hard to understand that it was still insecure by those that should be handling the security of plugins on the WordPress side of things.
It would be great if someone on the WordPress side of things can finally step in and get the forum moderation cleaned up and clean up the Plugin Directory team, so stuff like this doesn’t happen anymore. We have repeatedly offered to help the Plugin Directory team with the problems they are causing even with the grief they have unnecessarily caused us over the years, so would be happy to help if there was decision made to finally get that team improved.
So we don’t just completely repeat ourselves, we will go back through the insecure code in Social Warfare in the opposite order we explained things in the last post.
The plugin register the function init() from the class SWP_Database_Migration to run once plugins have loaded:
38 | add_action( 'plugins_loaded', array( $this, 'init' ), 100 ); |
That will in turn cause the function debug_parameters() to be run:
69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 | public function init() { // Check for and migrate the settings page data. if ( !$this->database_is_migrated() ) { $this->migrate(); } // Initialize the database for new installs. if ( !$this->has_3_0_0_settings() ) { $this->initialize_database(); } // Check for and migrate the post meta fields. if ( !$this->post_meta_is_migrated() ) { $this->update_post_meta(); $this->update_hidden_post_meta(); $this->update_last_migrated(); } $this->debug_parameters(); |
That makes all of the following code accessible to anyone:
196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 | public function debug_parameters() { global $post, $swp_user_options; // Output an array of user options if called via a debugging parameter. if ( true === SWP_Utility::debug('get_user_options') ) : $options = get_option( 'social_warfare_settings', array() ); $options = SWP_Database_Migration::filter_options( $options ); ksort( $options ); echo "<pre>", var_export( $options ), "</pre>"; wp_die(); endif; // /** // * Output text representation of array of user options if called via a debugging parameter. // * Text is formatted for use with `eval`. // * // * @since 3.5.0 | 14 DEC 2018 | Created. // */ // if ( true === SWP_Utility::debug('get_user_options_raw') ) { // $options = get_option( 'social_warfare_settings', array() ); // die(var_export('return ' . $options . ';')); // } if ( true === SWP_Utility::debug('get_filtered_options') ) : global $swp_user_options; echo "<pre>"; var_export( SWP_Database_Migration::filter_options( $swp_user_options ) ); echo "</pre>"; wp_die(); endif; if ( true == SWP_Utility::debug('get_post_meta') ) : add_action( 'template_redirect', array( $this, 'print_post_meta' ) ); endif; /** * v3.4.1 brought to our attention that the default value for * post meta `swp_float_location` is 'on' instead of 'deafult'. * *This debug paramter has an optional paramter, `post_type`, which defaults to 'page'. * * @since 3.4.2 */ if ( true == SWP_Utility::debug('reset_float_location') ) { if (!is_admin()) { wp_die('You do not have authorization to view this page.'); } $post_type = isset( $_GET['post_type'] ) ? $_GET['post_type'] : 'page'; $this->reset_post_meta_float_location( $post_type ); } // Migrate settings page if explicitly being called via a debugging parameter. if ( true === SWP_Utility::debug('migrate_db') ) { if (!is_admin()) { wp_die('You do not have authorization to view this page.'); } $this->migrate(); } // Initialize database if explicitly being called via a debugging parameter. if ( true === SWP_Utility::debug('initialize_db') ) { if (!is_admin()) { wp_die('You do not have authorization to view this page.'); } $this->initialize_db(); } // Update post meta if explicitly being called via a debugging parameter. if ( true === SWP_Utility::debug('migrate_post_meta') ) { if (!is_admin()) { wp_die('You do not have authorization to view this page.'); } $this->update_post_meta(); $this->update_hidden_post_meta(); } // Output the last_migrated status if called via a debugging parameter. if ( true === SWP_Utility::debug('get_last_migrated') ) { if (!is_admin()) { wp_die('You do not have authorization to view this page.'); } $this->get_last_migrated( true ); } // Update the last migrated status if called via a debugging parameter. if ( true === SWP_Utility::debug('update_last_migrated') ) { if (!is_admin()) { wp_die('You do not have authorization to view this page.'); } $this->update_last_migrated(); } if ( true === SWP_Utility::debug( ( 'delete_plugin_data' ) ) ) { $password = isset($_GET['swp_confirmation']) ? urldecode($_GET['swp_confirmation']) : ''; $user = wp_get_current_user(); if ( !is_admin() || false == current_user_can( 'administrator' ) || false == wp_check_password( $password, $user->user_pass, $user->ID) ) { wp_die('You do not have authorization to view this page.'); } global $wpdb; $query = "DELETE FROM {$wpdb->prefix}postmeta WHERE meta_key LIKE '\_%\_shares' OR meta_key LIKE 'swp\_%'"; $message = ''; $results = $wpdb->get_results( $query, ARRAY_N ); if ( $results ) { $message .= 'Deleted plugin postmeta.<br/>'; } $deleted = delete_option('social_warfare_settings'); if ( $deleted ) { $message .= 'Deleted plugin settings.<br/>'; } $deleted = delete_option('swp_registered_options'); if ( $deleted ) { $message .= 'Deleted plugin metadata.<br/>'; } if ( $message ) { $message .= 'All available Social Warfare and Social Warfare - Pro data has been deleted.'; wp_die( $message ); } wp_die('Sorry, there was an error processing the request. If you continue to get this message and need to delete all plugin data, please contact support at https://warfareplugins.com/submit-ticket'); } } |
Several of those seem like they are intended to have their access restricted as there is a check of the function is_admin(), but as we noted yesterday that doesn’t do what the developer seemed to think it did:
Things start off looking bad when the code checks if is_admin(), which doesn’t indicate if someone is Administrator, but if they are accessing an admin page, which even those not logged in can do (why the WordPress developer haven’t done something about that misleading function despite the problem with it being warned about before it was introduced in the production version of WordPress over 8 years ago is beyond us)
To easily see that in fact that functionality is still accessible to anyone (save for one piece that is restricted in a way that doesn’t seem totally appropriate), below is a proof of concept that shows the export of the plugin’s setting can be done by those not logged in to WordPress. There doesn’t appear to be any sensitive information that is accessible through that.
Proof of Concept
The following proof of concept will generate a page with the plugin’s settings.
Make sure to replace “[path to WordPress]” with the location of WordPress.
http://[path to WordPress]/wp-admin/admin-post.php?swp_debug=get_user_options
Personally I feel like simply dropping a note onto the WordPress support forums is not an appropriate method to go about contacting a developer regarding a security flaw. If it it a serious vulnerability, such as this one which affected a very large number of users then greater efforts can be made to contact the developer. Pretty much every single developer has their own website or at the least an email address. Contacting them through this medium takes no time and is far more direct.
I appreciate that you disagree with the admins’ actions at WordPress.org but for security issues you should put aside petty squabbles and disagreements. It’s massively irresponsible in my opinion to post proof of concepts and full disclosures without making a halfway serious attempt at contacting the responsible developer first.
As explained in our original post, we are full disclosing vulnerabilities and only contacting the developers through the Support Forum until the moderation of the Support Forum is cleaned up, so it isn’t a situation where we don’t understand how to contact developers and we did that for years before we started this protest. The problems with the moderation relate to the poor handling of security with plugins and don’t involve “petty squabbles and disagreements”.
We have been doing this since September, so the WordPress team had six months to prevent this particular full disclosure. We even warned them that this disclosure was coming, to again try to get them to take action, but they didn’t care. So they don’t seem to be concerned that people might get hacked.
The WordPress team (as well as other security companies and any else) could have done the same type of monitoring we do and found this vulnerability before us, but they didn’t. Again there doesn’t seem to be concern from them whether people get hacked. That is part of the problem with the moderation, as it is used to cover up the problems with the security of WordPress plugins exacerbated by the WordPress team.
You or anyone else can always spend time contacting the developers of these plugins about the vulnerabilities we are disclosing, though so far no one complaining about what we are doing appears to have been interested in doing that.