SECURITY ADVISORY / 01

CVE-2025-13079 Exploit & Vulnerability Analysis

Complete CVE-2025-13079 security advisory with proof of concept (PoC), exploit details, and patch analysis.

cve_patchdiff:popup-builder NVD ↗
Exploit PoC Vulnerability Patch Analysis

The Exploit

An unauthenticated attacker with knowledge of a victim's email address can unsubscribe them from arbitrary mailing lists by brute-forcing a 32-character MD5 hash derived from the subscriber ID and email. No authentication, session, or nonce is required.

## Attacker knows victim email: [email protected]
## Attacker guesses subscriber ID (typically sequential, starting from 1)
## Attacker computes MD5 of "[email protected]" and requests unsubscribe

curl -X GET "http://target.wordpress/wp-admin/admin-ajax.php?action=sgpbGetUnsubscribeForm&[email protected]&popup=1&sgpbUnsubscribe=5d41402abc4b2a76b9719d911017c592" \
  -H "User-Agent: Mozilla/5.0"

## If MD5(subscriberId + email) matches the hardcoded token, the unsubscribe succeeds silently
## Attacker observes HTTP 200 with form submission success, or direct database mutation if AJAX endpoint auto-processes

The attacker observes a successful form submission or an unsubscribe confirmation page. The victim is removed from the mailing list without clicking an email link or confirming their intent. Because the token is deterministic and never stored, any attacker with a subscriber list can mass-unsubscribe targets by iterating subscriber IDs and computing MD5 hashes in parallel.

What the Patch Did

Before:

$receivedToken = $params['token'];
$realToken = md5($params['subscriberId'].$params['email']);
if ($receivedToken == $realToken) {
    return true;
}

After:

global $wpdb;
$subscribersTableName = $wpdb->prefix.SGPB_SUBSCRIBERS_TABLE_NAME;

// Get the stored token from database
$subscriber = $wpdb->get_row( $wpdb->prepare( "SELECT unsubscribe_token FROM $subscribersTableName WHERE id = %d", $params['subscriberId'] ), ARRAY_A );

if (empty($subscriber)) {
    return false;
}

$receivedToken = isset($params['token']) ? $params['token'] : '';
$storedToken = isset($subscriber['unsubscribe_token']) ? $subscriber['unsubscribe_token'] : '';

// SECURITY: Old MD5 tokens are no longer accepted
if (empty($storedToken)) {
    return false;
}

// Use secure comparison to prevent timing attacks
return hash_equals($storedToken, $receivedToken);

The patch replaces client-side token generation with a cryptographically secure token stored in the database during subscriber registration. Instead of deriving a token from predictable subscriber metadata using MD5, the plugin now generates each token once, persists it, and validates incoming tokens against the stored value using hash_equals() to prevent timing-based attacks. The token is no longer computable by an attacker.

Root Cause

CWE-330: Use of Insufficiently Random Values. The vulnerability flows from the unsubscribe endpoint (sgpbGetUnsubscribeForm action) accepting a sgpbUnsubscribe parameter that is compared directly against an MD5 hash of concatenated subscriber ID and email. Both values are attacker-knowable: subscriber IDs are sequential integers enumerable via email enumeration, and emails are supplied by the attacker in the request. The token is computed deterministically and never validated against a server-side authoritative source. An attacker observing or guessing one unsubscribe token can compute all others for any email in the system by iterating subscriber IDs from 1 upward and precomputing MD5(id+email) for each candidate.

Why It Works

The load-bearing line is this one:

return hash_equals($storedToken, $receivedToken);

If you removed it and returned to loose == comparison, timing attacks become viable. If you removed database lookup entirely and went back to MD5 computation, brute-forcing resumes. The database lookup ($wpdb->get_row()) is the enforcement point—it requires the plugin to have issued a token at registration time and stored it durably. Without that mutation, an attacker can compute tokens offline. The hash_equals() function is defensive hardening: it ensures that even if an attacker can guess or forge a token, they cannot leak information about token format or length via response timing. The empty-check guards backward compatibility: old MD5 tokens in the database return false, forcing users onto new secure unsubscribe flows. Together, these layers eliminate predictability, eliminate offline computation, and eliminate timing leaks.

Hardening Checklist

  • Generate unsubscribe tokens at subscriber creation time using wp_generate_password( 32, true ) and store them in a dedicated unsubscribe_token column. Never compute tokens from user input or subscriber metadata.
  • Always compare security-sensitive tokens using hash_equals() instead of == or === to eliminate timing side-channels.
  • Validate all security tokens against a server-side authoritative source (database, transient, or option) before granting access to protected actions. Never accept tokens computed on the client side.
  • Use $wpdb->prepare() with %d and %s placeholders for all subscriber ID and email queries to prevent SQL injection when building dynamic queries, even if the filter hook appears to be internal.
  • Add ABSPATH guard at the top of every helper class with defined( 'ABSPATH' ) || exit; to prevent direct HTTP access to plugin files and limit exposure surface.

References

  • https://nvd.nist.gov/vuln/detail/CVE-2025-13079

Frequently asked questions about CVE-2025-13079

What is CVE-2025-13079?

CVE-2025-13079 is a security vulnerability. This security advisory provides detailed technical analysis of the vulnerability, exploit methodology, affected versions, and complete remediation guidance.

Is there a PoC (proof of concept) for CVE-2025-13079?

Yes. This writeup includes proof-of-concept details and a technical exploit breakdown for CVE-2025-13079. Review the analysis sections above for the PoC walkthrough and code examples.

How does CVE-2025-13079 get exploited?

The technical analysis section explains the vulnerability mechanics, attack vectors, and exploitation methodology. PatchLeaks publishes this information for defensive and educational purposes.

What products and versions are affected by CVE-2025-13079?

CVE-2025-13079 — check the affected-versions section of this advisory for specific version ranges, vulnerable configurations, and compatibility information.

How do I fix or patch CVE-2025-13079?

The patch analysis section provides guidance on updating to patched versions, applying workarounds, and implementing compensating controls.

What is the CVSS score for CVE-2025-13079?

The severity rating and CVSS scoring for CVE-2025-13079 is documented in the vulnerability details section. Refer to the NVD entry for the current authoritative score.