[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"$fZdTeP1OkASinkpRW4N7qAPepeUh3md-jRiREk0fOKeA":3},{"id":4,"url_slug":5,"title":6,"description":7,"plugin_slug":8,"theme_slug":9,"affected_versions":10,"patched_in_version":11,"severity":12,"cvss_score":13,"cvss_vector":14,"vuln_type":15,"published_date":16,"updated_date":17,"references":18,"days_to_patch":20,"patch_diff_files":21,"patch_trac_url":9,"research_status":29,"research_verified":30,"research_rounds_completed":31,"research_plan":32,"research_summary":33,"research_vulnerable_code":34,"research_fix_diff":35,"research_exploit_outline":36,"research_model_used":37,"research_started_at":38,"research_completed_at":39,"research_error":9,"poc_status":9,"poc_video_id":9,"poc_summary":9,"poc_steps":9,"poc_tested_at":9,"poc_wp_version":9,"poc_php_version":9,"poc_playwright_script":9,"poc_exploit_code":9,"poc_has_trace":30,"poc_model_used":9,"poc_verification_depth":9,"poc_exploit_code_gated":30,"source_links":40},"CVE-2026-42668","omnisend-for-woocommerce-unauthenticated-omnisend-account-takeover-via-predictable-connect-token","Omnisend for WooCommerce \u003C= 1.18.0 - Unauthenticated Omnisend Account Takeover via Predictable Connect Token","The Omnisend for WooCommerce plugin for WordPress is vulnerable to an unauthenticated account takeover via insufficiently random values in versions up to, and including, 1.18.0. This is due to the generate_install_url() function deriving the OAuth connect token solely from the Unix timestamp at page-load time (hash('sha256', time())), which yields only ~86,400 distinct values per day and is trivially brute-forceable. This makes it possible for unauthenticated attackers to predict the connect token, authenticate to the POST \u002Fwp-json\u002Fomnisend-api\u002Fv1\u002Fconnect REST endpoint, and replace the store's omnisend_api_key and brand_id with attacker-supplied values, redirecting all customer PII synchronization, order webhooks, and marketing communications to the attacker's Omnisend account.","omnisend-connect",null,"\u003C=1.18.0","1.18.1","high",7.5,"CVSS:3.1\u002FAV:N\u002FAC:L\u002FPR:N\u002FUI:N\u002FS:U\u002FC:N\u002FI:H\u002FA:N","Use of Insufficiently Random Values","2026-05-13 00:00:00","2026-05-13 15:43:29",[19],"https:\u002F\u002Fwww.wordfence.com\u002Fthreat-intel\u002Fvulnerabilities\u002Fid\u002F092061a5-5315-4401-8503-6153f660acdc?source=api-prod",1,[22,23,24,25,26,27,28],"changelog.txt","includes\u002Fblocks\u002Fpackage-lock.json","includes\u002Fblocks\u002Fpackage.json","includes\u002Fclass-omnisend-install.php","includes\u002Fomnisend-api.php","omnisend-woocommerce.php","readme.txt","researched",false,3,"# Vulnerability Research Plan: CVE-2026-42668 - Omnisend Account Takeover\n\n## 1. Vulnerability Summary\nThe **Omnisend for WooCommerce** plugin (up to and including version 1.18.0) contains a critical vulnerability where the OAuth connection token is generated using a predictable value. Specifically, the `Omnisend_Install::generate_install_url()` function creates a `connect_token` using `hash('sha256', time())`. Since `time()` is a Unix timestamp with second-level precision, the resulting token space is extremely small (~86,400 values per day), allowing an unauthenticated attacker to brute-force the token and gain access to the `POST \u002Fwp-json\u002Fomnisend-api\u002Fv1\u002Fconnect` REST endpoint.\n\nSuccessful exploitation allows an attacker to overwrite the `omnisend_api_key` and `omnisend_account_id` (brand ID) settings, effectively hijacking the data synchronization between the WordPress store and Omnisend. This results in sensitive customer PII and order data being sent to the attacker's Omnisend account.\n\n## 2. Attack Vector Analysis\n- **REST Endpoint:** `POST \u002Fwp-json\u002Fomnisend-api\u002Fv1\u002Fconnect`\n- **Permissions:** Restricted by the `validate_connect_token` permission callback, which requires a valid `connect_token`.\n- **Vulnerability:** The `connect_token` is generated using a predictable `time()` value and persists in the `omnisend_connect_token` option until used or explicitly cleared.\n- **Precondition:** The `omnisend_connect_token` must have been generated (typically by an administrator visiting the Omnisend settings page) but not yet used to complete a connection.\n\n## 3. Code Flow\n1. **Token Generation:** \n   - When an administrator visits the settings page, `Omnisend_Install::generate_install_url()` (in `includes\u002Fclass-omnisend-install.php`) is called.\n   - It checks if `omnisend_connect_token` is empty:\n     ```php\n     $token = get_option( 'omnisend_connect_token', '' );\n     if ( $token === '' ) {\n         $token = hash( 'sha256', time() );\n         update_option( 'omnisend_connect_token', $token );\n     }\n     ```\n2. **REST Authentication:**\n   - The `\u002Fconnect` route uses `validate_connect_token` as its `permission_callback` (`includes\u002Fomnisend-api.php`).\n   - `validate_connect_token` extracts `connect_token` from the JSON body and compares it to the stored option:\n     ```php\n     $token = get_option( 'omnisend_connect_token', '' );\n     if ( $token !== $request['connect_token'] ) { \u002F\u002F 401 Unauthorized if mismatch\n     ```\n3. **Account Takeover:**\n   - If the token matches, `omnisend_connect_account` is executed.\n   - It updates the API keys with the attacker-provided values:\n     ```php\n     update_option( 'omnisend_connect_token', null );\n     Omnisend_Settings::set_brand_id( $body['brand_id'] );\n     update_option( 'omnisend_api_key', $body['omnisend_api_key'] );\n     ```\n\n## 4. Nonce Acquisition Strategy\nThis vulnerability does **not** require a WordPress nonce for the final exploitation of the REST endpoint. The `permission_callback` only validates the `connect_token`. \n\nHowever, to trigger the generation of the token if it doesn't exist, an administrator must visit the settings page. For the purpose of a PoC in a test environment, the agent should simulate this by navigating to the Omnisend settings page or manually calling the function via WP-CLI.\n\n## 5. Exploitation Strategy\n\n### Step 1: Synchronize Time\nObtain the server's current time to establish a baseline for the brute-force attack.\n- **Request:** `GET \u002F`\n- **Action:** Extract the `Date` header from the response. Convert this to a Unix timestamp.\n\n### Step 2: Brute Force Token\nIterate through timestamps backwards from the current time (e.g., covering the last 24 hours).\n- **Target Endpoint:** `POST \u002Fwp-json\u002Fomnisend-api\u002Fv1\u002Fconnect`\n- **Method:** `POST`\n- **Headers:** `Content-Type: application\u002Fjson`\n- **Payload:** \n  ```json\n  {\n    \"connect_token\": \"HASH_OF_TIMESTAMP\",\n    \"brand_id\": \"attacker_brand_123\",\n    \"omnisend_api_key\": \"attacker_api_key_456\"\n  }\n  ```\n- **Logic:**\n  1. Calculate `token = hash('sha256', (string)$timestamp)`.\n  2. Send request.\n  3. If response is `401` (`omnisend_incorrect_connect_token`), decrement timestamp and retry.\n  4. If response is `403` (`omnisend_connect_denied`), the token is empty or already used (abort\u002Freset).\n  5. If response is `200` (`success: true`), the takeover is successful.\n\n## 6. Test Data Setup\n1. Install and activate the plugin: `wp plugin install omnisend-connect --version=1.18.0 --activate`.\n2. Ensure the plugin is in an \"unconnected\" state.\n3. **Critical Step:** Simulate an admin visit to generate the token:\n   - Use `wp eval \"Omnisend_Install::get_registration_url();\"` OR\n   - Login as admin and navigate to `wp-admin\u002Fadmin.php?page=omnisend-woocommerce`.\n\n## 7. Expected Results\n- The brute-force script should eventually identify the timestamp used during the \"admin visit\" simulation.\n- The server will return a `200 OK` response with `{\"success\": true}`.\n- The `omnisend_api_key` option will be updated to `attacker_api_key_456`.\n\n## 8. Verification Steps\nAfter the HTTP exploit succeeds, verify the database state using WP-CLI:\n- Check Brand ID: `wp option get omnisend_account_id`\n- Check API Key: `wp option get omnisend_api_key`\n- Check Token cleared: `wp option get omnisend_connect_token` (should be empty\u002Fnull)\n\n## 9. Alternative Approaches\nIf the plugin is already connected, the `validate_connect_token` function returns a `403`. In a real-world scenario, the attacker would wait for a \"disconnect\" event (or trigger one if another vulnerability exists) to force the site back into the connectable state. For this PoC, focus on the **initial connection phase** or a site where the admin has navigated to the setup page but hasn't finalized the connection.","The Omnisend for WooCommerce plugin is vulnerable to an unauthenticated account takeover due to the use of a predictable OAuth connection token generated using only the current Unix timestamp. Attackers can brute-force the approximately 86,400 possible tokens for a given day to authenticate to a sensitive REST endpoint and overwrite the store's API keys. This allows the attacker to hijack the synchronization of sensitive customer PII and order data to their own Omnisend account.","\u002F\u002F includes\u002Fclass-omnisend-install.php (around line 234 in version 1.18.0)\n\tprivate static function generate_install_url() {\n\t\t$token = get_option( 'omnisend_connect_token', '' );\n\n\t\tif ( $token === '' ) {\n\t\t\t$token = hash( 'sha256', time() );\n\t\t\tupdate_option( 'omnisend_connect_token', $token );\n\t\t}\n\n---\n\n\u002F\u002F includes\u002Fomnisend-api.php (around line 170 in version 1.18.0)\nfunction validate_connect_token( WP_REST_Request $request ) {\n\t$body = json_decode( $request->get_body(), true );\n\n\tif ( ! isset( $body['connect_token'] ) ) {\n\t\treturn new WP_Error(\n\t\t\t'omnisend_missing_connect_token',\n\t\t\t'Missing connect token in request.',\n\t\t\tarray( 'status' => 400 )\n\t\t);\n\t}\n\n\t$token = get_option( 'omnisend_connect_token', '' );\n\n\tif ( $token === '' ) {\n\t\treturn new WP_Error(\n\t\t\t'omnisend_connect_denied',\n\t\t\t'Connect token is already used.',\n\t\t\tarray( 'status' => 403 )\n\t\t);\n\t}\n\n\tif ( $token !== $request['connect_token'] ) {\n\t\treturn new WP_Error(\n\t\t\t'omnisend_incorrect_connect_token',\n\t\t\t'Connect token is incorrect.',\n\t\t\tarray( 'status' => 401 )\n\t\t);\n\t}\n\n\treturn true;\n}","diff -ru \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fomnisend-connect\u002F1.18.0\u002Fincludes\u002Fclass-omnisend-install.php \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fomnisend-connect\u002F1.18.1\u002Fincludes\u002Fclass-omnisend-install.php\n--- \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fomnisend-connect\u002F1.18.0\u002Fincludes\u002Fclass-omnisend-install.php\t2025-10-09 11:58:08.000000000 +0000\n+++ \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fomnisend-connect\u002F1.18.1\u002Fincludes\u002Fclass-omnisend-install.php\t2026-04-15 12:40:58.000000000 +0000\n@@ -231,7 +231,7 @@\n \t\t$token = get_option( 'omnisend_connect_token', '' );\n \n \t\tif ( $token === '' ) {\n-\t\t\t$token = hash( 'sha256', time() );\n+\t\t\t$token = bin2hex( random_bytes( 32 ) );\n \t\t\tupdate_option( 'omnisend_connect_token', $token );\n \t\t}\n \n--- \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fomnisend-connect\u002F1.18.0\u002Fincludes\u002Fomnisend-api.php\t2025-10-09 11:58:08.000000000 +0000\n+++ \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fomnisend-connect\u002F1.18.1\u002Fincludes\u002Fomnisend-api.php\t2026-04-15 12:40:58.000000000 +0000\n@@ -147,34 +147,30 @@\n \t$body = json_decode( $request->get_body(), true );\n \n \tif ( ! isset( $body['connect_token'] ) ) {\n-\t\treturn new WP_Error(\n-\t\t\t'omnisend_missing_connect_token',\n-\t\t\t'Missing connect token in request.',\n-\t\t\tarray( 'status' => 400 )\n-\t\t);\n+\t\treturn omnisend_connect_token_invalid_error();\n \t}\n \n \t$token = get_option( 'omnisend_connect_token', '' );\n \n \tif ( $token === '' ) {\n-\t\treturn new WP_Error(\n-\t\t\t'omnisend_connect_denied',\n-\t\t\t'Connect token is already used.',\n-\t\t\tarray( 'status' => 403 )\n-\t\t);\n+\t\treturn omnisend_connect_token_invalid_error();\n \t}\n \n-\tif ( $token !== $request['connect_token'] ) {\n-\t\treturn new WP_Error(\n-\t\t\t'omnisend_incorrect_connect_token',\n-\t\t\t'Connect token is incorrect.',\n-\t\t\tarray( 'status' => 401 )\n-\t\t);\n+\tif ( ! hash_equals( $token, (string) $request['connect_token'] ) ) {\n+\t\treturn omnisend_connect_token_invalid_error();\n \t}\n \n \treturn true;\n }\n \n+function omnisend_connect_token_invalid_error() {\n+\treturn new WP_Error(\n+\t\t'omnisend_invalid_connect_token',\n+\t\t'Connect token is invalid.',\n+\t\tarray( 'status' => 403 )\n+\t);\n+}","The exploit targets the vulnerable OAuth connection process of the Omnisend plugin. \n\n1.  **Preparation**: The attacker identifies a target site where the administrator has initiated but not completed the Omnisend connection process (or triggers the process if possible). This populates the `omnisend_connect_token` option with a predictable value: `hash('sha256', time())`.\n2.  **Time Synchronization**: The attacker sends a request to the target site (e.g., `GET \u002F`) and captures the `Date` header to synchronize their local clock with the server's Unix timestamp.\n3.  **Brute-Force**: The attacker iterates through potential timestamps (typically backwards from the current time) and calculates the SHA256 hash for each second. \n4.  **Endpoint Hit**: For each candidate token, the attacker sends an unauthenticated `POST` request to `\u002Fwp-json\u002Fomnisend-api\u002Fv1\u002Fconnect` with a payload containing the candidate `connect_token`, an attacker-controlled `brand_id`, and an `omnisend_api_key`.\n5.  **Validation**: A response code of `401` indicates an incorrect token, while a `200 OK` response indicates a successful guess and account takeover. \n6.  **Takeover**: Once successful, the plugin replaces the legitimate connection settings with the attacker's credentials, rerouting all subsequent WooCommerce webhooks and customer data syncs to the attacker's infrastructure.","gemini-3-flash-preview","2026-05-14 17:36:44","2026-05-14 17:37:47",{"type":41,"vulnerable_version":42,"fixed_version":11,"vulnerable_browse":43,"vulnerable_zip":44,"fixed_browse":45,"fixed_zip":46,"all_tags":47},"plugin","1.18.0","https:\u002F\u002Fplugins.trac.wordpress.org\u002Fbrowser\u002Fomnisend-connect\u002Ftags\u002F1.18.0","https:\u002F\u002Fdownloads.wordpress.org\u002Fplugin\u002Fomnisend-connect.1.18.0.zip","https:\u002F\u002Fplugins.trac.wordpress.org\u002Fbrowser\u002Fomnisend-connect\u002Ftags\u002F1.18.1","https:\u002F\u002Fdownloads.wordpress.org\u002Fplugin\u002Fomnisend-connect.1.18.1.zip","https:\u002F\u002Fplugins.trac.wordpress.org\u002Fbrowser\u002Fomnisend-connect\u002Ftags"]