SECURITY ADVISORY / 01

CVE-2024-2876 Exploit & Vulnerability Analysis

Complete CVE-2024-2876 security advisory with proof of concept (PoC), exploit details, and patch analysis for email-subscribers.

email-subscribers products NVD ↗
Exploit PoC Vulnerability Patch Analysis

The Exploit

Unauthenticated attacker; no authentication required.

GET /wp-admin/admin-ajax.php?action=ig_es_get_subscribers&list_id=1' UNION SELECT user_login,user_pass,3,4,5,6,7,8,9,10 FROM wp_users-- HTTP/1.1
Host: target.local

The attacker appends a UNION-based SQL injection payload into the list_id parameter. The vulnerable code processes non-numeric campaign values without escaping them before concatenating them into a SQL IN clause. The response leaks WordPress user credentials directly into the subscriber query result set.

What the Patch Did

Before

if ( is_numeric( $v ) ) {
    $campaigns[] = $v;
    unset( $value[ $k ] );
}

After

if ( is_numeric( $v ) ) {
    $campaigns[] = $v;
    unset( $value[ $k ] );
} else {
    $value[ $k ] = esc_sql( $v );
}

The patch added an else clause that applies WordPress's esc_sql() function to any non-numeric values in the campaign filter array. esc_sql() escapes single quotes, double quotes, and backslashes—the characters that enable SQL syntax injection in a query context. Prior to the fix, non-numeric values passed through unescaped into the SQL IN clause at line 226 of the vulnerable file.

Root Cause

CWE-89: SQL Injection.

The vulnerable code at line 226 constructs a SQL WHERE clause by directly concatenating unsanitized array values:

"lists_subscribers.contact_id IN ( SELECT contact_id FROM {$wpbd->prefix}ig_lists_contacts WHERE list_id IN (" . implode( ',', array_filter( $value, 'is_numeric' ) ) . ") AND status IN( 'subscribed', 'confirmed' ) )"

When a non-numeric value (e.g., "1' UNION SELECT...-- ") reaches the $value array parameter, the array_filter() call with the is_numeric callback discards numeric-only entries but leaves strings untouched. These strings flow directly into the implode() call and into the final query. The attacker-controlled list_id parameter from the AJAX request populates $value without prior validation or escaping, crossing the trust boundary at the request handler entry point and reaching the SQL sink unguarded.

Why It Works

The load-bearing line is $value[ $k ] = esc_sql( $v );—removing it would restore full exploitability.

Before the patch, the code assumed that an array_filter( $value, 'is_numeric' ) call would remove all dangerous values. This assumption is false: non-numeric strings pass through the filter and land in the query. The engineer's fix applies esc_sql() to the rejected (non-numeric) values. The conditional structure (if numeric, add to campaigns; else escape) ensures every value in $value is either numeric (safe in a SQL number context) or escaped. Critically, esc_sql() quotes and escapes literal strings so that any injected SQL syntax characters become inert data literals. Without it, the attacker's quote character closes the SQL string and opens a new statement. The else clause itself is the defence-in-depth: it forces the engineer to account for the non-numeric case explicitly, preventing the silent bypasses that occur when input validation is "optimistic" and unchecked paths remain.

Hardening Checklist

  • Apply esc_sql() or use parameterized queries with $wpdb->prepare() on all user-supplied values before concatenating them into SQL strings. Parameterized queries (prepared statements) are preferred; esc_sql() is a fallback for dynamic IN clauses where parameter placeholders are not sufficient.

  • Use array_filter() with explicit callbacks only for type coercion, never as a security control. Type checking (is_numeric()) is a business logic gate, not an escape mechanism. Always escape values that will enter SQL, regardless of type.

  • Audit all AJAX handlers (functions hooked to wp_ajax_* and wp_ajax_nopriv_*) for direct use of $_GET, $_POST, and $_REQUEST in SQL queries. Use static analysis or grep for patterns like $wpdb->prepare() calls missing from parameter-driven queries.

  • Require explicit sanitize_*() or esc_sql() calls at the point of use, not at request entry. WordPress's sanitize_text_field() removes some HTML; it does not escape SQL syntax. SQL escaping must happen immediately before the query.

  • Test with payloads containing single quotes, double quotes, and comment syntax (--, /**/, #) in every user-supplied parameter that touches a $wpdb->query(), $wpdb->get_results(), or similar call. Automated SAST tools should flag string concatenation into SQL.

References

  • https://nvd.nist.gov/vuln/detail/CVE-2024-2876

Frequently asked questions about CVE-2024-2876

What is CVE-2024-2876?

CVE-2024-2876 is a security vulnerability identified in email-subscribers. 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-2024-2876?

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

How does CVE-2024-2876 get exploited?

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

What products and versions are affected by CVE-2024-2876?

CVE-2024-2876 affects email-subscribers. Check the affected-versions section of this advisory for specific version ranges, vulnerable configurations, and compatibility information.

How do I fix or patch CVE-2024-2876?

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

What is the CVSS score for CVE-2024-2876?

The severity rating and CVSS scoring for CVE-2024-2876 affecting email-subscribers is documented in the vulnerability details section. Refer to the NVD entry for the current authoritative score.