The Wordfence Threat Intelligence has responsibly disclosed a vulnerability in Access Demo Importer, a WordPress plugin installed on over 20,000 sites.

This flaw made it possible for authenticated attackers with just subscriber-level access to upload arbitrary files that could be used to achieve remote code execution. On sites with open registration, an anonymous user could easily register and exploit this vulnerability.

Access Demo Importer is a plugin designed to import the demo content for themes developed by AccessPress Themes. The importer functionality will import everything from content and photos to plugins required to optimize a site’s functionality.

One feature the plugin integrated was the ability to install plugins that are hosted outside of the WordPress.org repository during an import. Unfortunately, this functionality was insecurely implemented, making it possible for authenticated users to upload arbitrary files.

The plugin registers the wp_ajax_plugin_offline_installer AJAX action, which is tied to the plugin_offline_installer_callback function. This function takes the supplied file_location, which could be any external URL to a ZIP file, along with the other specifying parameters like slugclass_name, and file, and then retrieves the file’s contents and extracts the ZIP file to the plugins directory.

public function plugin_offline_installer_callback() {
    $plugin = array();
 
    $file_location = $plugin['location'] = isset( $_POST['file_location'] ) ? sanitize_text_field( wp_unslash( $_POST['file_location'] ) ) : '';
    $file           = isset( $_POST['file'] ) ? sanitize_text_field( wp_unslash( $_POST['file'] ) ) : '';
    $host_type      = isset( $_POST['host_type'] ) ? sanitize_text_field( wp_unslash( $_POST['host_type'] ) ) : '';
    $plugin_class   = $plugin['class'] = isset( $_POST['class_name'] ) ? sanitize_text_field( wp_unslash( $_POST['class_name'] ) ) : '';
    $plugin_slug    = $plugin['slug'] = isset( $_POST['slug'] ) ? sanitize_text_field( wp_unslash( $_POST['slug'] ) ) : '';
    $plugin_directory = WP_PLUGIN_DIR;
 
    $plugin_file = $plugin_slug . '/' . $file;
 
    if( $host_type == 'remote' ) {
        $file_location = $this->get_local_dir_path($plugin);
    }
 
    $zip = new ZipArchive();
    if ($zip->open($file_location) === TRUE) {
        $zip->extractTo($plugin_directory);
        $zip->close();
 
        activate_plugin($plugin_file);
 
        if( $host_type == 'remote' ) {
            unlink($file_location);
        }
 
        echo 'success';
 
        die();
    } else {
        echo 'failed';
    }
 
    die();
}

Unfortunately, this function had no capability check, nor any nonce checks, which made it possible for authenticated users with minimal permissions, like subscribers, to install a zip file as a “plugin” from an external source.

READ
HPE Reports Data Breach Following Russian State-Sponsored Cyberattack

This “plugin” zip file could contain malicious PHP files, including web shells, that could be used to achieve remote code execution once extracted and ultimately be used to completely take over a site.