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

CVE-2024-13362: Freemius <= 2.10.1 – Reflected DOM-Based Cross-Site Scripting via url Parameter (wpide)

Plugin wpide
Severity Medium (CVSS 6.1)
CWE 79
Vulnerable Version 3.5.1
Patched Version 3.5.2
Disclosed April 29, 2026

Analysis Overview

Atomic Edge analysis of CVE-2024-13362:

This vulnerability is a reflected DOM-based cross-site scripting flaw in the Freemius SDK, which is used by many WordPress plugins and themes. The vulnerability affects Freemius versions up to and including 2.10.1. An unauthenticated attacker can inject arbitrary JavaScript into the Freemius trial promotion notice by manipulating the ‘url’ parameter. This occurs because the SDK does not properly sanitize or escape user-supplied input before embedding it into the page’s DOM. The CVSS score is 6.1 (Medium).

The root cause lies in the Freemius class-freemius.php file, specifically in the trial promotion message construction. The vulnerable code concatenates user-controlled data (the ‘url’ parameter) directly into an HTML anchor element. In the diff, the original code (line 24000) uses sprintf with ‘%s’ to place the trial URL directly into the href attribute of an ‘‘ tag. The patched code restructures the message to separate the button from the promotional text string, and applies the ‘trial_promotion_message’ filter to the text but not the button HTML. However, the vulnerability is in how the filter output is used; the original code allowed unescaped data from the filter to be placed into the DOM. The patch does not directly sanitize the ‘url’ parameter itself; instead, it moves the button outside the filtered message, reducing the attack surface. The actual fix for XSS in the URL parameter appears to be a separate change not fully shown here, but the diff indicates structural changes to the message composition.

An attacker can exploit this by crafting a URL that passes a malicious payload in the ‘url’ parameter to a page that loads the Freemius trial promotion. For example, a link like: ‘http://target.com/wp-admin/admin.php?page=freemius-trial&url=javascript:alert(document.cookie)’ could trigger the XSS when the admin visits the page. The unescaped URL is placed into the href attribute, allowing ‘javascript:’ or event handler injection. The attacker must trick a user (typically an admin) into clicking the crafted link. Since the Freemius SDK runs on admin-facing pages, the payload executes in the admin context, potentially compromising the entire site.

The patch modifies how the trial promotion message is built. The old code combined the message text, credit card string, and the trial URL button into a single string passed to the ‘trial_promotion_message’ filter. The new code separates these: it first builds the message text (without the button), applies the filter to only the text, then appends the button HTML separately. This change means that even if the filter is abused, the attacker cannot inject into the button’s URL. However, the XSS in the ‘url’ parameter itself is not directly addressed in this diff; the patch focuses on restructuring the message flow. The true fix likely requires escaping the URL with esc_url() or validating it against a whitelist. The diff does show other changes (e.g., adding __wakeup to FS_User, changing count check to empty, modifying admin menu URL logic), but the core XSS fix is not fully demonstrated here.

Successful exploitation allows an attacker to execute arbitrary JavaScript in the browser of an authenticated administrator. This can lead to session hijacking, credential theft, creation of rogue admin accounts, or injection of backdoors into the WordPress site. Since the attack requires user interaction (clicking a link), it is less severe than an unauthenticated remote code execution, but the impact on a compromised admin session is complete site takeover. The attacker can also modify plugin settings, exfiltrate the database, or install malicious plugins. Given Freemius’s widespread use in commercial plugins, this vulnerability affects a large number of WordPress sites.

Differential between vulnerable and patched code

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

Code Diff
--- a/wpide/App/AppConfig.php
+++ b/wpide/App/AppConfig.php
@@ -11,7 +11,16 @@

     public static function load(): Config
     {
-        self::$config = empty(self::$config) ? new Config(require DIR.'_configuration.php') : self::$config;
+        if (empty(self::$config)) {
+            // Only load config after WordPress init
+            if (!did_action('init')) {
+                add_action('init', fn() => self::$config = new Config(require DIR.'_configuration.php'));
+                // Return empty config temporarily
+                self::$config = new Config([]);
+            } else {
+                self::$config = new Config(require DIR.'_configuration.php');
+            }
+        }
         return self::$config;
     }

--- a/wpide/App/Classes/Freemius.php
+++ b/wpide/App/Classes/Freemius.php
@@ -63,9 +63,7 @@
             self::$fs->add_filter( 'hide_account_tabs', '__return_true' );
             self::$fs->add_filter( 'hide_freemius_powered_by', '__return_true' );
             self::$fs->add_filter( 'hide_billing_and_payments_info', '__return_true' );
-            self::$fs->override_i18n( array(
-                'contact-us' => __( 'Support', 'wpide' ),
-            ) );
+            self::$fs->add_action( 'plugins_loaded', [__CLASS__, 'override_freemius_strings'] );
             add_action( 'admin_enqueue_scripts', [__CLASS__, 'admin_enqueue_scripts'] );
             self::$loaded = true;
             // Signal that SDK was initiated.
@@ -212,4 +210,10 @@
         return self::$loaded;
     }

+    public static function override_freemius_strings() : void {
+        self::$fs->override_i18n( array(
+            'contact-us' => __( 'Support', 'wpide' ),
+        ) );
+    }
+
 }
--- a/wpide/freemius/includes/class-freemius.php
+++ b/wpide/freemius/includes/class-freemius.php
@@ -24000,13 +24000,15 @@

             // Start trial button.
             $button = ' ' . sprintf(
-                    '<a style="margin-left: 10px; vertical-align: super;" href="%s"><button class="button button-primary">%s  ➜</button></a>',
+                    '<div><a class="button button-primary" href="%s">%s  ➜</a></div>',
                     $trial_url,
                     $this->get_text_x_inline( 'Start free trial', 'call to action', 'start-free-trial' )
                 );

+            $message_text = $this->apply_filters( 'trial_promotion_message', "{$message} {$cc_string}" );
+
             $this->_admin_notices->add_sticky(
-                $this->apply_filters( 'trial_promotion_message', "{$message} {$cc_string} {$button}" ),
+                "<div class="fs-trial-message-container"><div>{$message_text}</div> {$button}</div>",
                 'trial_promotion',
                 '',
                 'promotion'
@@ -25476,7 +25478,7 @@
                 $img_dir = WP_FS__DIR_IMG;

                 // Locate the main assets folder.
-                if ( 1 < count( $fs_active_plugins->plugins ) ) {
+                if ( ! empty( $fs_active_plugins->plugins ) ) {
                     $plugin_or_theme_img_dir = ( $this->is_plugin() ? WP_PLUGIN_DIR : get_theme_root( get_stylesheet() ) );

                     foreach ( $fs_active_plugins->plugins as $sdk_path => &$data ) {
--- a/wpide/freemius/includes/class-fs-plugin-updater.php
+++ b/wpide/freemius/includes/class-fs-plugin-updater.php
@@ -542,24 +542,8 @@

             global $wp_current_filter;

-            $current_plugin_version = $this->_fs->get_plugin_version();
-
-            if ( ! empty( $wp_current_filter ) && 'upgrader_process_complete' === $wp_current_filter[0] ) {
-                if (
-                    is_null( $this->_update_details ) ||
-                    ( is_object( $this->_update_details ) && $this->_update_details->new_version !== $current_plugin_version )
-                ) {
-                    /**
-                     * After an update, clear the stored update details and reparse the plugin's main file in order to get
-                     * the updated version's information and prevent the previous update information from showing up on the
-                     * updates page.
-                     *
-                     * @author Leo Fajardo (@leorw)
-                     * @since 2.3.1
-                     */
-                    $this->_update_details  = null;
-                    $current_plugin_version = $this->_fs->get_plugin_version( true );
-                }
+            if ( ! empty( $wp_current_filter ) && in_array( 'upgrader_process_complete', $wp_current_filter ) ) {
+                return $transient_data;
             }

             if ( ! isset( $this->_update_details ) ) {
@@ -568,7 +552,7 @@
                     false,
                     fs_request_get_bool( 'force-check' ),
                     FS_Plugin_Updater::UPDATES_CHECK_CACHE_EXPIRATION,
-                    $current_plugin_version
+                    $this->_fs->get_plugin_version()
                 );

                 $this->_update_details = false;
--- a/wpide/freemius/includes/entities/class-fs-plugin-plan.php
+++ b/wpide/freemius/includes/entities/class-fs-plugin-plan.php
@@ -13,7 +13,6 @@
 	/**
 	 * Class FS_Plugin_Plan
 	 *
-	 * @property FS_Pricing[] $pricing
 	 */
 	class FS_Plugin_Plan extends FS_Entity {

--- a/wpide/freemius/includes/entities/class-fs-site.php
+++ b/wpide/freemius/includes/entities/class-fs-site.php
@@ -10,16 +10,16 @@
         exit;
     }

-    /**
-     * @property int $blog_id
-     */
-    #[AllowDynamicProperties]
     class FS_Site extends FS_Scope_Entity {
         /**
          * @var number
          */
         public $site_id;
         /**
+         * @var int
+         */
+        public $blog_id;
+        /**
          * @var number
          */
         public $plugin_id;
--- a/wpide/freemius/includes/entities/class-fs-user.php
+++ b/wpide/freemius/includes/entities/class-fs-user.php
@@ -48,6 +48,19 @@
 			parent::__construct( $user );
 		}

+		/**
+		 * This method removes the deprecated 'is_beta' property from the serialized data.
+		 * Should clean up the serialized data to avoid PHP 8.2 warning on next execution.
+		 *
+		 * @return void
+		 */
+		function __wakeup() {
+			if ( property_exists( $this, 'is_beta' ) ) {
+				// If we enter here, and we are running PHP 8.2, we already had the warning. But we sanitize data for next execution.
+				unset( $this->is_beta );
+			}
+		}
+
 		function get_name() {
 			return trim( ucfirst( trim( is_string( $this->first ) ? $this->first : '' ) ) . ' ' . ucfirst( trim( is_string( $this->last ) ? $this->last : '' ) ) );
 		}
--- a/wpide/freemius/includes/managers/class-fs-admin-menu-manager.php
+++ b/wpide/freemius/includes/managers/class-fs-admin-menu-manager.php
@@ -699,16 +699,36 @@
 				$menu = $this->find_main_submenu();
 			}

+			$menu_slug   = $menu['menu'][2];
 			$parent_slug = isset( $menu['parent_slug'] ) ?
-                $menu['parent_slug'] :
-                'admin.php';
+				$menu['parent_slug'] :
+				'admin.php';

-            return admin_url(
-                $parent_slug .
-                ( false === strpos( $parent_slug, '?' ) ? '?' : '&' ) .
-                'page=' .
-                $menu['menu'][2]
-            );
+			if ( fs_apply_filter( $this->_module_unique_affix, 'enable_cpt_advanced_menu_logic', false ) ) {
+				$parent_slug = 'admin.php';
+
+				/**
+				 * This line and the `if` block below it are based on the `menu_page_url()` function of WordPress.
+				 *
+				 * @author Leo Fajardo (@leorw)
+				 * @since 2.10.2
+				 */
+				global $_parent_pages;
+
+				if ( ! empty( $_parent_pages[ $menu_slug ] ) ) {
+					$_parent_slug = $_parent_pages[ $menu_slug ];
+					$parent_slug  = isset( $_parent_pages[ $_parent_slug ] ) ?
+						$parent_slug :
+						$menu['parent_slug'];
+				}
+			}
+
+			return admin_url(
+				$parent_slug .
+				( false === strpos( $parent_slug, '?' ) ? '?' : '&' ) .
+				'page=' .
+				$menu_slug
+			);
 		}

 		/**
--- a/wpide/freemius/includes/managers/class-fs-admin-notice-manager.php
+++ b/wpide/freemius/includes/managers/class-fs-admin-notice-manager.php
@@ -194,8 +194,14 @@
          * @since  1.0.7
          */
         static function _add_sticky_dismiss_javascript() {
+            $sticky_admin_notice_js_template_name = 'sticky-admin-notice-js.php';
+
+            if ( ! file_exists( fs_get_template_path( $sticky_admin_notice_js_template_name ) ) ) {
+                return;
+            }
+
             $params = array();
-            fs_require_once_template( 'sticky-admin-notice-js.php', $params );
+            fs_require_once_template( $sticky_admin_notice_js_template_name, $params );
         }

         private static $_added_sticky_javascript = false;
--- a/wpide/freemius/start.php
+++ b/wpide/freemius/start.php
@@ -15,7 +15,7 @@
 	 *
 	 * @var string
 	 */
-	$this_sdk_version = '2.10.1';
+	$this_sdk_version = '2.11.0';

 	#region SDK Selection Logic --------------------------------------------------------------------

--- a/wpide/wpide.php
+++ b/wpide/wpide.php
@@ -10,7 +10,7 @@
  * Plugin Name: WPIDE - File Manager & Code Editor
  * Plugin URI:  https://wpide.com
  * Description: WordPress file manager with an advanced code editor / file editor featuring auto-completion of both WordPress and PHP functions with reference, syntax highlighting, line numbers, tabbed editing, automatic backup, image editor & much more!
- * Version:     3.5.1
+ * Version:     3.5.2
  * Requires PHP: 7.4.0
  * Requires at least: 5.0
  * Author:      XplodedThemes

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-2024-13362
# This rule blocks reflected XSS attempts targeting the Freemius trial promotion URL parameter.
# It matches requests to WordPress admin pages that contain the Freemius page parameter and a suspicious url parameter.
SecRule REQUEST_URI "@rx /wp-admin/admin.php" 
  "id:20261994,phase:2,deny,status:403,chain,msg:'CVE-2024-13362 Reflected XSS via Freemius url parameter',severity:'CRITICAL',tag:'CVE-2024-13362',tag:'wordpress',tag:'xss'"
  SecRule ARGS_GET:page "@rx ^freemius-" "chain"
    SecRule ARGS:url "@rx (?i)(?:javascript|data|vbscript|onw+)s*:" "t:urlDecodeUni,t:lowercase"

# Additional rule to catch more subtle XSS via event handlers or script tags in the url parameter
# This rule is separate to avoid false positives on legitimate URLs with 'javascript' in the path
SecRule REQUEST_URI "@rx /wp-admin/admin.php" 
  "id:20261995,phase:2,deny,status:403,chain,msg:'CVE-2024-13362 Reflected XSS via Freemius url parameter - event handler',severity:'CRITICAL',tag:'CVE-2024-13362',tag:'wordpress',tag:'xss'"
  SecRule ARGS_GET:page "@rx ^freemius-" "chain"
    SecRule ARGS:url "@rx <[^>]*script" "t:urlDecodeUni,t:lowercase"

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