Cross-Site Request Forgery (CSRF)/Cross-Site Scripting (XSS) Vulnerability in Simply Static
The description page for the plugin Simply Static makes the case for it use based in part on the insecurity of WordPress plugins:
WordPress is used by one in four websites[1]. That makes it a prime target for hackers. There are a lot of ways that your site can be compromised, but two-thirds of all hacks are caused by vulnerabilities in WordPress plugins, themes, and core files[2].
Keeping WordPress secure requires constant vigilance. Exploits are being found for WordPress themes and plugins every day. Even WordPress itself has critical vulnerabilities from time to time. If you don’t stay on top of updates, your site will get hacked. It’s just a matter of when.
But what if there was an easy way to keep WordPress secure? What if you could lock WordPress away somewhere where no one can get to it but you?
The plugin itself could be used an example of that. An arbitrary file viewing vulnerability, which is a type that is likely to have exploitation attempts, was recently disclosed in the plugin. When we went to look into that to provide to put together a post with details of the vulnerability we noticed there were other security issues, though much less severe.
We should mention that idea is the WordPress website with this plugin would be in a location that is non-public, so if people were doing that, the threat of vulnerabilities in the plugin would be limited:
With Simply Static you can put your WordPress installation in a secure location and publish a static site for the rest of the world to see. You can keep WordPress at a secret URL, protect it with .htaccess, or even put it behind a VPN. Simply Static will create static copies of all of the pages from your WordPress installation and replace the URLs to match where you’ll be hosting it.
While taking a quick look over the plugin we noticed that is protection against cross-site request forgery (CSRF) was only half implemented. While a nonce had been added to the forms for changing the plugin’s settings and generating the static files, there was no check done to see if it is valid when processing requested from those forms.
Here is where settings were saved (in the file /includes/class-simply-static.php):
371 372 373 374 375 | if ( isset( $_POST['_settings'] ) ) { $this->save_options(); $message = __( 'Settings saved.', self::SLUG ); $this->view->add_flash( 'updated', $message ); } |
The generation of the static files is handle through WordPress’ AJAX functionality, it is registered here:
82 | add_action( 'wp_ajax_generate_static_archive', array( self::$instance, 'generate_static_archive' ) ); |
The function generate_static_archive also don’t do any nonce check:
279 280 281 282 283 284 | function generate_static_archive() { $action = $_POST['perform']; $archive_manager = new Simply_Static_Archive_Manager( $this->options ); $archive_manager>perform( $action ); |
With that an attacker could cause a logged in Administrator to change the settings without intending it and any logged in user to re-generate the static files.
Often times a CSRF vulnerability can be used to add JavaScript code to the setting’s page, leading to cross-site request forgery (CSRF)/cross-site request (XSS) vulnerability. In this case we found the settings’ values were escaped, which prevents that from happening. But the same value were used elsewhere allowing cross-site scripting to occur. For example, the Additional URLS value will be be included on the Generate page (after doing a re-generation) and on the Diagnostic’s page.
The value of that was saved without sanitization here:
437 | ->set( 'additional_urls', filter_input( INPUT_POST, 'additional_urls' ) ) |
It was output on the Diagnostic’s page without escaping it here (in the file /includes/class-simply-static-diagnostic.php):
106 107 | public function is_additional_url_valid( $url ) { $label = sprintf( __( 'Checking if Additional URL <code>%s</code> is valid', 'simply-static' ), $url ); |
After we notified the developer they released version 1.7.1, which fixes the issues. Checks were added to insure a valid nonce is included, here is the one that one that was added for saving the settings:
460 461 | public function save_options() { check_admin_referer( 'simply-static_settings' ); |
The settings’ values are now run through a new function fetch_post_value() to sanitize them.
Proof of Concept
The following proof of concept will cause an alert box with any accessible cookies to be shown on the page /wp-admin/admin.php?page=simply-static_diagnostics, when submitted as an Administrator.
Make sure to replace “[path to WordPress]” with the location of WordPress.
<html> <head> </head> <body> <form method="post" action="http://localhost/wordpress/wp-admin/admin.php?page=simply-static_settings"> <input type="hidden" name="_settings" value="1" /> <input type="hidden" name="additional_urls" value="<script>alert(document.cookie);</script>" /> <input type="submit" value="Submit" /> </form> </body> </html>
Timeline
- 10/11/2016 – Developer notified
- 10/12/2016 – Developer responds.
- 10/20/2016 – Version 1.7.1 released, which fixes the vulnerability.