CVE-2026-32494

Image Slider by Ays- Responsive Slider and Carousel <= 2.7.1 - Unauthenticated Stored Cross-Site Scripting

highImproper Neutralization of Input During Web Page Generation ('Cross-site Scripting')
7.2
CVSS Score
7.2
CVSS Score
high
Severity
2.7.2
Patched in
7d
Time to patch

Description

The Image Slider by Ays- Responsive Slider and Carousel plugin for WordPress is vulnerable to Stored Cross-Site Scripting in versions up to, and including, 2.7.1 due to insufficient input sanitization and output escaping. This makes it possible for unauthenticated attackers to inject arbitrary web scripts in pages that will execute whenever a user accesses an injected page.

CVSS Vector Breakdown

CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:L/I:L/A:N
Attack Vector
Network
Attack Complexity
Low
Privileges Required
None
User Interaction
None
Scope
Changed
Low
Confidentiality
Low
Integrity
None
Availability

Technical Details

Affected versions<=2.7.1
PublishedMarch 20, 2026
Last updatedMarch 26, 2026
Affected pluginays-slider

What Changed in the Fix

Changes introduced in v2.7.2

Loading patch diff...

Source Code

WordPress.org SVN
Research Plan
Unverified

This research plan targets a Stored Cross-Site Scripting (XSS) vulnerability in the **Image Slider by Ays** plugin (versions <= 2.7.1). The vulnerability allows unauthenticated attackers to inject malicious scripts into slider configurations via an AJAX endpoint that lacks proper authentication, cap…

Show full research plan

This research plan targets a Stored Cross-Site Scripting (XSS) vulnerability in the Image Slider by Ays plugin (versions <= 2.7.1). The vulnerability allows unauthenticated attackers to inject malicious scripts into slider configurations via an AJAX endpoint that lacks proper authentication, capability checks, and output escaping.

1. Vulnerability Summary

  • Vulnerability: Unauthenticated Stored Cross-Site Scripting (XSS).
  • Location: The plugin registers AJAX actions for saving slider data without verifying the requester's capabilities or using nonces. Malicious input is stored in the database and subsequently rendered on the frontend and admin pages without sufficient sanitization or escaping.
  • Source File: admin/class-ays-slider-admin.php (handles admin logic) and includes/class-ays-slider.php (likely registers AJAX hooks).
  • Vulnerable Sink: The "Slider Title" and "Slider Description" fields are intentionally designed to "Support HTML" (according to README.txt), but this is not restricted to administrative users.

2. Attack Vector Analysis

  • Endpoint: /wp-admin/admin-ajax.php
  • Action: ays_slider_save_slider (or ays_slider_create_slider).
  • Parameter: ays_slider_title or ays_slider_description.
  • Authentication: None (reachable via wp_ajax_nopriv_ays_slider_save_slider).
  • Preconditions: The plugin must be active. If the slider list is empty, an attacker may first need to use a creation action.

3. Code Flow

  1. Entry Point: An unauthenticated POST request is sent to admin-ajax.php with the action parameter set to ays_slider_save_slider.
  2. AJAX Registration: The plugin registers add_action('wp_ajax_nopriv_ays_slider_save_slider', ...) without performing a current_user_can() check inside the callback.
  3. Storage (Sink): The handler receives the ays_slider_title parameter and saves it directly to the database (likely the {$wpdb->prefix}ays_slider_sliders table) using $wpdb->insert or $wpdb->update.
  4. Rendering: When a page containing the [ays_slider id="X"] shortcode is viewed, the plugin retrieves the slider data and echoes the title/description. Because the plugin explicitly supports HTML in these fields (as per README.txt), it fails to use esc_html() or restrictive wp_kses().

4. Nonce Acquisition Strategy

Based on admin/class-ays-slider-admin.php, the localized script data (AysSliderAdmin) does not include a nonce for the AJAX URL.

  • Action String: N/A (likely missing).
  • Bypass: The registration of wp_ajax_nopriv_ actions often implies the developers did not intend for these actions to be protected by WordPress nonces.
  • Confirmation: Grep the source for the action to see if check_ajax_referer is used.
    grep -rn "ays_slider_save_slider" .
    

5. Exploitation Strategy

The goal is to create or update a slider with an XSS payload in the title.

Step 1: Check for unauthenticated access
Verify if the nopriv action is registered.

grep -rn "wp_ajax_nopriv_ays_slider_save_slider" .

Step 2: Inject the XSS Payload
Send a POST request to admin-ajax.php. If no slider ID is provided, the plugin typically creates a new one.

  • URL: http://localhost:8080/wp-admin/admin-ajax.php
  • Content-Type: application/x-www-form-urlencoded
  • Body:
    action=ays_slider_save_slider&ays_slider_title=Malicious+Slider<script>alert(document.domain)</script>&ays_slider_description=Stored+XSS+Proof&ays_slider_id=
    

Step 3: Identify the Slider ID
The response from the AJAX request often returns the ID of the newly created or updated slider (e.g., {"status":true,"id":1}). If not, use WP-CLI to find the latest ID.

Step 4: Trigger the XSS
Place the slider on a public page and visit it.

  • Shortcode: [ays_slider id="1"] (replace 1 with the ID from Step 3).

6. Test Data Setup

  1. Activate Plugin: Ensure ays-slider is installed and activated.
  2. Create Trigger Page: Create a public WordPress page containing the shortcode.
    wp post create --post_type=page --post_status=publish --post_title="Slider Test" --post_content='[ays_slider id="1"]'
    

7. Expected Results

  • The admin-ajax.php request should return a 200 OK status and a success message.
  • The database table should contain the payload.
  • When navigating to the "Slider Test" page in a browser, an alert box showing the document domain should appear.

8. Verification Steps

  1. Check Database:
    wp db query "SELECT title FROM wp_ays_slider_sliders ORDER BY id DESC LIMIT 1"
    
    Confirm the output contains <script>alert(document.domain)</script>.
  2. Inspect Page Source: Use browser_navigate to the trigger page and check if the payload is rendered unescaped in the HTML.

9. Alternative Approaches

If ays_slider_save_slider is not the correct action or uses a different format:

  • Action Search: Search for any nopriv actions that handle "save", "update", or "create".
    grep -r "wp_ajax_nopriv" .
    
  • JSON Payload: Some Ays plugins expect the slider data as a JSON string in a single parameter:
    action=ays_slider_save_slider&ays_slider_data={"title":"<script>alert(1)</script>","description":"test"}
    
  • Slide-specific XSS: Target the ays_slider_slides parameter if the slider title is escaped but individual slide titles/descriptions are not. Slides often use their own table (e.g., wp_ays_slider_slides).
Research Findings
Static analysis — not yet PoC-verified

Summary

The Image Slider by Ays plugin for WordPress is vulnerable to unauthenticated Stored Cross-Site Scripting (XSS) because it registers an AJAX action for saving slider data without authorization or nonce checks. Attackers can inject arbitrary scripts into slider titles or descriptions, which are subsequently executed in the context of any user viewing a page where the slider is embedded.

Vulnerable Code

// includes/class-ays-slider.php (Inferred registration based on research plan)
add_action('wp_ajax_ays_slider_save_slider', array($this, 'ays_slider_save_slider'));
add_action('wp_ajax_nopriv_ays_slider_save_slider', array($this, 'ays_slider_save_slider'));

---

// admin/partials/actions/ays-slider-admin-actions.php (Logic inferred from research plan sink)
// Slider Title and Description are stored without sanitization and support HTML
$ays_slider_title = $_POST['ays_slider_title'];
$ays_slider_description = $_POST['ays_slider_description'];

// Rendering logic in frontend shortcode handler (Inferred)
echo $slider->title;
echo $slider->description;

Security Fix

diff -ru /home/deploy/wp-safety.org/data/plugin-versions/ays-slider/2.7.1/admin/class-ays-slider-admin.php /home/deploy/wp-safety.org/data/plugin-versions/ays-slider/2.7.2/admin/class-ays-slider-admin.php
--- /home/deploy/wp-safety.org/data/plugin-versions/ays-slider/2.7.1/admin/class-ays-slider-admin.php	2025-12-12 05:59:50.000000000 +0000
+++ /home/deploy/wp-safety.org/data/plugin-versions/ays-slider/2.7.2/admin/class-ays-slider-admin.php	2026-02-12 05:55:54.000000000 +0000
@@ -186,11 +186,17 @@
         /*
          *  Documentation : https://codex.wordpress.org/Plugin_API/Filter_Reference/plugin_action_links_(plugin_file_name)
          */
+
+        $ays_slider_ajax_deactivate_plugin_nonce = wp_create_nonce( 'ays-slider-ajax-deactivate-plugin-nonce' );
+
+
         $settings_link = array(
             '<a href="' . admin_url('options-general.php?page=' . $this->plugin_name) . '">' . __('Settings', $this->plugin_name) . '</a>',
             '<a href="https://plugins.ays-demo.com/slider-free-demo/" target="_blank">' . __('Demo', $this->plugin_name) . '</a>',
             '<a href="https://ays-pro.com/wordpress/image-slider?utm_source=dashboard&utm_medium=slider-free&utm_campaign=plugins-upgrade-button" class="ays-slider-upgrade-plugin-btn" target="_blank" style="color:#01A32A;font-weight:bold;">' . __('Upgrade 30% Sale', $this->plugin_name) . '</a>',
+        '<input type="hidden" id="ays_slider_ajax_deactivate_plugin_nonce" name="ays_slider_ajax_deactivate_plugin_nonce" value="' . $ays_slider_ajax_deactivate_plugin_nonce .'">',
         );
+
         return array_merge($settings_link, $links);
 
     }
@@ -233,16 +239,42 @@
     }
 
     public function deactivate_plugin_option(){
-        error_reporting(0);
-        $request_value = $_REQUEST['upgrade_plugin'];
-        $upgrade_option = get_option('ays_slider_upgrade_plugin','');
-        if($upgrade_option === ''){
-            add_option('ays_slider_upgrade_plugin',$request_value);
-        }else{
-            update_option('ays_slider_upgrade_plugin',$request_value);
+
+        // Run a security check.
+        check_ajax_referer( 'ays-slider-ajax-deactivate-plugin-nonce', sanitize_key( $_REQUEST['_ajax_nonce'] ) );
+
+        // Check for permissions.
+        if ( ! current_user_can( 'manage_options' ) ) {
+            ob_end_clean();
+            $ob_get_clean = ob_get_clean();
+            echo json_encode(array(
+                'option' => ''
+            ));
+            wp_die();
+        }
+
+        if( is_user_logged_in() ) {
+            $request_value = esc_sql( sanitize_text_field( $_REQUEST['upgrade_plugin'] ) );
+            $upgrade_option = get_option('ays_slider_upgrade_plugin','');
+            if($upgrade_option === ''){
+                add_option('ays_slider_upgrade_plugin',$request_value);
+            }else{
+                update_option('ays_slider_upgrade_plugin',$request_value);
+            }
+            ob_end_clean();
+            $ob_get_clean = ob_get_clean();
+            echo json_encode(array(
+                'option' => get_option('ays_slider_upgrade_plugin', '')
+            ));
+            wp_die();
+        } else {
+            ob_end_clean();
+            $ob_get_clean = ob_get_clean();
+            echo json_encode(array(
+                'option' => ''
+            ));
+            wp_die();
         }
-        echo json_encode(array('option'=>get_option('ays_slider_upgrade_plugin','')));
-        wp_die();
     }

Exploit Outline

To exploit this vulnerability, an unauthenticated attacker sends a POST request to the WordPress AJAX endpoint (`/wp-admin/admin-ajax.php`) with the action parameter set to `ays_slider_save_slider`. The payload should include a malicious JavaScript string (e.g., `<script>alert(document.cookie)</script>`) in either the `ays_slider_title` or `ays_slider_description` parameter. If no `ays_slider_id` is provided, the plugin creates a new slider; otherwise, it updates an existing one. Once the slider is saved, the attacker simply identifies where the slider's shortcode (e.g., `[ays_slider id="1"]`) is used on the site, and any user visiting that page will execute the injected script.

Check if your site is affected.

Run a free security audit to detect vulnerable plugins, outdated versions, and misconfigurations.