[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"$f-6b6A-SMk1Sgf1JqhSWg0NdkVMaibcui-REZ4f1pjtM":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-6177","custom-twitter-feeds-unauthenticated-stored-cross-site-scripting-via-cached-tweet-text","Custom Twitter Feeds \u003C= 2.5.4 - Unauthenticated Stored Cross-Site Scripting via Cached Tweet Text","The Custom Twitter Feeds plugin for WordPress is vulnerable to Stored Cross-Site Scripting in versions up to and including 2.5.4. This is due to insufficient output escaping in the CTF_Display_Elements::get_post_text() function when rendering cached tweet text. The plugin's ctf_get_more_posts AJAX action is available to unauthenticated users and directly outputs cached tweet data through nl2br() without HTML escaping. When an attacker can get malicious content into cached tweet data (either by tweeting content that gets cached by the site's feed configuration, or through other vulnerabilities), the malicious HTML\u002FJavaScript is executed when the unauthenticated endpoint is accessed. This makes it possible for unauthenticated attackers to inject arbitrary web scripts in pages that will execute whenever a user accesses the affected endpoint.","custom-twitter-feeds",null,"\u003C=2.5.4","2.5.5","high",7.2,"CVSS:3.1\u002FAV:N\u002FAC:L\u002FPR:N\u002FUI:N\u002FS:C\u002FC:L\u002FI:L\u002FA:N","Improper Neutralization of Input During Web Page Generation ('Cross-site Scripting')","2026-05-12 00:00:00","2026-05-13 12:29:56",[19],"https:\u002F\u002Fwww.wordfence.com\u002Fthreat-intel\u002Fvulnerabilities\u002Fid\u002Fe4b2178f-e4da-4bfb-9c27-8c1884499769?source=api-prod",2,[22,23,24,25,26,27,28,29],"README.txt","admin\u002Fbuilder\u002Fassets\u002Fjs\u002Fbuilder.js","custom-twitter-feed.php","inc\u002FAdmin\u002FCTF_Global_Settings.php","inc\u002FBuilder\u002FCTF_Feed_Saver.php","inc\u002FBuilder\u002FTabs\u002FCTF_Builder_Customizer_Tab.php","inc\u002FCTF_Display_Elements.php","vendor\u002Fcomposer\u002Finstalled.php","researched",false,3,"# Vulnerability Research Plan: CVE-2026-6177\n\n## 1. Vulnerability Summary\n**Vulnerability:** Unauthenticated Stored Cross-Site Scripting (XSS)\n**Plugin:** Custom Twitter Feeds (slug: `custom-twitter-feeds`)\n**Affected Versions:** \u003C= 2.5.4\n**Sink:** `CTF_Display_Elements::get_post_text()` in `inc\u002FCTF_Display_Elements.php`\n**Issue:** The plugin fails to escape tweet content retrieved from its local cache before rendering it via an unauthenticated AJAX endpoint. While the source is theoretically Twitter\u002FX API, the plugin's reliance on `nl2br()` instead of proper WordPress escaping functions like `esc_html()` or `wp_kses_post()` allows any HTML stored in the `ctf_posts` table to be executed in the browser.\n\n## 2. Attack Vector Analysis\n- **Endpoint:** `\u002Fwp-admin\u002Fadmin-ajax.php`\n- **AJAX Action:** `ctf_get_more_posts` (Registered for both `wp_ajax_` and `wp_ajax_nopriv_`)\n- **Vulnerable Parameter:** The payload is retrieved from the database (Stored XSS). The trigger is the AJAX request.\n- **Authentication:** Unauthenticated.\n- **Preconditions:** \n    1. A Twitter feed must be configured or a dummy feed created.\n    2. Malicious HTML must be present in the `twitter_text` (or equivalent) column of the `ctf_posts` table.\n    3. A valid AJAX nonce must be provided (if enforced).\n\n## 3. Code Flow\n1. **Entry Point:** An unauthenticated user sends a POST request to `admin-ajax.php` with `action=ctf_get_more_posts`.\n2. **AJAX Handler:** The handler (likely in `inc\u002Fctf-functions.php`) identifies the feed and queries the `ctf_posts` table (defined by the constant `CTF_POSTS_TABLE`) for the next batch of tweets.\n3. **Data Retrieval:** The tweet text is fetched from the database.\n4. **Sink Call:** The plugin iterates through the tweets and calls `CTF_Display_Elements::get_post_text($post_data, $feed_options)`.\n5. **Rendering:** `get_post_text()` processes the tweet text using `nl2br()` to preserve line breaks but fails to call `esc_html()`.\n6. **Output:** The raw, unescaped HTML is echoed back in the AJAX response (usually as part of a JSON object containing the HTML for the new posts).\n\n## 4. Nonce Acquisition Strategy\nThe plugin enqueues a frontend script (`ctf-scripts.min.js`) which contains the AJAX logic and nonce.\n\n1. **Shortcode:** The primary shortcode is `[custom-twitter-feeds]`.\n2. **Setup:** Create a public page containing this shortcode.\n3. **Navigation:** Use the `browser_navigate` tool to visit this page.\n4. **Extraction:** The nonce and AJAX URL are typically localized in the `ctf_script` global variable.\n   - **Command:** `browser_eval(\"window.ctf_script?.nonce\")`\n   - **Alternative:** `browser_eval(\"window.ctf_script?.ajax_url\")`\n   - (Note: Based on previous versions of Smash Balloon plugins, the key is usually `nonce` within the `ctf_script` object).\n\n## 5. Exploitation Strategy\nTo demonstrate the vulnerability in a controlled environment, we will manually inject a payload into the plugin's cache table to simulate a malicious tweet being ingested.\n\n### Step 1: Inject Payload into Database\nSince the vulnerability is \"Stored,\" we need the payload in the DB. Use WP-CLI to insert a record into the `ctf_posts` table.\n```bash\n# Verify table name first\nwp db query \"SHOW TABLES LIKE '%ctf_posts%';\"\n\n# Insert malicious tweet text\n# 'id_str' and 'text_data' are typical columns for this plugin\nwp db query \"INSERT INTO wp_ctf_posts (twitter_id, twitter_text, created_at) VALUES ('12345', '\u003Cimg src=x onerror=alert(\\\"XSS_PROVEN\\\")>', NOW());\"\n```\n\n### Step 2: Trigger the Vulnerable Endpoint\nSend the AJAX request using the `http_request` tool.\n\n- **URL:** `http:\u002F\u002Flocalhost:8080\u002Fwp-admin\u002Fadmin-ajax.php`\n- **Method:** POST\n- **Headers:** `Content-Type: application\u002Fx-www-form-urlencoded`\n- **Body Parameters:**\n    - `action`: `ctf_get_more_posts`\n    - `feed_id`: `1` (or the ID of the feed created in setup)\n    - `ctf_nonce`: `[EXTRACTED_NONCE]`\n    - `num`: `5`\n    - `offset`: `0`\n\n## 6. Test Data Setup\n1. **Activate Plugin:** Ensure `custom-twitter-feeds` is active.\n2. **Create a Feed:**\n   ```bash\n   # Use the plugin's internal method to create a basic feed if needed, \n   # or simply ensure the ctf_posts table exists by visiting the admin dashboard.\n   ```\n3. **Publish a Page:**\n   ```bash\n   wp post create --post_type=page --post_title=\"Twitter Feed\" --post_status=publish --post_content='[custom-twitter-feeds]'\n   ```\n4. **Populate Table:** Manually insert the XSS payload into the `ctf_posts` table as described in the Exploitation Strategy.\n\n## 7. Expected Results\n- The AJAX response will return a JSON object.\n- Within the `html` or `output` key of that JSON, the string `\u003Cimg src=x onerror=alert(\"XSS_PROVEN\")>` (or its `nl2br` version) will be present literally.\n- Specifically, it will **not** be converted to `&lt;img ... &gt;`.\n- When rendered in a browser context, the `onerror` event will fire.\n\n## 8. Verification Steps\n1. **HTTP Verification:** Inspect the body of the `http_request` response.\n   - Search for the raw string: `onerror=alert`\n2. **DOM Verification:** Use `browser_navigate` to the page with the shortcode.\n   - Click the \"Load More\" button (if available) or trigger the AJAX call via `browser_eval`.\n   - Use `browser_eval(\"document.body.innerHTML\")` to check for the presence of the injected `\u003Cimg>` tag.\n\n## 9. Alternative Approaches\n- **Feed Type Manipulation:** If `ctf_get_more_posts` behaves differently for different feed types (e.g., `hashtag` vs `usertimeline`), try setting the `type` parameter in the AJAX request.\n- **Cache Table Variants:** Depending on the specific version, the table might be `wp_ctf_posts` or data might be stored in `wp_options` as a transient. If the `ctf_posts` table is empty, check for transients:\n  ```bash\n  wp transient list | grep ctf_\n  ```\n- **Direct Output:** Some versions might render the initial feed load (not just \"Load More\") using the same vulnerable function. If so, visiting the page with the shortcode directly after DB injection may trigger the XSS.","The Custom Twitter Feeds plugin for WordPress is vulnerable to unauthenticated Stored Cross-Site Scripting via cached tweet text. This occurs because the `CTF_Display_Elements::get_post_text()` function fails to escape HTML content retrieved from the database cache, instead only processing it through `nl2br()`. An unauthenticated attacker can exploit this by ensuring malicious script content is stored in the tweet cache, which is then executed when the `ctf_get_more_posts` AJAX action is triggered.","\u002F\u002F inc\u002FCTF_Display_Elements.php\n\n\u002F\u002F Line 502 (Approximate)\n                \u003Cp class=\"ctf-tweet-text\">\n                    \u003C?php echo nl2br( $post_text ) ?>\n                    \u003C?php\n                        if(!$feed_options['is_legacy'] || ($feed_options['is_legacy'] && ctf_show( 'placeholder', $feed_options ))){\n                            echo $post_media_text;\n                        }\n                    ?>\n                \u003C\u002Fp>\n\n---\n\n\u002F\u002F Line 518 (Approximate)\n            \u003Ca class=\"ctf-tweet-text-link\" \u003C?php echo $text_and_link_attr; ?> href=\"\u003C?php echo esc_url( 'https:\u002F\u002Ftwitter.com\u002F' . $author_screen_name . '\u002Fstatus\u002F' .$post_id ) ?>\" target = \"_blank\" rel = \"noopener noreferrer\">\n                \u003Cp class=\"ctf-tweet-text\" \u003C?php echo $post_text_attr; ?>>\u003C\u002Fp>\n            \u003C\u002Fa>\n            \u003Cp class=\"ctf-tweet-text\" \u003C?php echo $text_no_link_attr; ?> \u003C?php echo $post_text_attr; ?>>\u003C?php echo nl2br( $post_text ) ?>\u003C\u002Fp>","diff -ru \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fcustom-twitter-feeds\u002F2.5.4\u002Finc\u002FCTF_Display_Elements.php \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fcustom-twitter-feeds\u002F2.5.5\u002Finc\u002FCTF_Display_Elements.php\n--- \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fcustom-twitter-feeds\u002F2.5.4\u002Finc\u002FCTF_Display_Elements.php\t2026-03-12 18:47:50.000000000 +0000\n+++ \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fcustom-twitter-feeds\u002F2.5.5\u002Finc\u002FCTF_Display_Elements.php\t2026-04-30 13:44:54.000000000 +0000\n@@ -502,7 +502,7 @@\n                 \u003Ca class=\"ctf-tweet-text-link\" href=\"\u003C?php echo esc_url( 'https:\u002F\u002Ftwitter.com\u002F' . $author_screen_name . '\u002Fstatus\u002F' .$post_id ) ?>\" target = \"_blank\" rel = \"noopener noreferrer\">\n             \u003C?php } ?>\n                 \u003Cp class=\"ctf-tweet-text\">\n-                    \u003C?php echo nl2br( $post_text ) ?>\n+                    \u003C?php echo wp_kses_post( nl2br( $post_text ) ) ?>\n                     \u003C?php\n                         if(!$feed_options['is_legacy'] || ($feed_options['is_legacy'] && ctf_show( 'placeholder', $feed_options ))){\n                             echo $post_media_text;\n@@ -518,7 +518,7 @@\n             \u003Ca class=\"ctf-tweet-text-link\" \u003C?php echo $text_and_link_attr; ?> href=\"\u003C?php echo esc_url( 'https:\u002F\u002Ftwitter.com\u002F' . $author_screen_name . '\u002Fstatus\u002F' .$post_id ) ?>\" target = \"_blank\" rel = \"noopener noreferrer\">\n                 \u003Cp class=\"ctf-tweet-text\" \u003C?php echo $post_text_attr; ?>>\u003C\u002Fp>\n             \u003C\u002Fa>\n-            \u003Cp class=\"ctf-tweet-text\" \u003C?php echo $text_no_link_attr; ?> \u003C?php echo $post_text_attr; ?>>\u003C?php echo nl2br( $post_text ) ?>\u003C\u002Fp>\n+            \u003Cp class=\"ctf-tweet-text\" \u003C?php echo $text_no_link_attr; ?> \u003C?php echo $post_text_attr; ?>>\u003C?php echo wp_kses_post( nl2br( $post_text ) ) ?>\u003C\u002Fp>\n             \u003C?php\n                 if(!$feed_options['is_legacy'] || ($feed_options['is_legacy'] && ctf_show( 'placeholder', $feed_options ))){\n                     echo $post_media_text;","The exploit requires a payload to be stored in the plugin's local tweet cache (typically the `ctf_posts` table) and then triggered via an AJAX request.\n\n1. **Precondition:** The attacker ensures malicious HTML\u002FJavaScript (e.g., `\u003Cimg src=x onerror=alert(1)>`) is ingested into the `ctf_posts` database table. This can be achieved if the site follows a Twitter account controlled by the attacker or through any mechanism that populates the cache.\n2. **Nonce Retrieval:** The attacker visits the site's frontend where a Twitter feed is displayed to extract the `ctf_nonce` and `feed_id` from the localized `ctf_script` JavaScript object.\n3. **Endpoint Trigger:** The attacker sends an unauthenticated POST request to `\u002Fwp-admin\u002Fadmin-ajax.php` with the action `ctf_get_more_posts`, the extracted nonce, and the target feed ID.\n4. **Execution:** The server retrieves the malicious payload from the database and returns it within a JSON response. The plugin's failure to use `esc_html` or `wp_kses_post` in the rendering logic for this AJAX endpoint causes the browser to execute the script when it is injected into the DOM.","gemini-3-flash-preview","2026-05-14 18:15:47","2026-05-14 18:16:32",{"type":42,"vulnerable_version":43,"fixed_version":11,"vulnerable_browse":44,"vulnerable_zip":45,"fixed_browse":46,"fixed_zip":47,"all_tags":48},"plugin","2.5.4","https:\u002F\u002Fplugins.trac.wordpress.org\u002Fbrowser\u002Fcustom-twitter-feeds\u002Ftags\u002F2.5.4","https:\u002F\u002Fdownloads.wordpress.org\u002Fplugin\u002Fcustom-twitter-feeds.2.5.4.zip","https:\u002F\u002Fplugins.trac.wordpress.org\u002Fbrowser\u002Fcustom-twitter-feeds\u002Ftags\u002F2.5.5","https:\u002F\u002Fdownloads.wordpress.org\u002Fplugin\u002Fcustom-twitter-feeds.2.5.5.zip","https:\u002F\u002Fplugins.trac.wordpress.org\u002Fbrowser\u002Fcustom-twitter-feeds\u002Ftags"]