GDPR Cookie Consent, a popular WordPress plugin with 700,000+ active installations, fixed a vulnerability affecting version 1.8.2 and below that could lead to authenticated stored XSS and privilege escalation.
Reference
A CVE ID has been requested and we’ll update this post when it is assigned.
In the __construct
method of the “admin/modules/cli-policy-generator/classes/class-policy-generator-ajax.php” script, the plugin registers the cli_policy_generator
action via the WordPress AJAX API, which loads ajax_policy_generator
:
public function __construct() { add_action('wp_ajax_cli_policy_generator',array($this,'ajax_policy_generator')); } /* * * Main Ajax hook for processing requests */ public function ajax_policy_generator() { $out=array( 'response'=>false, 'message'=>__('Unable to handle your request.','cookie-law-info'), ); $non_json_response=array(); if(isset($_POST['cli_policy_generator_action'])) { $cli_policy_generator_action=$_POST['cli_policy_generator_action']; $allowed_actions=array('autosave_contant_data','save_contentdata','get_policy_pageid'); if(in_array($cli_policy_generator_action,$allowed_actions) && method_exists($this,$cli_policy_generator_action)) { $out=$this->{$cli_policy_generator_action}(); } } if(in_array($cli_policy_generator_action,$non_json_response)) { echo is_array($out) ? $out['message'] : $out; }else { echo json_encode($out); } exit(); }
It lacks capability checks and, although a nonce is sent over AJAX, it is not checked anywhere in the PHP code. It accepts three different values for the cli_policy_generator_action
input and calls the corresponding method. Two of them, autosave_contant_data
and save_contentdata
can be easily exploited by an attacker.
Privilege Escalation
The save_contentdata
method allows the administrator to save the GDPR cookie notice to the database as a page post type:
public function save_contentdata() { $out=array( 'response'=>true, 'er'=>'' ); $content_data=isset($_POST['content_data']) ? $_POST['content_data'] : array(); $page_id=(int) isset($_POST['page_id']) ? $_POST['page_id']*1 : 0; $enable_webtofee_powered_by=(int) isset($_POST['enable_webtofee_powered_by']) ? $_POST['enable_webtofee_powered_by']*1 : 0; $id=wp_insert_post( array( 'ID'=>$page_id, //if ID is zero it will create new page otherwise update 'post_title'=>'Cookie Policy', 'post_type'=>'page', 'post_content'=>Cookie_Law_Info_Cli_Policy_Generator::generate_page_content($enable_webtofee_powered_by,$content_data,0), 'post_status' => 'draft', //default is draft ) ); if(is_wp_error($id)) { $out=array( 'response'=>false, 'er'=>__('Error','cookie-law-info'), //'er'=>$id->get_error_message(), ); }else { Cookie_Law_Info_Cli_Policy_Generator::set_cookie_policy_pageid($id); $out['url']=get_edit_post_link($id); } return $out; }
An authenticated user such as a subscriber can use it to put any existing page or post (or the entire website) offline by changing their status from “published” to “draft”:
Additionally, it is possible to delete or change their content. Injected content can include formatted text, local or remote images as well as hyperlinks and shortcodes.
Authenticated Stored XSS
The autosave_contant_data
method is used to save, in the background, the GDPR cookie info page while the admin is editing it:
public function autosave_contant_data() { global $wpdb; $scan_table=$wpdb->prefix.$this->main_tb; $out=array( 'response'=>true, 'er'=>'' ); $content_data=isset($_POST['content_data']) ? $_POST['content_data'] : array(); $page_id=isset($_POST['page_id']) ? $_POST['page_id'] : ''; $enable_webtofee_powered_by=(int) isset($_POST['enable_webtofee_powered_by']) ? $_POST['enable_webtofee_powered_by']*1 : 0; if(is_array($content_data)) { $content_html=Cookie_Law_Info_Cli_Policy_Generator::generate_page_content($enable_webtofee_powered_by,$content_data); update_option('cli_pg_content_data',$content_html); }else { $out=array( 'response'=>false, 'er'=>__('Error','cookie-law-info') ); } return $out; }
It saves the data into the cli_pg_content_data
database field without validating it. An authenticated user can use it to inject JavaScript code, which will be loaded and executed each time someone, authenticated or not, visits the “http://example.com/cli-policy-preview/” page.
Timeline
We discovered the vulnerability and reported it to the wordpress.org team on January 28, 2020 and to the author on February 04. A new version 1.8.3 was released on February 10, 2020.
Recommendations
Update as soon as possible if you have version 1.8.2 or below installed.
If you are using our web application firewall for WordPress, NinjaFirewall WP Edition (free) and NinjaFirewall WP+ Edition (premium), you are protected against this vulnerability since January 28th, 2020.
Stay informed about the latest vulnerabilities in WordPress plugins and themes: @nintechnet