weMail <= 2.0.7 - Insufficient Authorization via x-wemail-user Header to Sensitive Information Disclosure
Description
The weMail - Email Marketing, Lead Generation, Optin Forms, Email Newsletters, A/B Testing, and Automation plugin for WordPress is vulnerable to authorization bypass in all versions up to, and including, 2.0.7. This is due to the plugin's REST API trusting the `x-wemail-user` HTTP header to identify users without verifying the request originates from an authenticated WordPress session. This makes it possible for unauthenticated attackers who know or can guess an admin email (easily enumerable via `/wp-json/wp/v2/users`) to impersonate that user and access the CSV subscriber endpoints, potentially exfiltrating subscriber PII (emails, names, phone numbers) from imported CSV files.
CVSS Vector Breakdown
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:N/A:NTechnical Details
What Changed in the Fix
Changes introduced in v2.0.8
Source Code
WordPress.org SVN# Research Plan: CVE-2025-14348 - weMail Improper Authorization ## 1. Vulnerability Summary The **weMail** plugin for WordPress (versions <= 2.0.7) contains an improper authorization vulnerability in its REST API implementation. The plugin defines a custom authentication mechanism that trusts the `…
Show full research plan
Research Plan: CVE-2025-14348 - weMail Improper Authorization
1. Vulnerability Summary
The weMail plugin for WordPress (versions <= 2.0.7) contains an improper authorization vulnerability in its REST API implementation. The plugin defines a custom authentication mechanism that trusts the x-wemail-user HTTP header to identify and authenticate users. Specifically, in includes/Rest/Csv.php, the permission callback extracts an email address from this header and uses wp_set_current_user() to impersonate that user without verifying if the request originated from a valid WordPress session or contains a valid nonce/secret.
An unauthenticated attacker can provide an administrator's email address in the x-wemail-user header to gain administrative access to the plugin's CSV data endpoints, leading to the disclosure of sensitive subscriber information (PII) contained in uploaded CSV files.
2. Attack Vector Analysis
- Vulnerable Endpoints:
GET /wp-json/wemail/v1/csv/(?P<id>[\d]+)/subscribersGET /wp-json/wemail/v1/csv/(?P<id>[\d]+)/meta-fieldsGET /wp-json/wemail/v1/csv/(?P<id>[\d]+)
- Vulnerable Parameter (Header):
x-wemail-user - Authentication Required: None (Unauthenticated).
- Preconditions:
- The attacker must know or guess a valid administrator's email (enumerable via
/wp-json/wp/v2/users). - A CSV file must have been uploaded to the WordPress Media Library (as an attachment) and be known to the plugin.
- The attacker must know or guess the attachment
idof the CSV file.
- The attacker must know or guess a valid administrator's email (enumerable via
3. Code Flow
- Entry Point: A request is made to
register_rest_routeendpoints inincludes/Rest/Csv.php. - Permission Check: The
permission_callbackpoints toWeDevs\WeMail\Rest\Csv::permission($request). - Header Extraction: At line 81, the code executes
$user_email = $request->get_header( 'x-wemail-user' );. - Impersonation:
- At line 84, it looks up the user:
$user = get_user_by( 'email', $user_email );. - At line 87, if the user exists, it performs the bypass:
wp_set_current_user( $user->ID );.
- At line 84, it looks up the user:
- Capability Check: It returns
wemail()->user->can( 'create_subscriber' ). In most weMail configurations, an administrator possesses this capability. - Data Sink: Upon successful permission check, the
subscribers($request)method (line 144) is called. It retrieves the attachment URL viawp_get_attachment_url($file_id), downloads the content viawp_remote_get, and parses it usingLeague\Csv\Reader. - Disclosure: The CSV content is returned as a JSON response via
WP_REST_Response.
4. Nonce Acquisition Strategy
According to the source code in includes/Rest/Csv.php, the permission method does not check for a WordPress nonce (X-WP-Nonce) if the x-wemail-user header is provided.
Specifically, comparing includes/Rest/Csv.php to includes/Rest/Forms.php:
Forms.phpuses:if ( $nonce && wp_verify_nonce( $nonce, 'wp_rest' ) )(Line 124).Csv.phpuses:if ( ! empty( $user_email ) ) { ... wp_set_current_user( $user->ID ); ... }(Lines 83-88).
Since the vulnerability is exactly that the plugin trusts the header instead of a session/nonce, no nonce is required for this exploitation.
5. Exploitation Strategy
- Identify Admin Email:
- Request:
GET /wp-json/wp/v2/users - Extract the email of a user with the
administratorrole.
- Request:
- Identify CSV Attachment ID:
- In a real attack, IDs are sequential integers (e.g., 1, 2, 3...).
- In PoC, we will create an attachment to get a valid ID.
- Exfiltrate Data:
- Request:
GET /wp-json/wemail/v1/csv/[ID]/subscribers - Header:
x-wemail-user: [ADMIN_EMAIL] - Tool:
http_request
- Request:
6. Test Data Setup
- Create Admin: Ensure a user exists with email
admin@example.com. - Upload Sensitive CSV:
- Create a file
subscribers.csvwith content:first_name,last_name,email,phone John,Doe,john@victim.com,555-0199 Jane,Smith,jane@victim.com,555-0200 - Use WP-CLI to sideload this into the media library to get an ID:
wp media import subscribers.csv --porcelain(Capture the returned ID).
- Create a file
- No specific plugin settings are required other than the plugin being active.
7. Expected Results
- The API should return a
200 OKresponse. - The response body should be a JSON object containing an array of subscriber records:
{ "subscribers": [ { "first_name": "John", "last_name": "Doe", "email": "john@victim.com", "phone": "555-0199" }, ... ] } - Without the
x-wemail-userheader, the API should return a403 Forbiddenorfalse.
8. Verification Steps
- Verify the exfiltrated data matches the content of the
subscribers.csvfile created in step 6. - Check the
wp_poststable via WP-CLI to confirm the attachment ID used matches the file:wp post get [ID] --field=post_title - Confirm unauthenticated access by attempting the request without the header and ensuring it fails.
9. Alternative Approaches
- Information Leakage (Metadata): If
/subscribersis blocked or empty, tryGET /wp-json/wemail/v1/csv/[ID]/meta-fieldsto at least prove authorization bypass by seeing the CSV headers. - Brute Force ID: If the specific attachment ID is unknown, the agent can loop through IDs 1-100.
- API Key Path: The
permissionfunction also checksX-WeMail-Keyagainst user metawemail_api_key. If thex-wemail-userpath were patched but this remained, an attacker could attempt to find leaked API keys. However,x-wemail-useris the primary and easiest vector here.
Check if your site is affected.
Run a free security audit to detect vulnerable plugins, outdated versions, and misconfigurations.