Atomic Edge Proof of Concept automated generator using AI diff analysis
Published : May 13, 2026

CVE-2026-6670: Media Sync <= 1.4.9 – Authenticated (Author+) Path Traversal via 'sub_dir' and 'media_items' Parameters (media-sync)

CVE ID CVE-2026-6670
Plugin media-sync
Severity Medium (CVSS 6.5)
CWE 22
Vulnerable Version 1.4.9
Patched Version 1.5.0
Disclosed May 12, 2026

Analysis Overview

Atomic Edge analysis of CVE-2026-6670:

This vulnerability is a Path Traversal issue in the Media Sync WordPress plugin, versions up to and including 1.4.9. Attackers with Author-level access or higher can leverage insufficient path validation in the ‘sub_dir’ and ‘media_items’ parameters to read or manipulate files outside the intended uploads directory. The CVSS score is 6.5.

The root cause lies in two code paths within the ‘MediaSync.class.php’ file. First, around line 1031, the ‘sub_dir’ parameter from GET requests is concatenated directly to the uploads directory path without validation: ‘self::$upload_dir_path . ‘/’ . $sub_dir’. This allows an attacker to inject directory traversal sequences like ‘../’ to escape the base directory. Second, around line 627, the ‘media_items’ parameter, which contains an ‘absolute_path’ derived from checkbox values, is used directly after URL decoding without any path validation. The insufficient validation allows arbitrary file paths to be processed by the plugin’s sync functionality.

To exploit this, an authenticated attacker with Author privileges sends a crafted GET or POST request to the vulnerable endpoints. For the ‘sub_dir’ parameter, the attacker calls the AJAX action or page handler that invokes the file listing function with a ‘sub_dir’ value like ‘../wp-config.php’. For the ‘media_items’ parameter, the attacker posts a JSON array with an ‘absolute_path’ set to an arbitrary server file, such as ‘/etc/passwd’ or a sensitive WordPress configuration file. The plugin then interacts with these files, potentially exposing or duplicating them into the media library.

The patch introduces the ‘media_sync_validate_path’ function (lines 1002-1046) which is applied to both vulnerable parameters. This function validates paths by rejecting stream wrappers (e.g., ‘ftp://’, ‘php://’), rejecting traversal sequences (‘..’), and then resolving the canonical path via ‘realpath()’. It confirms the resolved path exists on disk and that it starts with the canonical uploads base directory. Before the patch, paths were used directly. After the patch, any path that does not strictly reside within the uploads directory is rejected with an ‘Invalid file path’ error.

Successful exploitation allows an authenticated attacker to read arbitrary files from the server. This can lead to disclosure of sensitive information such as database credentials from ‘wp-config.php’, other plugin configuration files, or system files like ‘/etc/passwd’. Depending on what the plugin does with the file path, there may also be potential to write or duplicate files outside the uploads directory, though the primary impact from the diff appears to be unauthorized file access within the scope of the sync operation.

Differential between vulnerable and patched code

Below is a differential between the unpatched vulnerable code and the patched update, for reference.

Code Diff
--- a/media-sync/includes/MediaSync.class.php
+++ b/media-sync/includes/MediaSync.class.php
@@ -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)) {
--- a/media-sync/media-sync.php
+++ b/media-sync/media-sync.php
@@ -4,7 +4,7 @@
  * Plugin Name: Media Sync
  * Plugin URI: https://wordpress.org/plugins/media-sync/
  * Description: Simple plugin to scan uploads directory and bring files to Media Library.
- * Version: 1.4.9
+ * Version: 1.5.0
  * Author: Media Sync Team
  * Author URI: https://mediasyncplugin.com/?utm_source=wordpress_dashboard&utm_medium=plugins_page&utm_campaign=pdal
  * License: GPLv2+

ModSecurity Protection Against This CVE

Here you will find our ModSecurity compatible rule to protect against this particular CVE.

ModSecurity
# Atomic Edge WAF Rule - CVE-2026-6670
SecRule REQUEST_URI "@rx /wp-admin/admin-ajax.php$" 
  "id:20261994,phase:2,deny,status:403,chain,msg:'CVE-2026-6670 - Media Sync Path Traversal via sub_dir',severity:'CRITICAL',tag:'CVE-2026-6670'"
  SecRule ARGS_GET:sub_dir "@rx ../" "chain"
    SecRule ARGS_GET:action "@rx media_sync_get_list_of_files|media_sync_sync" "t:none"

Frequently Asked Questions

How Atomic Edge Works

Simple Setup. Powerful Security.

Atomic Edge acts as a security layer between your website & the internet. Our AI inspection and analysis engine auto blocks threats before traditional firewall services can inspect, research and build archaic regex filters.

Get Started

Trusted by Developers & Organizations

Trusted by Developers
Blac&kMcDonaldCovenant House TorontoAlzheimer Society CanadaUniversity of TorontoHarvard Medical School