Full Disclosure of CSRF/PHP Object Injection Vulnerability in WordPress Theme with 70,000+ Installs
With our service we cover WordPress plugins (as you might guess from our name), but not WordPress themes. There are a number of reasons for that, including the dearth of vulnerabilities being disclosed in themes, which seems to be related to the limited amount of potentially vulnerable code in them despite it being possible for them to contain all the same types of issues as plugins. We got a reminder of that when we did a check over some of the most popular themes available in the WordPress Theme Directory against the checks we do of changes being made to plugins as part of our proactive monitoring to try to catch serious vulnerabilities before they are exploited and a few other checks. The proactive monitoring checks didn’t pull up anything, but one of the other checks brought up the fact that the theme Hueman , which has 70,000+ active installs according to wordpress.org, contains the plugin OptionTree.
Last week disclosed that OptionTree contains an authenticated PHP object injection vulnerability after noticing its usage in another plugin. With the theme Hueman the situation is somewhat worse since it isn’t even using the latest version of OptionTree, which means that it is also still vulnerable to a vulnerability that was discovered by Kacper Szurek and was fixed over two years ago.
The outdated version of OptionTree also makes the authenticated PHP object injection vulnerability worse as well since unlike in the latest version of OptionTree where the attacker would need to have access to a WordPress account that can at least create a post, so normally at least a user with the Contributor role, it can be exploited by any logged in user in the version included with the them. That is because with the version of OptionTree included with the theme there is no protection against CSRF before the vulnerable code runs, so the attacker doesn’t need access to a valid nonce to exploit this. That also means that if an attack could get someone logged in to the website to visit a URL they specify it could also be exploited.
We have added a check to our Plugin Security Checker that will flag the inclusion vulnerable versions of OptionTree in another plugin. While that tool is designed for plugins, if you are a customer of our service you can upload plugins not in the Plugin Directory to check those and that same capability can also be used to check themes. While looking into adding that check to the tool we found that at least a couple of commercial themes have at least in the past included it as well, so you may want to manually check themes for that inclusion of that plugin or run them through the tool, where it could also identify possible other security issues in the themes.
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 only trying to notify the Theme Review team (interestingly there is contact form for contacting them that is linked to from every theme’s page, while plugins don’t have anything like that). 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).
Technical Details
The theme loads the plugin in the file /functions/init-core.php:
127 | load_template( get_template_directory() . '/option-tree/ot-loader.php' ); |
OptionTree then makes the function add_list_item() available to anyone logged in to WordPress through WordPress’ AJAX functionality:
530 | add_action( 'wp_ajax_add_list_item', array( $this, 'add_list_item' ) ); |
That function passes user input, in the form of the GET or POST input “settings” through the unserialize() function, which could permit PHP object injection:
73 74 75 76 | public function add_list_item() { ot_list_item_view( $_REQUEST['name'], $_REQUEST['count'], array(), $_REQUEST['post_id'], $_REQUEST['get_option'], unserialize( ot_decode( $_REQUEST['settings'] ) ), $_REQUEST['type'] ); die(); } |
Before it passes the value to unserialize() though it passes it through ot_decode(), which it turns out just base64 decodes the value (which can remove an impediment to PHP object injection being successful with user input like this):
2455 2456 2457 2458 2459 2460 | function ot_decode( $value ) { $func = 'base64' . '_decode'; return $func( $value ); } |
Proof of Concept
With our plugin for testing for PHP object injection installed and activated, the following proof of concept will cause the message “PHP object injection has occurred.” be shown, when logged in to WordPress.
Make sure to replace “[path to WordPress]” with the location of WordPress.
http://[path to WordPress]/wp-admin/admin-ajax.php?action=add_list_item&settings=TzoyMDoicGhwX29iamVjdF9pbmplY3Rpb24iOjA6e30=
I’m not that involved in security bit I suppose the full effect of this vulnerability is mitigated by the fact that that the website would need to have additional code somewhere which implements the __wakeup() method that itself is written on somewhat exploitable way? Some kind of chaining?
Without get into the details of that since it would make exploitation easier (which is also part of why our proof of concepts for this type of vulnerability are handled the way they are), advanced hackers have clearly been aware of code that they can utilize for that in WordPress and have exploited this type of issue on WordPress websites, but it does appear to stop less advanced hackers from exploiting this type of vulnerability.