WordPress PWA for WP and AMP plugin fixed vulnerabilities.

The WordPress PWA for WP & AMP plugin (20,000+ active installations) fixed a broken access control vulnerability affecting version 1.7.32 and below that could lead to arbitrary file upload among other issues.

Authenticated Arbitrary File Upload

In the plugin’s settings page, the administrator can upload a Splash Screen Icon:

The upload process is performed via the WordPress pwaforwp_splashscreen_uploader AJAX action in the “pwa-for-wp/admin/settings.php” script:

if(!function_exists('pwaforwp_splashscreen_uploader')){
   add_action('wp_ajax_pwaforwp_splashscreen_uploader','pwaforwp_splashscreen_uploader');

   function pwaforwp_splashscreen_uploader(){
      if ( ! isset( $_GET['pwaforwp_security_nonce'] ) ){
         echo json_encode(array("status"=>500, "message"=> "Splash screen uploaded successfully"));
            die;
         }
         if ( !wp_verify_nonce( $_GET['pwaforwp_security_nonce'], 'pwaforwp_ajax_check_nonce' ) ){
            echo json_encode(array("status"=>500, "message"=> "Splash screen uploaded successfully"));
            die;
         }
         $pwaforwp_settings = pwaforwp_defaultSettings();
            
         $upload = wp_upload_dir();
         $path = $upload['basedir']."/pwa-splash-screen/";
         wp_mkdir_p($path);
         $zipfilename = $path."file.zip";
         $input = fopen('php://input', 'rb');
         $file = fopen($zipfilename, 'wb'); 

         // Note: we don't need open and stream to stream,
         // we could've used file_get_contents and file_put_contents
         stream_copy_to_stream($input, $file);
         fclose($input);
         fclose($file);

         if(function_exists('WP_Filesystem')){ WP_Filesystem(); }
         unzip_file($zipfilename, $path);
         ...
         ...

The plugin will accept a ZIP archive and will extract its content into the “wp-content/uploads/pwa-splash-screen/” folder.
The function lacks a capability check to ensure that only the administrator can perform that action. Instead, it relies only on the pwaforwp_security_nonce security nonce, which is populated in the pwaforwp_enqueue_style_js function found in the same script:

/**
* Enqueue CSS and JS
*/
function pwaforwp_enqueue_style_js( $hook ) {
   // Load only on pwaforwp plugin pages
   if ( !is_admin() ) {
      return;
   }       
   
   wp_register_script('pwaforwp-all-page-js', PWAFORWP_PLUGIN_URL . 'assets/js/all-page-script.js', array( ), PWAFORWP_PLUGIN_VERSION, true);
          
   $object_name = array(
      'ajax_url'                  => admin_url( 'admin-ajax.php' ),
      'uploader_title'            => esc_html__('Application Icon', 'pwa-for-wp'),
      'splash_uploader_title'     => esc_html__('Splash Screen Icon', 'pwa-for-wp'),
      'uploader_button'           => esc_html__('Select Icon', 'pwa-for-wp'),
      'file_status'               => esc_html__('Check permission or download from manual', 'pwa-for-wp'),
      'pwaforwp_security_nonce'   => wp_create_nonce('pwaforwp_ajax_check_nonce'),   
      ...
      ...

That function is loaded by the admin_enqueue_scripts hook, i.e., each time someone accesses the backend:

add_action( 'admin_enqueue_scripts', 'pwaforwp_enqueue_style_js' );

The nonce is therefore accessible to any logged-in user, in the source of the HTML page:

An authenticated user such as a subscriber could upload one or more PHP scripts in a ZIP archive that would be extracted and accessible in the “wp-content/uploads/pwa-splash-screen/” folder, which could lead to remote code execution:

Authenticated Settings Change

In the “pwa-for-wp/admin/settings.php” script line 2633, the pwaforwp_update_features_options function is loaded by the AJAX hook.
The function too lacks a capability check and relies only on the same pwaforwp_security_nonce nonce.
An authenticated user could change the plugin’s settings.

Recommendations

Update immediately if you have version 1.7.32 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.

Note that if you have the premium version of NinjaFirewall you can proactively protect your blog against this type of vulnerabilities because the firewall has an option to scan ZIP archives and to reject them if they include a dangerous file. It can be found in the “NinjaFirewall > Firewall Policies > Uploads” section and I recommend to enable it:

Timeline

The vulnerability was reported to the authors on June 22, 2021, and a new version 1.7.33 was released on June 24, 2021.

Stay informed about the latest vulnerabilities