CVE-2026-9197

Smart Slider 3 <= 3.5.1.36 - Authenticated (Administrator+) Path Traversal to Arbitrary File Read via 'src'/'srcset' Attribute in HTML Export

mediumImproper Limitation of a Pathname to a Restricted Directory ('Path Traversal')
4.9
CVSS Score
4.9
CVSS Score
medium
Severity
3.5.1.37
Patched in
1d
Time to patch

Description

The Smart Slider 3 plugin for WordPress is vulnerable to Directory Traversal in all versions up to, and including, 3.5.1.36 via the replaceHTMLImage function. This makes it possible for authenticated attackers, with administrator-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:H/UI:N/S:U/C:H/I:N/A:N
Attack Vector
Network
Attack Complexity
Low
Privileges Required
High
User Interaction
None
Scope
Unchanged
High
Confidentiality
None
Integrity
None
Availability

Technical Details

Affected versions<=3.5.1.36
PublishedJune 5, 2026
Last updatedJune 6, 2026
Affected pluginsmart-slider-3

What Changed in the Fix

Changes introduced in v3.5.1.37

Loading patch diff...

Source Code

WordPress.org SVN
Research Plan
Unverified

# Exploitation Research Plan - CVE-2026-9197 ## 1. Vulnerability Summary **CVE-2026-9197** is a high-severity Path Traversal vulnerability in the **Smart Slider 3** plugin for WordPress (versions <= 3.5.1.36). The vulnerability exists within the HTML Export functionality, specifically in the proces…

Show full research plan

Exploitation Research Plan - CVE-2026-9197

1. Vulnerability Summary

CVE-2026-9197 is a high-severity Path Traversal vulnerability in the Smart Slider 3 plugin for WordPress (versions <= 3.5.1.36). The vulnerability exists within the HTML Export functionality, specifically in the processing of src and srcset attributes. When a slider is exported as HTML, the plugin attempts to "localize" images referenced in the slides by reading them from the server's filesystem and bundling them into the export package (typically a ZIP file).

Because the plugin fails to properly validate that the paths extracted from these attributes are restricted to the allowed media directories, an authenticated Administrator can create a slide containing an image with a path traversal payload (e.g., ../../../../wp-config.php). During export, the plugin's replaceHTMLImage function (inferred sink) reads the contents of the target file and includes it in the resulting ZIP file, allowing for arbitrary file disclosure.

2. Attack Vector Analysis

  • Endpoint: wp-admin/admin.php or wp-admin/admin-ajax.php
  • Action: nextend_ajax with n2action set to a slider export controller (e.g., export or export-html).
  • Payload Location: The backgroundImage parameter or an image layer's src attribute within a slide's configuration data.
  • Authentication: Authenticated Administrator+ access is required.
  • Preconditions: A slider must exist or be created, and the attacker must be able to edit its slides to include the traversal payload.

3. Code Flow

  1. Entry Point: The administrator initiates an export of a slider (likely via the "Export" tab in the Smart Slider 3 dashboard).
  2. Controller: The request is handled by a controller inheriting from Nextend\Framework\Controller\Admin\AdminAjaxController.
  3. Export Logic: The controller instantiates Nextend\SmartSlider3\BackupSlider\ExportSlider.
  4. Slide Processing: The create() method in ExportSlider.php iterates through slides and their layers.
  5. Image Identification: The code identifies image URLs via the src or srcset attributes in the slide's HTML/JSON data.
  6. Path Resolution: The plugin calls ResourceTranslator::toPath($image) to convert the URL into a filesystem path.
  7. The Sink: The replaceHTMLImage function (as per CVE description) or the logic inside ExportSlider::create() (around line 147) calls file_get_contents($file) on the resolved path without sufficient validation against the WordPress root or images directory.
  8. Output: The content of the traversed file is written into the export ZIP package.

4. Nonce Acquisition Strategy

Smart Slider 3 uses a centralized AJAX handling system. The nonce is typically localized in the WordPress admin dashboard for the plugin.

  1. Navigate to Dashboard: Use browser_navigate to go to the Smart Slider 3 main page: wp-admin/admin.php?page=smart-slider-3.
  2. Extract Nonce: The nonce is stored in the window.nextend or window.n2 global JavaScript objects.
  3. JavaScript Execution:
    // Command for PoC agent
    browser_eval("window.n2?.ajaxNonce || window.nextend?.ajaxNonce")
    
  4. Fallback: If the above objects are not found, check for the script localization key:
    browser_eval("window._N2?.ajaxNonce")
    

5. Exploitation Strategy

Step 1: Create a Malicious Slider and Slide

The attacker first creates a slider and adds a slide containing the traversal payload.

  • URL: http://example.com/wp-admin/admin-ajax.php
  • Method: POST
  • Parameters:
    • action: nextend_ajax
    • n2action: SlideCreate
    • sliderId: [TARGET_SLIDER_ID]
    • nonce: [EXTRACTED_NONCE]
    • slideData: A JSON-encoded string representing the slide. The backgroundImage or a layer's image attribute must contain the payload: ../../../../../../wp-config.php.

Step 2: Trigger the HTML Export

Once the slide is saved, the attacker triggers the export process that processes the malicious path.

  • URL: http://example.com/wp-admin/admin-ajax.php
  • Method: POST
  • Parameters:
    • action: nextend_ajax
    • n2action: SliderExport (or ExportHTML if available)
    • sliderId: [TARGET_SLIDER_ID]
    • nonce: [EXTRACTED_NONCE]
    • format: html (to trigger the HTML image replacement logic)

Step 3: Retrieve the File

The response will be a ZIP file (or a link to one). Download the ZIP and examine the images/ directory.

  • Expected File Path inside ZIP: images/wp-config.php (The plugin typically flattens the directory structure into an images/ folder during export).

6. Test Data Setup

  1. Plugin State: Ensure Smart Slider 3 version 3.5.1.36 is installed and active.
  2. Slider: Create a simple slider via wp-cli or the UI to get a valid sliderId.
  3. Target File: Ensure wp-config.php exists in the standard WordPress root.
  4. Admin User: Perform all actions as an administrator.

7. Expected Results

  • The export process completes successfully.
  • The downloaded ZIP archive contains a file named wp-config.php (or a similar name depending on how basename() handles the traversed path).
  • The contents of the file match the server's wp-config.php, including database credentials and security salts.

8. Verification Steps

  1. Inspect ZIP: Use a CLI tool to list the contents of the generated export:
    unzip -l export_file.zip | grep wp-config
    
  2. Verify Content: Extract and read the file to confirm it is not just an empty file or error message:
    unzip -p export_file.zip images/wp-config.php | head -n 20
    

9. Alternative Approaches

  • Blind Read: If the ZIP generation is restricted or the extension check is enforced for .php, attempt to read files that mimic allowed extensions, such as .svg files that might contain sensitive data or .log files.
  • Direct Browse: If the export fails, test the ControllerAjaxBrowse::actionIndex endpoint identified in the source files. While restricted by strpos($path, $root) !== 0, certain configurations or symlink behaviors might allow directory listing via the path parameter:
    • action: nextend_ajax
    • n2action: BrowseIndex
    • path: ../../../../
Research Findings
Static analysis — not yet PoC-verified

Summary

The Smart Slider 3 plugin is vulnerable to authenticated path traversal via its HTML Export functionality. Administrators can embed path traversal sequences in image source attributes within a slider, causing the plugin to read arbitrary server files and bundle them into the generated export ZIP file.

Vulnerable Code

// Nextend/SmartSlider3/BackupSlider/ExportSlider.php line 381

    public function replaceHTMLImage($found) {
        $path = Filesystem::absoluteURLToPath(self::addProtocol($found[2]));

        if (substr($path, 0, 5) === "data:") {
            return $found[0];
        }

        if (strpos($path, Filesystem::getBasePath()) !== 0) {
            $imageUrl = Url::relativetoabsolute($path);
            $path     = Filesystem::absoluteURLToPath($imageUrl);
        }

        if ($path == $found[2]) {
            return $found[0];
        }
        if (Filesystem::fileexists($path)) {
            if (!isset($this->imageTranslation[$path])) {
                $fileName = strtolower(basename($path));
                while (in_array($fileName, $this->usedNames)) {
                    $fileName = $this->uniqueCounter . $fileName;
                    $this->uniqueCounter++;
                }
                $this->usedNames[]            = $fileName;
                $this->imageTranslation[$path] = $fileName;

                $this->files[] = array(
                    $path,
                    'images/' . $fileName
                );
            }

            return str_replace($found[2], 'images/' . $fileName, $found[0]);
        } else {
            return $found[0];
        }
    }

Security Fix

diff -ru /home/deploy/wp-safety.org/data/plugin-versions/smart-slider-3/3.5.1.34/Nextend/SmartSlider3/BackupSlider/ExportSlider.php /home/deploy/wp-safety.org/data/plugin-versions/smart-slider-3/3.5.1.37/Nextend/SmartSlider3/BackupSlider/ExportSlider.php
--- /home/deploy/wp-safety.org/data/plugin-versions/smart-slider-3/3.5.1.34/Nextend/SmartSlider3/BackupSlider/ExportSlider.php	2026-03-24 07:41:32.000000000 +0000
+++ /home/deploy/wp-safety.org/data/plugin-versions/smart-slider-3/3.5.1.37/Nextend/SmartSlider3/BackupSlider/ExportSlider.php	2026-05-28 12:10:14.000000000 +0000
@@ -379,21 +379,21 @@
     }
 
     public function replaceHTMLImage($found) {
-        $path = Filesystem::absoluteURLToPath(self::addProtocol($found[2]));
+        $resource = ResourceTranslator::urlToResource(self::addProtocol($found[2]));
 
-        if (substr($path, 0, 5) === "data:") {
+        if (substr($resource, 0, 5) === "data:") {
             return $found[0];
         }
 
-        if (strpos($path, Filesystem::getBasePath()) !== 0) {
-            $imageUrl = Url::relativetoabsolute($path);
-            $path     = Filesystem::absoluteURLToPath($imageUrl);
-        }
-
-        if ($path == $found[2]) {
+        if (!ResourceTranslator::isResource($resource) || preg_match('#(^|/)\.\.(/|$)#', $resource)) {
             return $found[0];
         }
-        if (Filesystem::fileexists($path)) {
+
+        $path = ResourceTranslator::toPath($resource);
+
+        if ($this->isAllowedExportFile($path)) {
+            $path = realpath($path);
+
             if (!isset($this->imageTranslation[$path])) {
                 $fileName = strtolower(basename($path));
                 while (in_array($fileName, $this->usedNames)) {
@@ -408,9 +408,38 @@
             }
 
             return str_replace($found[2], 'images/' . $fileName, $found[0]);
-        } else {
-            return $found[0];
         }
+
+        return $found[0];
+    }
+
+    private function isAllowedExportFile($path) {
+
+        $realPath = realpath($path);
+
+        if (!$realPath || !is_file($realPath)) {
+            return false;
+        }
+
+        $extension = strtolower(pathinfo($realPath, PATHINFO_EXTENSION));
+
+        $allowedExtensions = array(
+            'jpg',
+            'jpeg',
+            'png',
+            'gif',
+            'mp4',
+            'mp3',
+            'svg',
+            'webp',
+            'avif'
+        );
+
+        if (!in_array($extension, $allowedExtensions, true)) {
+            return false;
+        }
+
+        return true;
     }

Exploit Outline

1. Login as an Administrator and obtain the AJAX nonce from the dashboard (typically via the `window.n2` object). 2. Create or edit a slider using the `nextend_ajax` action and `SlideCreate` n2action. 3. In the slide configuration data, set an image attribute (like `backgroundImage`) to a path traversal payload pointing to a sensitive file (e.g., `../../../../wp-config.php`). 4. Trigger the slider export process by sending an AJAX request with `n2action` set to `SliderExport` (or equivalent HTML export action) and the `format` set to `html`. 5. Download the resulting ZIP package. The contents of the targeted file (e.g., database credentials from `wp-config.php`) will be stored within the `images/` directory inside the archive.

Check if your site is affected.

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