CVE-2026-32495

WP Terms Popup – Terms and Conditions and Privacy Policy WordPress Popups <= 2.10.0 - Missing Authorization

mediumMissing Authorization
5.3
CVSS Score
5.3
CVSS Score
medium
Severity
2.11.0
Patched in
8d
Time to patch

Description

The WP Terms Popup – Terms and Conditions and Privacy Policy WordPress Popups plugin for WordPress is vulnerable to unauthorized access due to a missing capability check on a function in all versions up to, and including, 2.10.0. This makes it possible for unauthenticated attackers to perform an unauthorized action.

CVSS Vector Breakdown

CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:L/A:N
Attack Vector
Network
Attack Complexity
Low
Privileges Required
None
User Interaction
None
Scope
Unchanged
None
Confidentiality
Low
Integrity
None
Availability

Technical Details

Affected versions<=2.10.0
PublishedMarch 20, 2026
Last updatedMarch 27, 2026
Affected pluginwp-terms-popup

What Changed in the Fix

Changes introduced in v2.11.0

Loading patch diff...

Source Code

WordPress.org SVN
Research Plan
Unverified

. I will focus on `wptp_save_settings` as the primary target. * Setup: Install plugin, create a popup, set it sitewide. * Nonce extraction: Create a page, or just check the homepage if popups are enabled. * Exploit: Send `action=wptp_save_settings&termsopt_sitewide=0` (disabl…

Show full research plan

.

I will focus on `wptp_save_settings` as the primary target.

*   Setup: Install plugin, create a popup, set it sitewide.
*   Nonce extraction: Create a page, or just check the homepage if popups are enabled.
*   Exploit: Send `action=wptp_save_settings&termsopt_sitewide=0` (disabling the popup).
*   Verification: Check the option value via WP-CLI.

*   The nonce is `wptp-ajaxhandler-nonce`.
*   The JS variable is `wptp_ajax_object.ajax_nonce`.
*   The action used in `ajaxhandler_css` and `ajaxhandler_popup` is `wptp-ajaxhandler-nonce`.
*   If `wptp_save_settings` also uses this nonce, we are in business.
*   If it doesn't use a nonce at all, even better.

Wait, what if the action name is `wptp_designer_save_css`?
The public file has `ajaxhandler_css`.
The public file also mentions "WP Terms Popup Designer" in the `readme`.
If the base plugin contains the AJAX handler for the Designer's CSS...
`ajaxhandler_css` in the public file is for *fetching* the CSS.
What if there's a counterpart for *saving*?

Let's assume the
Research Findings
Static analysis — not yet PoC-verified

Summary

The WP Terms Popup plugin for WordPress is vulnerable to unauthorized information disclosure and access due to missing capability checks and insufficient validation on its AJAX handlers. Unauthenticated attackers can exploit this to retrieve the content of arbitrary posts, including private or draft content, by providing a specific post ID to the public-facing AJAX endpoints.

Vulnerable Code

/**
     * Handle CSS AJAX.
     *
     * @since    2.0.0
     */
    public function ajaxhandler_css()
    {
        // check_ajax_referer('wptp-ajaxhandler-nonce', 'wptp_nonce');

        if (!isset($_POST['wptp_nonce']) || !wp_verify_nonce(sanitize_text_field($_POST['wptp_nonce']), 'wptp-ajaxhandler-nonce')) {
            exit();
        }

        $wptp_content['css'] = $this->popup_css($_POST['termspageid']);

        die(json_encode($wptp_content));
    }

    /**
     * Handle HTML AJAX.
     *
     * @since    2.0.0
     */
    public function ajaxhandler_popup()
    {
        // check_ajax_referer('wptp-ajaxhandler-nonce', 'wptp_nonce');

        if (!isset($_POST['wptp_nonce']) || !wp_verify_nonce(sanitize_text_field($_POST['wptp_nonce']), 'wptp-ajaxhandler-nonce')) {
            exit();
        }

        $wptp_content['popup'] = $this->popup_html(sanitize_text_field($_POST['termspageid']));

        die(json_encode($wptp_content));
    }

Security Fix

diff -ru /home/deploy/wp-safety.org/data/plugin-versions/wp-terms-popup/2.10.0/index.php /home/deploy/wp-safety.org/data/plugin-versions/wp-terms-popup/2.11.0/index.php
--- /home/deploy/wp-safety.org/data/plugin-versions/wp-terms-popup/2.10.0/index.php	2025-08-06 12:22:14.000000000 +0000
+++ /home/deploy/wp-safety.org/data/plugin-versions/wp-terms-popup/2.11.0/index.php	2026-03-02 16:36:26.000000000 +0000
@@ -9,7 +9,7 @@
  * Plugin Name:       WP Terms Popup
  * Plugin URI:        https://termsplugin.com
  * Description:       Ask users to agree to a popup before they are allowed to view your site.
- * Version:           2.10.0
+ * Version:           2.11.0
  * Author:            Link Software LLC
  * Author URI:        https://linksoftwarellc.com
  * License:           GPL-2.0+
@@ -26,7 +26,7 @@
 /**
  * Currently plugin version.
  */
-define('WP_TERMS_POPUP_VERSION', '2.10.0');
+define('WP_TERMS_POPUP_VERSION', '2.11.0');
 
 function activate_wp_terms_popup()
 {
diff -ru /home/deploy/wp-safety.org/data/plugin-versions/wp-terms-popup/2.10.0/public/class-wp-terms-popup-public.php /home/deploy/wp-safety.org/data/plugin-versions/wp-terms-popup/2.11.0/public/class-wp-terms-popup-public.php
--- /home/deploy/wp-safety.org/data/plugin-versions/wp-terms-popup/2.10.0/public/class-wp-terms-popup-public.php	2025-08-06 12:22:14.000000000 +0000
+++ /home/deploy/wp-safety.org/data/plugin-versions/wp-terms-popup/2.11.0/public/class-wp-terms-popup-public.php	2026-03-02 16:36:26.000000000 +0000
@@ -163,15 +163,25 @@
      */
     public function ajaxhandler_css()
     {
-        // check_ajax_referer('wptp-ajaxhandler-nonce', 'wptp_nonce');
-
         if (!isset($_POST['wptp_nonce']) || !wp_verify_nonce(sanitize_text_field($_POST['wptp_nonce']), 'wptp-ajaxhandler-nonce')) {
-            exit();
+            wp_die('', 'Forbidden', array('response' => 403));
         }
 
-        $wptp_content['css'] = $this->popup_css($_POST['termspageid']);
+        if (!isset($_POST['termspageid']) || !is_numeric($_POST['termspageid'])) {
+            wp_die('', 'Bad Request', array('response' => 400));
+        }
+
+        $terms_page_id = intval($_POST['termspageid']);
+        
+        // Verify that the post exists, is of the correct type, and is published
+        $post = get_post($terms_page_id);
+        if (!$post || $post->post_type !== 'termpopup' || $post->post_status !== 'publish') {
+            wp_die('', 'Bad Request', array('response' => 400));
+        }
+
+        $wptp_content['css'] = $this->popup_css($terms_page_id);
 
-        die(json_encode($wptp_content));
+        wp_die(json_encode($wptp_content));
     }
 
     /**
@@ -181,15 +191,25 @@
      */
     public function ajaxhandler_popup()
     {
-        // check_ajax_referer('wptp-ajaxhandler-nonce', 'wptp_nonce');
-
         if (!isset($_POST['wptp_nonce']) || !wp_verify_nonce(sanitize_text_field($_POST['wptp_nonce']), 'wptp-ajaxhandler-nonce')) {
-            exit();
+            wp_die('', 'Forbidden', array('response' => 403));
         }
 
-        $wptp_content['popup'] = $this->popup_html(sanitize_text_field($_POST['termspageid']));
+        if (!isset($_POST['termspageid']) || !is_numeric($_POST['termspageid'])) {
+            wp_die('', 'Bad Request', array('response' => 400));
+        }
+
+        $terms_page_id = intval($_POST['termspageid']);
+        
+        // Verify that the post exists, is of the correct type, and is published
+        $post = get_post($terms_page_id);
+        if (!$post || $post->post_type !== 'termpopup' || $post->post_status !== 'publish') {
+            wp_die('', 'Bad Request', array('response' => 400));
+        }
+
+        $wptp_content['popup'] = $this->popup_html($terms_page_id);
 
-        die(json_encode($wptp_content));
+        wp_die(json_encode($wptp_content));
     }

Exploit Outline

The exploit target is the `ajaxhandler_popup` or `ajaxhandler_css` AJAX functions, which are intended to serve popup content to unauthenticated users but lack server-side validation that the requested ID belongs to a public 'termpopup' post type. 1. **Nonce Acquisition**: An attacker visits the site's homepage or any page where the plugin is active. If the 'Load popups with JavaScript' setting is enabled, the plugin localizes a script containing the `wptp-ajaxhandler-nonce` in the `wptp_ajax_object.ajax_nonce` JavaScript variable. 2. **Unauthorized Request**: The attacker sends a POST request to `/wp-admin/admin-ajax.php` with the following parameters: - `action`: `wptp_ajax_popup` (or `wptp_ajax_css`) - `wptp_nonce`: The extracted nonce value. - `termspageid`: The numeric ID of a target post or page (including private, draft, or password-protected content). 3. **Information Extraction**: Because the vulnerable versions (<= 2.10.0) do not check the post type or post status of the provided ID, the plugin will process the ID and return the HTML or CSS of that post in the JSON response.

Check if your site is affected.

Run a free security audit to detect vulnerable plugins, outdated versions, and misconfigurations.