Devs CRM – Manage tasks, attendance and teams all together <= 1.1.8 - Missing Authorization to Unauthenticated Lead Tag Update
Description
The Devs CRM – Manage tasks, attendance and teams all together plugin for WordPress is vulnerable to unauthorized modification of data due to a missing capability check on the '/wp-json/devs-crm/v1/bulk-update' REST-API endpoint in all versions up to, and including, 1.1.8. This makes it possible for unauthenticated attackers to update leads tags.
CVSS Vector Breakdown
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:L/A:NTechnical Details
# Exploitation Research Plan: CVE-2025-13093 (Devs CRM) ## 1. Vulnerability Summary The **Devs CRM** plugin for WordPress (up to version 1.1.8) registers a REST API endpoint `/wp-json/devs-crm/v1/bulk-update` which lacks a proper `permission_callback` or capability check. This allows unauthenticate…
Show full research plan
Exploitation Research Plan: CVE-2025-13093 (Devs CRM)
1. Vulnerability Summary
The Devs CRM plugin for WordPress (up to version 1.1.8) registers a REST API endpoint /wp-json/devs-crm/v1/bulk-update which lacks a proper permission_callback or capability check. This allows unauthenticated remote attackers to perform bulk modifications on "leads" data, specifically updating lead tags. This is a classic "Missing Authorization" vulnerability where a sensitive administrative function is exposed to the public API.
2. Attack Vector Analysis
- Endpoint:
/wp-json/devs-crm/v1/bulk-update - Method:
POST - Namespace:
devs-crm/v1 - Route:
bulk-update - Preconditions:
- The plugin must be active.
- At least one "Lead" must exist in the system (to observe the modification).
- Authentication: None required (Unauthenticated).
3. Code Flow (Inferred)
- Registration: The plugin uses the
rest_api_inithook to register its API routes. - Endpoint Definition: Inside the registration function (likely in
includes/api/class-rest-api.phpor a similar REST handler class),register_rest_routeis called for thebulk-updatepath. - Vulnerability: The
permission_callbackargument is likely set to__return_trueor is omitted entirely, allowing any visitor to access the callback. - Execution: The
callbackfunction associated with the route (e.g.,bulk_update_leads) retrieves theidsandtagsfrom the request parameters and updates the lead records in the database (likely usingwp_set_object_termsif leads are Custom Post Types, or direct$wpdbqueries if using custom tables).
4. Nonce Acquisition Strategy
WordPress REST API endpoints typically do not require a nonce (X-WP-Nonce) for unauthenticated requests unless the logic specifically checks for one or if the request is being made from a browser session with active authentication cookies.
Since this is an unauthenticated vulnerability:
- Primary approach: Send the
POSTrequest directly using thehttp_requesttool without any cookies or nonce headers. - Backup approach: If the REST API is configured to strictly require the
wp_restnonce for all requests, we can obtain it from any frontend page where the plugin is active:- Check for localized scripts using
browser_eval("window.devs_crm_obj")(inferred variable name). - If no variable is found, create a page with a Devs CRM shortcode (e.g.,
[devs_crm_leads]or similar found inadd_shortcodecalls) to force the plugin to enqueue scripts and nonces.
- Check for localized scripts using
5. Exploitation Strategy
The goal is to modify the tags of an existing lead without authentication.
Step 1: Discover Lead IDs
Since we are in a test environment, we will create a lead manually to obtain its ID. In a real-world scenario, lead IDs are often sequential or can be discovered via other public API endpoints.
Step 2: Craft the Payload
The endpoint bulk-update likely expects a JSON body. Based on the "leads tags" description, the expected parameters are probably ids (array of integers) and tags (string or array).
Target URL: http://vulnerable-wp.local/wp-json/devs-crm/v1/bulk-update
Method: POST
Content-Type: application/json
Payload (Inferred):
{
"ids": [123],
"tags": "pwned_tag"
}
Step 3: Execute Request
Use the http_request tool to send the payload.
6. Test Data Setup
Before testing the exploit, prepare the environment:
- Install Plugin: Ensure
devs-crmversion <= 1.1.8 is installed. - Create a Lead:
Use WP-CLI to create a lead if they are Custom Post Types, or use the plugin's UI.# If leads are a CPT (assume 'crm_lead' - verify via 'wp post-type list') wp post create --post_type=crm_lead --post_title="Test Lead" --post_status=publish - Verify Existing Tags: Check the tags of the newly created lead (ID:
123).
7. Expected Results
- Response Code:
200 OKor201 Created. - Response Body: A JSON success message (e.g.,
{"success": true, "message": "Updated successfully"}). - Effect: The lead with the specified ID will have its tags updated to the value provided in the payload.
8. Verification Steps
After sending the exploit request, verify the database state:
- Check via WP-CLI:
# If using CPT and standard taxonomy: wp post term list <LEAD_ID> post_tag # Or check custom table if applicable: wp db query "SELECT * FROM wp_devs_crm_leads WHERE id = <LEAD_ID>" - Confirm Absence of Authorization: Attempt the same request with no headers to confirm that the plugin did not reject the request due to missing permissions.
9. Alternative Approaches
If the application/json payload fails:
- Try URL-Encoded:
ids[]=123&tags=pwned_tagwithContent-Type: application/x-www-form-urlencoded. - Check for different keys: Try
lead_idsinstead ofidsortag_idinstead oftags. - Examine Route Registration: Use
grep -r "register_rest_route" wp-content/plugins/devs-crmto find the exact function name handling thebulk-updateroute and identify the exact parameter names from theWP_REST_Requestobject.
Summary
The Devs CRM plugin for WordPress fails to implement authorization checks on its bulk-update REST API endpoint. This allows unauthenticated attackers to modify lead tags by sending a crafted POST request to the '/wp-json/devs-crm/v1/bulk-update' endpoint.
Vulnerable Code
// likely located in includes/api/class-rest-api.php or similar register_rest_route('devs-crm/v1', '/bulk-update', array( 'methods' => 'POST', 'callback' => array($this, 'bulk_update_leads'), 'permission_callback' => '__return_true', // This allows unauthenticated access )); --- public function bulk_update_leads(WP_REST_Request $request) { $params = $request->get_params(); $lead_ids = isset($params['ids']) ? $params['ids'] : array(); $tags = isset($params['tags']) ? $params['tags'] : ''; foreach ($lead_ids as $id) { // Logic to update tags for lead ID, such as wp_set_object_terms wp_set_object_terms($id, $tags, 'lead_tag'); } return new WP_REST_Response(array('success' => true), 200); }
Security Fix
@@ -10,7 +10,9 @@ register_rest_route('devs-crm/v1', '/bulk-update', array( 'methods' => 'POST', 'callback' => array($this, 'bulk_update_leads'), - 'permission_callback' => '__return_true', + 'permission_callback' => function() { + return current_user_can('manage_options'); + }, ));
Exploit Outline
1. Identify the target WordPress site running Devs CRM <= 1.1.8. 2. Identify valid Lead IDs (often sequential integers or enumerable via other API endpoints). 3. Craft a POST request to `/wp-json/devs-crm/v1/bulk-update`. 4. Set the 'Content-Type' header to 'application/json'. 5. Include a JSON body containing the target lead IDs and the desired tags, e.g., '{"ids": [1, 2, 3], "tags": "unauthorized_tag"}'. 6. Send the request without any authentication headers or nonces. The plugin will process the update and apply the tags to the specified lead records.
Check if your site is affected.
Run a free security audit to detect vulnerable plugins, outdated versions, and misconfigurations.