[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"$fAblL0gswnW09Jd-vuy6eM0WQnRXNpr6bM4jb3L88G4k":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":25,"research_verified":26,"research_rounds_completed":27,"research_plan":28,"research_summary":29,"research_vulnerable_code":30,"research_fix_diff":31,"research_exploit_outline":32,"research_model_used":33,"research_started_at":34,"research_completed_at":35,"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":26,"poc_model_used":9,"poc_verification_depth":9,"poc_exploit_code_gated":26,"source_links":36},"CVE-2026-4329","blackhole-for-bad-bots-unauthenticated-stored-cross-site-scripting-via-user-agent-http-header","Blackhole for Bad Bots \u003C= 3.8 - Unauthenticated Stored Cross-Site Scripting via User-Agent HTTP Header","The Blackhole for Bad Bots plugin for WordPress is vulnerable to Stored Cross-Site Scripting via the User-Agent HTTP header in all versions up to and including 3.8. This is due to insufficient input sanitization and output escaping. The plugin uses sanitize_text_field() when capturing bot data (which strips HTML tags but does not escape HTML entities like double quotes), then stores the data via update_option(). When an administrator views the Bad Bots log page, the stored data is output directly into HTML input value attributes (lines 75-83) without esc_attr() and into HTML span content without esc_html(). This makes it possible for unauthenticated attackers to inject arbitrary web scripts that execute when an administrator views the Blackhole Bad Bots admin page.","blackhole-bad-bots",null,"\u003C=3.8","3.8.1","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-03-25 00:00:00","2026-03-26 03:37:28",[19],"https:\u002F\u002Fwww.wordfence.com\u002Fthreat-intel\u002Fvulnerabilities\u002Fid\u002Fa71992e2-fdac-4e89-8867-4b771d9b4374?source=api-prod",1,[22,23,24],"blackhole.php","inc\u002Fbadbots-register.php","readme.txt","researched",false,3,"# Research Plan: Blackhole for Bad Bots Stored XSS\n\n## 1. Vulnerability Summary\nThe **Blackhole for Bad Bots** plugin (\u003C= 3.8) is vulnerable to **Unauthenticated Stored Cross-Site Scripting (XSS)** via the `User-Agent` HTTP header. \n\nThe plugin is designed to trap \"bad bots\" that ignore `robots.txt` rules and visit a hidden \"blackhole\" link. When a bot hits this link, the plugin captures its request metadata (IP, User-Agent, etc.) and stores it in the WordPress database (within the `bbb_badbots` option).\n\nThe vulnerability exists because:\n1.  **Insufficient Sanitization**: Captured `User-Agent` data is processed using `sanitize_text_field()`. While this function strips HTML tags, it **does not escape quotes or HTML entities**.\n2.  **Lack of Output Escaping**: When an administrator views the \"Bad Bots\" log in the WordPress dashboard (`wp-admin\u002Fadmin.php?page=blackhole_badbots`), the stored `User-Agent` is output directly into HTML `value` attributes of `input` tags (used for form persistence) and into `\u003Cspan>` content without using `esc_attr()` or `esc_html()`.\n\nThis allows an unauthenticated attacker to inject attributes into the `input` tags (e.g., `onfocus`) to execute arbitrary JavaScript when the administrator views the log.\n\n## 2. Attack Vector Analysis\n- **Endpoint**: Any public-facing page (to find the trap) and the \"blackhole\" trap URL itself.\n- **Trap URL**: Typically `\u002F?blackhole` or a randomized path added to `robots.txt`.\n- **HTTP Parameter**: `User-Agent` HTTP header.\n- **Authentication**: Unauthenticated (the bot is by definition an external visitor).\n- **Preconditions**:\n    - The plugin must be active.\n    - The attacker must identify the forbidden \"blackhole\" link path.\n\n## 3. Code Flow\n1.  **Entry Point**: `blackhole_scanner()` (registered in `blackhole.php` line 98) runs on the `init` hook for every request.\n2.  **Detection**: `blackhole_scanner` checks if the current request URI matches the \"blackhole\" trigger link.\n3.  **Data Capture**: If matched, the plugin gathers `$_SERVER['HTTP_USER_AGENT']`.\n4.  **Storage**: The plugin updates the `bbb_badbots` option.\n5.  **Sanitization Sink**: The `register_setting` for `bbb_badbots` (defined in `inc\u002Fbadbots-register.php` line 9) specifies `blackhole_validate_badbots` as the validation callback.\n6.  **Validation**: `blackhole_validate_badbots` (line 21) runs `sanitize_text_field()` on the `user_agent` field. \n    - Input: `EvilBot \" onfocus=\"alert(1)\" autofocus=\"`\n    - Output: `EvilBot \" onfocus=\"alert(1)\" autofocus=\"` (Quotes are preserved).\n7.  **Output Sink**: An administrator navigates to the Bad Bots page. The function `blackhole_callback_blocked_bots()` (line 64) is called.\n    - **Line 85**: `echo '\u003Cinput name=\"bbb_badbots['. $key .'][user_agent]\" type=\"hidden\" value=\"'. $user_agent .'\" \u002F> ';`\n    - The injected quote breaks out of the `value` attribute, and `autofocus` + `onfocus` executes the script.\n\n## 4. Nonce Acquisition Strategy\n**No nonce is required.** \nThe vulnerability is triggered by an unauthenticated GET request to the \"blackhole\" trap URL. The `blackhole_scanner()` logic on `init` does not perform nonce verification because its purpose is to catch automated bots which would not possess a WordPress nonce.\n\n## 5. Exploitation Strategy\n1.  **Identify the Trap**:\n    - Navigate to the site's homepage using `browser_navigate`.\n    - Check the HTML source for a hidden link (usually in the footer, as per `blackhole.php` lines 96-97).\n    - Alternatively, check `\u002Frobots.txt` for a `Disallow` rule pointing to a blackhole path.\n2.  **Submit Payload**:\n    - Send an HTTP GET request to the identified trap URL using `http_request`.\n    - Set the `User-Agent` header to a payload that breaks out of an HTML attribute.\n3.  **Payload Design**:\n    - Since `sanitize_text_field` is used, we cannot use `\u003Cscript>` tags.\n    - We must use attribute breakout: `EvilBot \" onfocus=\"alert(1)\" autofocus=\"`\n    - Note: Avoid the strings \"googlebot\", \"bingbot\", \"chrome\", or \"wordpress\" as they are whitelisted (see `blackhole.php` line 182).\n4.  **Trigger Execution**:\n    - Log in to the WordPress admin panel.\n    - Navigate to `http:\u002F\u002Flocalhost:8080\u002Fwp-admin\u002Fadmin.php?page=blackhole_badbots`.\n    - The `input` tag will render as: `\u003Cinput ... value=\"EvilBot \" onfocus=\"alert(1)\" autofocus=\"\" \u002F>`.\n    - The `autofocus` attribute will trigger `onfocus` immediately upon page load in modern browsers.\n\n## 6. Test Data Setup\n1.  **Plugin Installation**: Ensure \"Blackhole for Bad Bots\" version 3.8 is installed and activated.\n2.  **Configuration**: The plugin defaults are usually sufficient.\n3.  **Admin User**: A standard administrator account to view the logs.\n4.  **Shortcode\u002FPage**: No specific shortcode is needed to trigger the vulnerability, as the trap is typically appended to the footer of all pages by `blackhole_trigger` on `wp_footer`.\n\n## 7. Expected Results\n- The malicious request is logged by the plugin.\n- The `bbb_badbots` option in the `wp_options` table contains the payload string with quotes intact.\n- Upon visiting the Bad Bots admin page, a JavaScript `alert(1)` (or equivalent) executes in the context of the administrator session.\n\n## 8. Verification Steps\n1.  **Check Database via WP-CLI**:\n    ```bash\n    wp option get bbb_badbots --format=json\n    ```\n    Verify that the `user_agent` field for one of the entries contains the raw payload string.\n2.  **Check HTML Output via HTTP Request (Admin Session)**:\n    - Perform an authenticated request to `\u002Fwp-admin\u002Fadmin.php?page=blackhole_badbots`.\n    - Inspect the response body for the unescaped payload:\n    ```html\n    \u003Cinput name=\"bbb_badbots[1][user_agent]\" type=\"hidden\" value=\"EvilBot \" onfocus=\"alert(1)\" autofocus=\"\" \u002F>\n    ```\n\n## 9. Alternative Approaches\nIf `autofocus` is filtered or blocked by the browser, use a payload that creates a larger invisible overlay to trigger on mouse movement:\n- **Payload**: `EvilBot \" style=\"position:fixed;top:0;left:0;width:100%;height:100%;display:block;\" onmouseover=\"alert(1)`\n- If `sanitize_text_field` interferes with the `style` attribute, try `onmouseenter` or `onclick` on the hidden input field, though `onfocus` with `autofocus` is the most reliable for hidden inputs. \n- Note that even though the input is `type=\"hidden\"`, most browsers will still honor `autofocus` and `onfocus` if they are manually injected into the DOM as attributes. If `type=\"hidden\"` prevents focus, we can attempt to inject `type=\"text\"` to overwrite the earlier `type` attribute (if the browser allows attribute precedence for the first occurrence).","The Blackhole for Bad Bots plugin for WordPress is vulnerable to Unauthenticated Stored Cross-Site Scripting (XSS) due to insufficient sanitization and output escaping of the User-Agent HTTP header. Attackers can trigger the vulnerability by visiting the site's hidden \"blackhole\" trap URL with a crafted User-Agent string, which then executes malicious JavaScript in the browser of an administrator viewing the Bad Bots log page.","\u002F\u002F inc\u002Fbadbots-register.php:21\nfunction blackhole_validate_badbots($bots) {\n\t\n\tforeach ($bots as $key => $val) {\n\t\t\n\t\t$bots[$key]['ip_address']   = isset($bots[$key]['ip_address'])   ? sanitize_text_field($bots[$key]['ip_address'])   : null;\n\t\t$bots[$key]['request_uri']  = isset($bots[$key]['request_uri'])  ? sanitize_text_field($bots[$key]['request_uri'])  : null;\n\t\t$bots[$key]['remote_host']  = isset($bots[$key]['remote_host'])  ? sanitize_text_field($bots[$key]['remote_host'])  : null;\n\t\t$bots[$key]['query_string'] = isset($bots[$key]['query_string']) ? sanitize_text_field($bots[$key]['query_string']) : null;\n\t\t$bots[$key]['user_agent']   = isset($bots[$key]['user_agent'])   ? sanitize_text_field($bots[$key]['user_agent'])   : null;\n\t\t$bots[$key]['referrer']     = isset($bots[$key]['referrer'])     ? sanitize_text_field($bots[$key]['referrer'])     : null;\n\t\t$bots[$key]['protocol']     = isset($bots[$key]['protocol'])     ? sanitize_text_field($bots[$key]['protocol'])     : null;\n\t\t$bots[$key]['method']       = isset($bots[$key]['method'])       ? sanitize_text_field($bots[$key]['method'])       : null;\n\t\t$bots[$key]['date']         = isset($bots[$key]['date'])         ? sanitize_text_field($bots[$key]['date'])         : null;\n\t\t\n\t}\n\t\n\treturn $bots;\n}\n\n---\n\n\u002F\u002F inc\u002Fbadbots-register.php:75\n\t\techo '\u003Cinput name=\"bbb_badbots['. $key .'][ip_address]\"   type=\"hidden\" value=\"'. $ip_address .'\" \u002F> ';\n\t\techo '\u003Cinput name=\"bbb_badbots['. $key .'][request_uri]\"  type=\"hidden\" value=\"'. $request_uri .'\" \u002F> ';\n\t\techo '\u003Cinput name=\"bbb_badbots['. $key .'][remote_host]\"  type=\"hidden\" value=\"'. $remote_host .'\" \u002F> ';\n\t\techo '\u003Cinput name=\"bbb_badbots['. $key .'][query_string]\" type=\"hidden\" value=\"'. $query_string .'\" \u002F> ';\n\t\techo '\u003Cinput name=\"bbb_badbots['. $key .'][user_agent]\"   type=\"hidden\" value=\"'. $user_agent .'\" \u002F> ';\n\t\techo '\u003Cinput name=\"bbb_badbots['. $key .'][referrer]\"     type=\"hidden\" value=\"'. $referrer .'\" \u002F> ';\n\t\techo '\u003Cinput name=\"bbb_badbots['. $key .'][protocol]\"     type=\"hidden\" value=\"'. $protocol .'\" \u002F> ';\n\t\techo '\u003Cinput name=\"bbb_badbots['. $key .'][method]\"       type=\"hidden\" value=\"'. $method .'\" \u002F> ';\n\t\techo '\u003Cinput name=\"bbb_badbots['. $key .'][date]\"         type=\"hidden\" value=\"'. $date .'\" \u002F> ';\n\t\t\n\t\t$data = '\u003Cstrong>'. $date .'\u003C\u002Fstrong> - '. $ip_address .' - '. $protocol .' - \u003Cspan class=\"bbb-user-agent\">'. $user_agent . '\u003C\u002Fspan>';","diff -ru \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fblackhole-bad-bots\u002F3.8\u002Fblackhole.php \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fblackhole-bad-bots\u002F3.8.1\u002Fblackhole.php\n--- \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fblackhole-bad-bots\u002F3.8\u002Fblackhole.php\t2026-02-06 22:29:52.000000000 +0000\n+++ \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fblackhole-bad-bots\u002F3.8.1\u002Fblackhole.php\t2026-03-20 16:43:10.000000000 +0000\n@@ -114,7 +114,7 @@\n \t\t\n \t\tprivate function constants() {\n \t\t\tif (!defined('BBB_REQUIRE')) define('BBB_REQUIRE', '4.7');\n-\t\t\tif (!defined('BBB_VERSION')) define('BBB_VERSION', '3.8');\n+\t\t\tif (!defined('BBB_VERSION')) define('BBB_VERSION', '3.8.1');\n \t\t\tif (!defined('BBB_NAME'))    define('BBB_NAME',    'Blackhole for Bad Bots');\n \t\t\tif (!defined('BBB_AUTHOR'))  define('BBB_AUTHOR',  'Jeff Starr');\n \t\t\tif (!defined('BBB_HOME'))    define('BBB_HOME',    'https:\u002F\u002Fperishablepress.com\u002Fblackhole-bad-bots\u002F');\ndiff -ru \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fblackhole-bad-bots\u002F3.8\u002Finc\u002Fbadbots-register.php \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fblackhole-bad-bots\u002F3.8.1\u002Finc\u002Fbadbots-register.php\n--- \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fblackhole-bad-bots\u002F3.8\u002Finc\u002Fbadbots-register.php\t2023-03-07 00:20:46.000000000 +0000\n+++ \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fblackhole-bad-bots\u002F3.8.1\u002Finc\u002Fbadbots-register.php\t2026-03-20 16:43:10.000000000 +0000\n@@ -72,17 +72,17 @@\n \t\t$method       = isset($val['method'])       ? $val['method']       : '';\n \t\t$date         = isset($val['date'])         ? $val['date']         : '';\n \t\t\n-\t\techo '\u003Cinput name=\"bbb_badbots['. $key .'][ip_address]\"   type=\"hidden\" value=\"'. $ip_address .'\" \u002F> ';\n-\t\techo '\u003Cinput name=\"bbb_badbots['. $key .'][request_uri]\"  type=\"hidden\" value=\"'. $request_uri .'\" \u002F> ';\n-\t\techo '\u003Cinput name=\"bbb_badbots['. $key .'][remote_host]\"  type=\"hidden\" value=\"'. $remote_host .'\" \u002F> ';\n-\t\techo '\u003Cinput name=\"bbb_badbots['. $key .'][query_string]\" type=\"hidden\" value=\"'. $query_string .'\" \u002F> ';\n-\t\techo '\u003Cinput name=\"bbb_badbots['. $key .'][user_agent]\"   type=\"hidden\" value=\"'. $user_agent .'\" \u002F> ';\n-\t\techo '\u003Cinput name=\"bbb_badbots['. $key .'][referrer]\"     type=\"hidden\" value=\"'. $referrer .'\" \u002F> ';\n-\t\techo '\u003Cinput name=\"bbb_badbots['. $key .'][protocol]\"     type=\"hidden\" value=\"'. $protocol .'\" \u002F> ';\n-\t\techo '\u003Cinput name=\"bbb_badbots['. $key .'][method]\"       type=\"hidden\" value=\"'. $method .'\" \u002F> ';\n-\t\techo '\u003Cinput name=\"bbb_badbots['. $key .'][date]\"         type=\"hidden\" value=\"'. $date .'\" \u002F> ';\n+\t\techo '\u003Cinput name=\"bbb_badbots['. $key .'][ip_address]\"   type=\"hidden\" value=\"'. esc_attr($ip_address) .'\" \u002F> ';\n+\t\techo '\u003Cinput name=\"bbb_badbots['. $key .'][request_uri]\"  type=\"hidden\" value=\"'. esc_attr($request_uri) .'\" \u002F> ';\n+\t\techo '\u003Cinput name=\"bbb_badbots['. $key .'][remote_host]\"  type=\"hidden\" value=\"'. esc_attr($remote_host) .'\" \u002F> ';\n+\t\techo '\u003Cinput name=\"bbb_badbots['. $key .'][query_string]\" type=\"hidden\" value=\"'. esc_attr($query_string) .'\" \u002F> ';\n+\t\techo '\u003Cinput name=\"bbb_badbots['. $key .'][user_agent]\"   type=\"hidden\" value=\"'. esc_attr($user_agent) .'\" \u002F> ';\n+\t\techo '\u003Cinput name=\"bbb_badbots['. $key .'][referrer]\"     type=\"hidden\" value=\"'. esc_attr($referrer) .'\" \u002F> ';\n+\t\techo '\u003Cinput name=\"bbb_badbots['. $key .'][protocol]\"     type=\"hidden\" value=\"'. esc_attr($protocol) .'\" \u002F> ';\n+\t\techo '\u003Cinput name=\"bbb_badbots['. $key .'][method]\"       type=\"hidden\" value=\"'. esc_attr($method) .'\" \u002F> ';\n+\t\techo '\u003Cinput name=\"bbb_badbots['. $key .'][date]\"         type=\"hidden\" value=\"'. esc_attr($date) .'\" \u002F> ';\n \t\t\n-\t\t$data = '\u003Cstrong>'. $date .'\u003C\u002Fstrong> - '. $ip_address .' - '. $protocol .' - \u003Cspan class=\"bbb-user-agent\">'. $user_agent . '\u003C\u002Fspan>';\n+\t\t$data = '\u003Cstrong>'. esc_html($date) .'\u003C\u002Fstrong> - '. esc_html($ip_address) .' - '. esc_html($protocol) .' - \u003Cspan class=\"bbb-user-agent\">'. esc_html($user_agent) . '\u003C\u002Fspan>';","1. Identify the hidden \"blackhole\" trap URL on the target WordPress site (found either in the robots.txt file or as a hidden link in the page source).\n2. Send an unauthenticated GET request to the trap URL.\n3. Include a malicious User-Agent header in the request (e.g., `\" onfocus=\"alert(1)\" autofocus=\"`).\n4. The plugin captures this header, passes it through `sanitize_text_field` (which preserves quotes), and saves it to the `bbb_badbots` WordPress option.\n5. Wait for an administrator to view the \"Bad Bots\" log page in the WordPress admin dashboard (`\u002Fwp-admin\u002Fadmin.php?page=blackhole_badbots`).\n6. The payload breaks out of the HTML `value` attribute of a hidden input tag, injecting the `onfocus` and `autofocus` attributes, which execute the JavaScript immediately upon page load.","gemini-3-flash-preview","2026-04-17 22:58:02","2026-04-17 22:58:34",{"type":37,"vulnerable_version":38,"fixed_version":11,"vulnerable_browse":39,"vulnerable_zip":40,"fixed_browse":41,"fixed_zip":42,"all_tags":43},"plugin","3.8","https:\u002F\u002Fplugins.trac.wordpress.org\u002Fbrowser\u002Fblackhole-bad-bots\u002Ftags\u002F3.8","https:\u002F\u002Fdownloads.wordpress.org\u002Fplugin\u002Fblackhole-bad-bots.3.8.zip","https:\u002F\u002Fplugins.trac.wordpress.org\u002Fbrowser\u002Fblackhole-bad-bots\u002Ftags\u002F3.8.1","https:\u002F\u002Fdownloads.wordpress.org\u002Fplugin\u002Fblackhole-bad-bots.3.8.1.zip","https:\u002F\u002Fplugins.trac.wordpress.org\u002Fbrowser\u002Fblackhole-bad-bots\u002Ftags"]