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.
Below is a differential between the unpatched vulnerable code and the patched update, for reference.
--- 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+
Here you will find our ModSecurity compatible rule to protect against this particular CVE.
# 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"