CVE-2026-6670

Media Sync <= 1.4.9 - Authenticated (Author+) Path Traversal via 'sub_dir' and 'media_items' Parameters

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

Description

The Media Sync plugin for WordPress is vulnerable to Path Traversal in all versions up to, and including, 1.4.9 via the 'sub_dir' and 'media_items' parameters. This is due to insufficient validation of user-supplied file paths, which are not checked for directory traversal sequences or restricted to the intended uploads directory. This makes it possible for authenticated attackers, with Author-level access and above, to perform actions on files outside of the originally intended directory.

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<=1.4.9
PublishedMay 13, 2026
Last updatedMay 14, 2026
Affected pluginmedia-sync

What Changed in the Fix

Changes introduced in v1.5.0

Loading patch diff...

Source Code

WordPress.org SVN
Research Plan
Unverified

I cannot fulfill this request. I am prohibited from generating actionable exploitation plans, specific payloads, or step-by-step guides for exploiting vulnerabilities in software. If you are interested in learning about securing WordPress plugins against vulnerabilities like Path Traversal, I recom…

Show full research plan

I cannot fulfill this request. I am prohibited from generating actionable exploitation plans, specific payloads, or step-by-step guides for exploiting vulnerabilities in software.

If you are interested in learning about securing WordPress plugins against vulnerabilities like Path Traversal, I recommend exploring the following defensive resources:

  • WordPress Plugin Handbook (Security Section): This provides official guidelines on data validation, sanitization, and filesystem security.
  • OWASP Path Traversal Guide: Offers a comprehensive overview of how these vulnerabilities occur and how to prevent them using allow-lists and proper path resolution.
  • PHP Security Documentation: Reviews safe practices for handling file paths, such as using realpath() and checking if paths remain within an intended directory.

For details on the fix for this specific issue, you can review the changes made in version 1.5.0 of the Media Sync plugin on the WordPress.org plugin repository.

Research Findings
Static analysis — not yet PoC-verified

Summary

The Media Sync plugin for WordPress is vulnerable to path traversal because it fails to validate the 'sub_dir' and 'media_items' parameters before using them in file operations. Authenticated attackers with Author-level access or higher can exploit this to scan and potentially import files from outside the intended uploads directory into the WordPress Media Library.

Vulnerable Code

// includes/MediaSync.class.php line 624
// In the context of importing files via AJAX
$absolute_path = urldecode($media_item['file']);
$relative_path = self::media_sync_url_encode(self::media_sync_get_relative_path($absolute_path));

---

// includes/MediaSync.class.php line 1027
// In the context of scanning for files to list
$sub_dir = self::sanitize_input_string(INPUT_GET, 'sub_dir');
if ($sub_dir) {
    // Since this path is always using forward slashes, we're also using forward slash
    self::$upload_dir_path = self::$upload_dir_path . '/' . $sub_dir;
}

Security Fix

diff -ru /home/deploy/wp-safety.org/data/plugin-versions/media-sync/1.4.9/includes/MediaSync.class.php /home/deploy/wp-safety.org/data/plugin-versions/media-sync/1.5.0/includes/MediaSync.class.php
--- /home/deploy/wp-safety.org/data/plugin-versions/media-sync/1.4.9/includes/MediaSync.class.php	2025-11-25 08:11:02.000000000 +0000
+++ /home/deploy/wp-safety.org/data/plugin-versions/media-sync/1.5.0/includes/MediaSync.class.php	2026-04-20 21:42:38.000000000 +0000
@@ -624,6 +624,18 @@
 
                         // This comes from JS and it's taken from checkbox value, which is $item['absolute_path'] from media_sync_get_list_of_files()
                         $absolute_path = urldecode($media_item['file']);
+
+                        $validated_path = self::media_sync_validate_path($absolute_path);
+                        if ($validated_path === false) {
+                            $results[] = array(
+                                'row_id' => $media_item['row_id'],
+                                'inserted' => false,
+                                'errorMessage' => __('Invalid file path.', 'media-sync')
+                            );
+                            continue;
+                        }
+                        $absolute_path = $validated_path;
+
                         $relative_path = self::media_sync_url_encode(self::media_sync_get_relative_path($absolute_path));
 
                         // It's quicker to get all files already in db and check that array, than to do this query for each file
@@ -990,6 +1002,46 @@
 
 
         /**
+         * Validate that a path resolves within the uploads directory.
+         *
+         * @since 1.5.0
+         * @param string $path Absolute path to validate
+         * @return string|false Resolved canonical path, or false if invalid
+         */
+        static private function media_sync_validate_path($path)
+        {
+            // Reject stream wrappers e.g. ftp://, php://
+            if (strpos($path, '://') !== false) {
+                return false;
+            }
+
+            // Reject path traversal sequences
+            if (strpos($path, '..') !== false) {
+                return false;
+            }
+
+            // Resolve canonical uploads base path
+            $uploads_basedir = realpath(self::media_sync_get_uploads_basedir());
+            if ($uploads_basedir === false) {
+                return false;
+            }
+
+            // Resolve canonical path (also confirms the path exists on disk)
+            $resolved = realpath($path);
+            if ($resolved === false) {
+                return false;
+            }
+
+            // Confirm resolved path is within uploads
+            if (strpos($resolved, $uploads_basedir) !== 0) {
+                return false;
+            }
+
+            return $resolved;
+        }
+
+
+        /**
          * Get path absolute to WP root. Always using forward slashes.
          *
          * e.g. /var/www/WP/wp-content/uploads -> /wp-content/uploads
@@ -1027,8 +1079,11 @@
             // Limit scanning to specific sub folder or encoded path (e.g. &sub_dir=2020%2F01)
             $sub_dir = self::sanitize_input_string(INPUT_GET, 'sub_dir');
             if ($sub_dir) {
-                // Since this path is always using forward slashes, we're also using forward slash
-                self::$upload_dir_path = self::$upload_dir_path . '/' . $sub_dir;
+                $sub_dir_validated = self::media_sync_validate_path( self::$upload_dir_path . '/' . $sub_dir );
+                if ( $sub_dir_validated === false ) {
+                    return array();
+                }
+                self::$upload_dir_path = $sub_dir_validated;
             }
 
             if(empty(self::$files_in_db)) {

Exploit Outline

The exploit targets the plugin's file scanning and import functionality. An attacker with Author-level permissions can interact with the plugin through two primary vectors: 1. Path Traversal via 'sub_dir': By providing a crafted GET request to the 'media-sync-page' with a 'sub_dir' parameter containing traversal sequences (e.g., `../../../../`), the attacker can force the plugin to scan and list files in arbitrary server directories (such as `/etc/` or the WordPress root). 2. Path Traversal via AJAX Import: The 'media_sync_import_files' AJAX action processes a 'media_items' array. Each item has a 'file' parameter that contains an absolute path. An attacker can intercept this AJAX call and replace the intended file path with a path to a sensitive file elsewhere on the system (e.g., `/var/www/html/wp-config.php`). The plugin will then attempt to process and 'import' this file into the Media Library, potentially exposing its contents or location.

Check if your site is affected.

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