Introduction
A missing authorization check in the LearnPress WordPress plugin allows any unauthenticated visitor to delete arbitrary quiz answer options, silently corrupting course assessments across more than 100,000 active installations. Tracked as CVE-2026-4365 with a CVSS score of 9.1, this flaw is particularly notable because the plugin itself hands attackers the only token they need: a wp_rest nonce embedded in public HTML.
LearnPress is one of the most popular open source Learning Management System (LMS) plugins for WordPress, developed by ThimPress. It powers online course creation, quizzes, and student management for educational institutions, corporate training platforms, and independent instructors worldwide. With over 100,000 active installations, vulnerabilities in LearnPress have a broad blast radius across the WordPress ecosystem.
Technical Information
The vulnerability is classified under CWE-862 (Missing Authorization) and stems from an architectural oversight where three separate components chain together to create an unauthenticated data deletion path.
The Nonce Exposure Problem
LearnPress embeds a wp_rest nonce directly into the lpData JavaScript object, which is rendered in the public frontend HTML of every page where the plugin is active. This nonce is visible to any visitor, authenticated or not. The relevant code can be found in class-lp-assets.php at line 177.
In standard WordPress security practice, nonces are meant to be paired with capability checks to provide both CSRF protection and authorization. A nonce alone does not prove a user is authorized to perform an action; it only proves the request originated from a page that WordPress rendered. By exposing this nonce publicly, LearnPress effectively neutralizes its only security gate.
The AJAX Dispatcher
The lp-load-ajax dispatcher, implemented in AbstractAjax.php at line 33, accepts incoming AJAX requests and routes them to the appropriate handler. The dispatcher validates the wp_rest nonce but performs no additional authentication or capability verification. Since the nonce is publicly available, this validation is trivially satisfied by any visitor.
The Vulnerable Action
The delete_question_answer action, found in EditQuestionAjax.php at line 285, handles the actual deletion of quiz answer options. This function contains no capability check and no ownership verification. It accepts a question answer ID and deletes it directly.
Attack Flow
The exploitation path is straightforward:
- An attacker visits any public page on a WordPress site running LearnPress (versions up to and including 4.3.2.8).
- The attacker inspects the page source or JavaScript context to extract the
wp_restnonce from thelpDataobject. - The attacker crafts a POST request to the
lp-load-ajaxendpoint, specifying thedelete_question_answeraction and including the extracted nonce along with the target answer ID. - The AJAX dispatcher validates the nonce (which passes, since it was legitimately generated by WordPress) and routes the request to the handler.
- The
delete_question_answerfunction executes the deletion without checking whether the requester has any WordPress capabilities or any relationship to the question. - The targeted quiz answer option is permanently removed from the database.
The following table summarizes how the three components interact:
| Component | Location | Flaw | Security Implication |
|---|---|---|---|
lpData object | class-lp-assets.php | Exposes wp_rest nonce in public HTML | Provides attackers the required token |
lp-load-ajax dispatcher | AbstractAjax.php | Uses nonce as the sole security gate | Bypasses standard authentication |
delete_question_answer | EditQuestionAjax.php | No capability or ownership check | Allows arbitrary answer deletion |
What makes this vulnerability especially impactful is the asymmetry between effort and damage. The attacker needs only a browser and a simple HTTP client. The damage, deletion of quiz answers, can silently corrupt assessments, invalidate student results, and undermine the integrity of entire course catalogs.
Patch Information
The vulnerability was patched in LearnPress version 4.3.3, released to the WordPress plugin repository. The fix is a surgical, single line addition inside the QuestionAnswerModel class located at inc/Models/Question/QuestionAnswerModel.php.
To understand the patch, we need to look at the pre-patch architecture. In version 4.3.2.8 and earlier, the delete() method calls check_valid_before_delete() as its gatekeeper. However, that pre-delete validation only checked two things: whether the parent question existed, and whether the question type still had the minimum number of answers. It performed no authorization check whatsoever.
Interestingly, the model class already had a check_capabilities_update() method that was properly used by the save() method for create and update operations. That method calls WordPress's user_can() to verify the current user holds the edit_lp_lesson capability for the specific question. The delete path simply never invoked it.
In version 4.3.3, the developers added a single call to $this->check_capabilities_update() inside check_valid_before_delete(), right after the question existence check and before the answer count validation:
public function check_valid_before_delete() { $questionPostModel = $this->get_question_post_model(); if ( ! $questionPostModel ) { throw new Exception( __( 'Question not found', 'learnpress' ) ); } + + $this->check_capabilities_update(); if ( $questionPostModel->get_type() === 'single_choice' || ...
The check_capabilities_update() method itself enforces the authorization:
public function check_capabilities_update() { $user = wp_get_current_user(); if ( ! user_can( $user, 'edit_' . LP_LESSON_CPT, $this->question_id ) ) { throw new Exception( __( 'You do not have permission to edit this item.', 'learnpress' ) ); } }
After the patch, when an unauthenticated attacker sends a crafted POST request to the delete_question_answer AJAX action (even with the publicly exposed wp_rest nonce), the check_capabilities_update() call retrieves the current user (which will be an anonymous visitor with no capabilities), the user_can() check fails, and an exception is thrown, halting the deletion before it ever reaches the database.
The method's PHPDoc version tag was also bumped from @version 1.0.0 to @version 1.0.1, confirming this was a targeted, intentional security fix. The elegance of this patch lies in reusing the existing check_capabilities_update() authorization method that was already protecting the save() flow. The delete path was simply an oversight that left it unguarded.
Organizations running LearnPress should update to version 4.3.3 or later immediately. The vulnerable and patched model files can be compared directly in the WordPress plugin SVN repository.
Affected Systems and Versions
All versions of the LearnPress WordPress plugin up to and including 4.3.2.8 are affected. The vulnerability is present in any WordPress installation where LearnPress is active, regardless of WordPress core version or server configuration.
The fix is available in LearnPress version 4.3.3.
Vendor Security History
ThimPress, the developer of LearnPress, has a documented pattern of security vulnerabilities in this plugin that warrants attention from organizations relying on it:
- Authenticated SQL Injection (versions up to 4.2.6.9.3): A SQL injection vulnerability via the
orderparameter was accessible to users with Contributor level access. This was patched in version 4.2.6.9.4. - CVE-2026-4333: A stored Cross Site Scripting (XSS) vulnerability was recently identified in LearnPress, further illustrating recurring input validation and authorization gaps.
This history of authorization bypasses, injection flaws, and XSS issues suggests that organizations deploying LearnPress should maintain strict monitoring, apply updates promptly, and consider additional hardening measures such as Web Application Firewalls with WordPress specific rulesets.
References
- Wordfence Vulnerability Advisory for CVE-2026-4365
- NVD Entry for CVE-2026-4365
- AbstractAjax.php (AJAX Dispatcher Source)
- EditQuestionAjax.php (Vulnerable Action Source)
- class-lp-assets.php (Nonce Exposure Source)
- QuestionAnswerModel.php (Version 4.3.2.8, Vulnerable)
- QuestionAnswerModel.php (Version 4.3.3, Patched)
- LearnPress on WordPress.org Plugin Directory
- LearnPress Official Changelog



