Security
Headlines
HeadlinesLatestCVEs

Headline

CVE-2020-36699: Authenticated settings change vulnerability in WordPress Quick Page/Post Redirect plugin (unpatched).

The Quick Page/Post Redirect Plugin for WordPress is vulnerable to authorization bypass due to missing capability checks on the qppr_save_quick_redirect_ajax and qppr_delete_quick_redirect functions in versions up to, and including, 5.1.9. This makes it possible for low-privileged attackers to interact with the plugin settings and to create a redirect link that would forward all traffic to an external malicious website.

CVE
#vulnerability#web#js#java#wordpress#php#auth#telnet

Revision: July 09th, 2020

This plugin is not maintained any longer and the vulnerability has never been fixed. Make sure to follow the recommendations below.

Quick Page/Post Redirect, a WordPress plugin with 200,000+ active installations, is prone to an authenticated settings change vulnerability in version 5.1.9 and below.

The plugin is used to redirect a page to a different URL or location.
In the “quick-pagepost-redirect-plugin/page_post_redirect_plugin.php” script, the plugin registers the qppr_save_quick_redirect AJAX action to load the qppr_save_quick_redirect_ajax function:

add_action( 'wp_ajax_qppr_save_quick_redirect’, array( $this, ‘qppr_save_quick_redirect_ajax’ ) ); // register ajax save quick redirect - 5.0.7

The function saves all redirects to the WordPress wp_options table:

function qppr_save_quick_redirect_ajax(){ check_ajax_referer( 'qppr_ajax_verify’, 'security’, true ); $protocols = apply_filters('qppr_allowed_protocols’,array( 'http’, 'https’, 'ftp’, 'ftps’, 'mailto’, 'news’, 'irc’, 'gopher’, 'nntp’, 'feed’, 'telnet’, 'mms’, 'rtsp’, 'svn’, 'tel’, 'fax’, ‘xmpp’)); $request = isset($_POST[‘request’]) && trim($_POST[‘request’]) != ‘’ ? esc_url(str_replace(' ',’%20’,trim($_POST[‘request’])), null, ‘appip’) : '’; $requestOrig = isset($_POST[‘original’]) && trim($_POST[‘original’]) != ‘’ ? esc_url(str_replace(' ',’%20’,trim($_POST[‘original’])), null, ‘appip’) : '’; $destination = isset($_POST[‘destination’]) && trim($_POST[‘destination’]) != ‘’ ? esc_url(str_replace(' ',’%20’,trim($_POST[‘destination’])), null, ‘appip’) : '’; $newWin = isset($_POST[‘newwin’]) && (int) trim($_POST[‘newwin’]) == 1 ? 1 : 0; $noFollow = isset($_POST[‘nofollow’]) && (int) trim($_POST[‘nofollow’]) == 1 ? 1 : 0; $updateRow = isset($_POST[‘row’]) && $_POST[‘row’] != ‘’ ? (int) str_replace('rowpprdel-',’’,$_POST[‘row’]) : -1; $curRedirects = get_option('quickppr_redirects’, array()); $curMeta = get_option('quickppr_redirects_meta’, array()); $rkeys = array_keys($curRedirects); $mkeys = array_keys($curMeta); … … // now save data back to the db options update_option('quickppr_redirects’, $curRedirects); update_option('quickppr_redirects_meta’, $curMeta); $this->qppr_try_to_clear_cache_plugins(); echo 'saved’; exit; }

It doesn’t check user capabilities and it is only protected with a security nonce. The nonce and the JavaScript code are populated and echoed by the qppr_admin_scripts function which is loaded by the admin_enqueue_scripts hook, i.e., each time a page loads in the admin backend:

add_action( ‘admin_enqueue_scripts’ , array( $this, ‘qppr_admin_scripts’ ) ); … … function qppr_admin_scripts($hook){ if(in_array( $hook, array( 'post-new.php’, 'edit.php’, 'post.php’, 'toplevel_page_redirect-updates’, 'quick-redirects_page_redirect-options’, 'quick-redirects_page_redirect-summary’, 'quick-redirects_page_redirect-faqs’, 'quick-redirects_page_redirect-import-export’, ‘quick-redirects_page_meta_addon’ ) ) ){ $ajax_add_nonce = wp_create_nonce( ‘qppr_ajax_verify’ ); $secDeleteNonce = wp_create_nonce( ‘qppr_ajax_delete_ALL_verify’ ); $protocols = apply_filters( 'qppr_allowed_protocols’, array( 'http’, 'https’, 'ftp’, 'ftps’, 'mailto’, 'news’, 'irc’, 'gopher’, 'nntp’, 'feed’, 'telnet’, 'mms’, 'rtsp’, 'svn’, 'tel’, 'fax’, ‘xmpp’)); wp_enqueue_style( 'qppr_admin_meta_style’, plugins_url( '/css/qppr_admin_style.css’, __FILE__ ) , null , $this->ppr_curr_version ); //wp_enqueue_script( 'qppr_admin_meta_script’, plugins_url( '/js/qppr_admin_script.js’, __FILE__ ) , array(‘jquery’), $this->ppr_curr_version ); wp_enqueue_script( 'qppr_admin_meta_script’, plugins_url( '/js/qppr_admin_script.min.js’, __FILE__ ) , array(‘jquery’), $this->ppr_curr_version ); wp_localize_script( 'qppr_admin_meta_script’, 'qpprData’, array( ‘msgAllDeleteConfirm’ => __( ‘Are you sure you want to PERMANENTLY Delete ALL Redirects and Settings (this cannot be undone)?’, ‘quick-pagepost-redirect-plugin’ ),’msgQuickDeleteConfirm’ => __( 'Are you sure you want to PERMANENTLY Delete ALL Quick Redirects?’, ‘quick-pagepost-redirect-plugin’ ), ‘msgIndividualDeleteConfirm’ => __( 'Are you sure you want to PERMANENTLY Delets ALL Individual Redirects?’, ‘quick-pagepost-redirect-plugin’ ), ‘securityDelete’ => $secDeleteNonce, ‘protocols’ => $protocols, ‘msgDuplicate’ => __( 'Redirect could not be saved as a redirect already exists with the same Request URL.’, ‘quick-pagepost-redirect-plugin’ ) , ‘msgDeleteConfirm’ => __( 'Are you sure you want to delete this redirect?’, ‘quick-pagepost-redirect-plugin’ ) , ‘msgErrorSave’ => __( 'Error Saving Redirect\nTry refreshing the page and trying again.’, ‘quick-pagepost-redirect-plugin’ ) , ‘msgSelect’ => 'select a file’, ‘msgFileType’ => __( 'File type not allowed,\nAllowed file type: *.txt’, ‘quick-pagepost-redirect-plugin’ ) , ‘adminURL’ => admin_url(‘admin.php’),’ajaxurl’=> admin_url(‘admin-ajax.php’), ‘security’ => $ajax_add_nonce, ‘error’ => __('Please add at least one redirect before submitting form’, ‘quick-pagepost-redirect-plugin’))); } return; }

Here too, it doesn’t check user capabilities but instead restricts the access to the function depending on which page is viewed. The problem is that three of them, ‘post-new.php’, ‘edit.php’ and ‘post.php’, are not restricted to the administrator only because WordPress allows some users with low privileges to access them. An authenticated user such as a contributor could visit the ‘wp-admin/edit.php’ page in the WordPress backend to fetch the leaked security nonce in the source of the HTML page:

The user could then access the qppr_save_quick_redirect_ajax function and use it to create a redirect link that would forward all traffic to an external malicious website. Redirections are performed via the “Location” header:

$ curl https://example.org/ -I HTTP/1.1 301 Moved Permanently Date: Mon, 17 Feb 2020 15:44:05 GMT RedirectType: Quick Page Post Redirect - Quick X-Redirect-By: WordPress Location: https://evil.com/

Additional issues

In the same script, the qppr_delete_quick_redirect action, which is used to delete existing redirect links, is too prone to the same vulnerability.

Timeline

We discovered the vulnerability and reported it to the author, unsuccessfully, on February 17th, 2020. The plugin was removed from the wordpress.org repo on February 28th, 2020. Full disclosure on July 09th, 2020.

Recommendations

We recommend to uninstall this plugin as there isn’t any security patch available. 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.

Stay informed about the latest vulnerabilities

  • Running WordPress? You can get email notifications about vulnerabilities in the plugins or themes installed on your blog.
  • On Twitter: @nintechnet

CVE: Latest News

CVE-2023-50976: Transactions API Authorization by oleiman · Pull Request #14969 · redpanda-data/redpanda
CVE-2023-6905
CVE-2023-6903
CVE-2023-6904
CVE-2023-3907