CVE-2026-3891

Pix for WooCommerce <= 1.5.0 - Unauthenticated Arbitrary File Upload

criticalUnrestricted Upload of File with Dangerous Type
9.8
CVSS Score
9.8
CVSS Score
critical
Severity
1.6.0
Patched in
1d
Time to patch

Description

The Pix for WooCommerce plugin for WordPress is vulnerable to arbitrary file uploads due to missing capability check and missing file type validation in the 'lkn_pix_for_woocommerce_c6_save_settings' function in all versions up to, and including, 1.5.0. This makes it possible for unauthenticated attackers to upload arbitrary files on the affected site's server which may make remote code execution possible.

CVSS Vector Breakdown

CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H
Attack Vector
Network
Attack Complexity
Low
Privileges Required
None
User Interaction
None
Scope
Unchanged
High
Confidentiality
High
Integrity
High
Availability

Technical Details

Affected versions<=1.5.0
PublishedMarch 12, 2026
Last updatedMarch 13, 2026

What Changed in the Fix

Changes introduced in v1.6.0

Loading patch diff...

Source Code

WordPress.org SVN
Research Plan
Unverified

# Exploitation Research Plan: CVE-2026-3891 (Pix for WooCommerce Arbitrary File Upload) ## 1. Vulnerability Summary The **Pix for WooCommerce** plugin (up to 1.5.0) is vulnerable to an **Unauthenticated Arbitrary File Upload**. The vulnerability resides in the `lkn_pix_for_woocommerce_c6_save_setti…

Show full research plan

Exploitation Research Plan: CVE-2026-3891 (Pix for WooCommerce Arbitrary File Upload)

1. Vulnerability Summary

The Pix for WooCommerce plugin (up to 1.5.0) is vulnerable to an Unauthenticated Arbitrary File Upload. The vulnerability resides in the lkn_pix_for_woocommerce_c6_save_settings function (and potentially similar functions for Cielo and Rede gateways).

The plugin provides a "Nonce Oracle" endpoint (lkn_pix_for_woocommerce_generate_nonce) that allows any user (including unauthenticated ones) to generate a valid WordPress nonce for any action. This nonce can then be used to authenticate requests to the vulnerable settings-saving AJAX actions. These actions fail to perform capability checks (current_user_can) and do not validate file types or extensions, allowing for the upload of PHP shells.

2. Attack Vector Analysis

  • Endpoints: /wp-admin/admin-ajax.php
  • Actions:
    1. lkn_pix_for_woocommerce_generate_nonce (To obtain the required nonce)
    2. lkn_pix_for_woocommerce_c6_save_settings (The file upload sink)
  • Parameters:
    • action_name: The action string for which to generate a nonce.
    • _ajax_nonce: The generated nonce.
    • settings: JSON-encoded settings string.
    • File fields: The file to be uploaded (e.g., certificate).
  • Authentication: Unauthenticated (assumed based on the CVE description and the absence of checks in the provided generate_nonce method).
  • Preconditions: WooCommerce must be active (the plugin depends on it).

3. Code Flow

  1. Entry Point (Oracle): An attacker sends a request to admin-ajax.php with action=lkn_pix_for_woocommerce_generate_nonce.
  2. Nonce Generation: The method LknPaymentPixForWoocommerceAdmin::generate_nonce (line 140 in Admin/LknPaymentPixForWoocommerceAdmin.php) reads the action_name from the request and calls wp_create_nonce($action) without any permission check. It returns the nonce.
  3. Entry Point (Upload): The attacker sends a multipart POST request to admin-ajax.php with action=lkn_pix_for_woocommerce_c6_save_settings.
  4. Verification: The handler for this action (likely in the LknPaymentPixForWoocommercePixC6 class) verifies the nonce provided in _ajax_nonce. Because the attacker obtained a valid nonce from the Oracle, this check passes.
  5. Sink: The handler processes $_FILES. Based on the frontend JS (Admin/js/pixForWoocommercePaymentAdminSaveFields.js line 62), it iterates through fields and appends them to a FormData object. The backend likely saves these files using wp_handle_upload or move_uploaded_file without extension filtering.

4. Nonce Acquisition Strategy

This plugin provides an explicit Nonce Oracle which makes browser-based extraction or shortcode-page creation unnecessary.

  1. Action to generate: lkn_pix_for_woocommerce_c6_settings_nonce (derived from Admin/js/pixForWoocommercePaymentAdminSaveFields.js line 111).
  2. Request:
    POST /wp-admin/admin-ajax.php
    Content-Type: application/x-www-form-urlencoded
    
    action=lkn_pix_for_woocommerce_generate_nonce&action_name=lkn_pix_for_woocommerce_c6_settings_nonce
    
  3. Extraction: Parse the JSON response: response.data.nonce.

5. Exploitation Strategy

Step 1: Obtain the Nonce

Call the Oracle to get a valid nonce for the C6 settings save action.

Step 2: Upload the PHP Shell

Send a multipart/form-data request to the C6 save settings action.

  • Action: lkn_pix_for_woocommerce_c6_save_settings
  • Field Name: certificate (This is a common field for bank gateway integrations requiring certificates).
  • Settings JSON: {"certificate":"shell.php"}

Step 3: Locate the Shell

The file will typically be uploaded to the WordPress uploads directory.

  • wp-content/uploads/yyyy/mm/shell.php
  • Alternatively, check if the plugin creates a specific directory like wp-content/uploads/pix-for-woocommerce/.

6. Test Data Setup

  1. Ensure WooCommerce is installed and active.
  2. Ensure "Pix for WooCommerce" is installed and active (version <= 1.5.0).
  3. No specific settings are required because the AJAX handlers are exposed via wp_ajax_nopriv_ (as per the "Unauthenticated" severity).

7. Expected Results

  • The Nonce Oracle returns a 10-character hexadecimal string.
  • The upload request returns a success message (e.g., {"success":true,"data":{"message":"Settings saved successfully"}}).
  • A PHP file is created on the server and is executable.

8. Verification Steps

  1. Direct Execution: Access the uploaded file via the browser:
    http://localhost:8080/wp-content/uploads/2025/05/shell.php?cmd=id
  2. WP-CLI Check: Confirm the file exists on disk:
    find /var/www/html/wp-content/uploads -name "shell.php"
  3. Database Check: Check the WooCommerce option for the gateway to see the stored file path:
    wp option get woocommerce_lkn_pix_for_woocommerce_c6_settings --format=json

9. Alternative Approaches

If the c6 endpoint is restricted or the field name is different, try the other gateways supported by the plugin using their respective suffixes and field names:

  • Cielo:
    • Action: lkn_pix_for_woocommerce_cielo_pix_save_settings
    • Nonce Action: lkn_pix_for_woocommerce_cielo_pix_settings_nonce
  • Rede:
    • Action: lkn_pix_for_woocommerce_rede_pix_save_settings
    • Nonce Action: lkn_pix_for_woocommerce_rede_pix_settings_nonce

If certificate is not the correct field name, fuzz the multipart field name with common candidates: cert, key, pix_cert, client_cert. The JS code implies the field name is whatever follows the woocommerce_lkn_pix_for_woocommerce_c6_ prefix in the original form.

Research Findings
Static analysis — not yet PoC-verified

Summary

The Pix for WooCommerce plugin (versions <= 1.5.0) is vulnerable to unauthenticated arbitrary file uploads. This is achieved by combining a 'nonce oracle' endpoint that generates valid security tokens for any user with AJAX handlers that lack capability checks and file type validation, allowing attackers to upload PHP shells and execute remote code.

Vulnerable Code

// Admin/LknPaymentPixForWoocommerceAdmin.php:140
public function generate_nonce()
{
	if (empty($_REQUEST['action_name'])) {
		wp_send_json_error(['message' => 'Missing action_name parameter.'], 400);
	}

	$action = sanitize_text_field(wp_unslash($_REQUEST['action_name']));
	$nonce = wp_create_nonce($action);

	wp_send_json_success(['nonce' => $nonce, 'action' => $action]);
}

---

// Includes/LknPaymentPixForWoocommerce.php:183
$this->loader->add_action('wp_ajax_lkn_pix_for_woocommerce_c6_save_settings', $this->LknPaymentPixForWoocommercePixC6Class, 'lkn_pix_for_woocommerce_c6_save_settings');
$this->loader->add_action('wp_ajax_nopriv_lkn_pix_for_woocommerce_c6_save_settings', $this->LknPaymentPixForWoocommercePixC6Class, 'lkn_pix_for_woocommerce_c6_save_settings');
$this->loader->add_action('wp_ajax_lkn_pix_for_woocommerce_generate_nonce', $plugin_admin, 'generate_nonce');
$this->loader->add_action('wp_ajax_nopriv_lkn_pix_for_woocommerce_generate_nonce', $plugin_admin, 'generate_nonce');

Security Fix

diff -ru /home/deploy/wp-safety.org/data/plugin-versions/payment-gateway-pix-for-woocommerce/1.5.0/Admin/LknPaymentPixForWoocommerceAdmin.php /home/deploy/wp-safety.org/data/plugin-versions/payment-gateway-pix-for-woocommerce/1.6.0/Admin/LknPaymentPixForWoocommerceAdmin.php
--- /home/deploy/wp-safety.org/data/plugin-versions/payment-gateway-pix-for-woocommerce/1.5.0/Admin/LknPaymentPixForWoocommerceAdmin.php	2025-10-14 16:29:14.000000000 +0000
+++ /home/deploy/wp-safety.org/data/plugin-versions/payment-gateway-pix-for-woocommerce/1.6.0/Admin/LknPaymentPixForWoocommerceAdmin.php	2026-03-11 20:15:54.000000000 +0000
@@ -158,16 +158,4 @@
 			);
 		}
 	}
-
-	public function generate_nonce()
-	{
-		if (empty($_REQUEST['action_name'])) {
-			wp_send_json_error(['message' => 'Missing action_name parameter.'], 400);
-		}
-
-		$action = sanitize_text_field(wp_unslash($_REQUEST['action_name']));
-		$nonce = wp_create_nonce($action);
-
-		wp_send_json_success(['nonce' => $nonce, 'action' => $action]);
-	}
 }
diff -ru /home/deploy/wp-safety.org/data/plugin-versions/payment-gateway-pix-for-woocommerce/1.5.0/Includes/LknPaymentPixForWoocommerce.php /home/deploy/wp-safety.org/data/plugin-versions/payment-gateway-pix-for-woocommerce/1.6.0/Includes/LknPaymentPixForWoocommerce.php
--- /home/deploy/wp-safety.org/data/plugin-versions/payment-gateway-pix-for-woocommerce/1.5.0/Includes/LknPaymentPixForWoocommerce.php	2026-03-09 16:11:42.000000000 +0000
+++ /home/deploy/wp-safety.org/data/plugin-versions/payment-gateway-pix-for-woocommerce/1.6.0/Includes/LknPaymentPixForWoocommerce.php	2026-03-11 20:15:54.000000000 +0000
@@ -178,11 +184,10 @@
         $this->loader->add_action('woocommerce_update_options_payment_gateways_' . $this->LknPaymentPixForWoocommercePixC6Class->id, $this->LknPaymentPixForWoocommercePixC6Class, "process_admin_options");
         $this->loader->add_action('woocommerce_order_details_after_order_table', $this->LknPaymentPixForWoocommercePixC6Class, "showPix");
         $this->loader->add_action('wp_ajax_lkn_pix_for_woocommerce_c6_save_settings', $this->LknPaymentPixForWoocommercePixC6Class, 'lkn_pix_for_woocommerce_c6_save_settings');
-        $this->loader->add_action('wp_ajax_nopriv_lkn_pix_for_woocommerce_c6_save_settings', $this->LknPaymentPixForWoocommercePixC6Class, 'lkn_pix_for_woocommerce_c6_save_settings');
-        $this->loader->add_action('wp_ajax_lkn_pix_for_woocommerce_generate_nonce', $plugin_admin, 'generate_nonce');
-        $this->loader->add_action('wp_ajax_nopriv_lkn_pix_for_woocommerce_generate_nonce', $plugin_admin, 'generate_nonce');
+        // SECURITY: Removed wp_ajax_nopriv_ hooks to prevent unauthenticated access
+        // SECURITY: Removed insecure generate_nonce functionality - each gateway generates its own nonces
         $this->loader->add_action('wp_ajax_pixforwoo_test_c6_pix_charge', $this->LknPaymentPixForWoocommercePixC6Class, 'pixforwoo_test_c6_pix_charge');
-        $this->loader->add_action('wp_ajax_nopriv_pixforwoo_test_c6_pix_charge', $this->LknPaymentPixForWoocommercePixC6Class, 'pixforwoo_test_c6_pix_charge');
+        // SECURITY: Removed wp_ajax_nopriv_pixforwoo_test_c6_pix_charge - test integration requires authentication

Exploit Outline

The exploit methodology involves three primary steps. First, an unauthenticated attacker hits the `/wp-admin/admin-ajax.php` endpoint with the `action=lkn_pix_for_woocommerce_generate_nonce` and `action_name=lkn_pix_for_woocommerce_c6_settings_nonce` parameters to obtain a valid WordPress nonce. Second, the attacker sends a multipart POST request to the same AJAX endpoint with `action=lkn_pix_for_woocommerce_c6_save_settings`, providing the stolen nonce in the `_ajax_nonce` field. This request includes a PHP shell payload in a file field (e.g., `certificate`) and a corresponding JSON entry in the `settings` parameter. Third, because the plugin fails to check user capabilities or filter file extensions, the PHP file is saved to the WordPress uploads directory, allowing the attacker to navigate to the file and execute arbitrary commands.

Check if your site is affected.

Run a free security audit to detect vulnerable plugins, outdated versions, and misconfigurations.