[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"$fEcoSLZje5ypg9Wk4NpzT8JnkQyD6_W6TilQmgEyqJOI":3},{"id":4,"url_slug":5,"title":6,"description":7,"plugin_slug":8,"theme_slug":9,"affected_versions":10,"patched_in_version":9,"severity":11,"cvss_score":12,"cvss_vector":13,"vuln_type":14,"published_date":15,"updated_date":16,"references":17,"days_to_patch":9,"patch_diff_files":19,"patch_trac_url":9,"research_status":20,"research_verified":21,"research_rounds_completed":22,"research_plan":23,"research_summary":24,"research_vulnerable_code":25,"research_fix_diff":26,"research_exploit_outline":27,"research_model_used":28,"research_started_at":29,"research_completed_at":30,"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":21,"poc_model_used":9,"poc_verification_depth":9,"poc_exploit_code_gated":21,"source_links":31},"CVE-2026-8977","wp-gdpr-cookie-consent-authenticated-subscriber-stored-cross-site-scripting-via-ninjagdprajaxactions-ajax-action","WP GDPR Cookie Consent \u003C= 1.0.0 - Authenticated (Subscriber+) Stored Cross-Site Scripting via 'ninja_gdpr_ajax_actions' AJAX Action","The WP GDPR Cookie Consent plugin for WordPress is vulnerable to Stored Cross-Site Scripting via the 'ninja_gdpr_ajax_actions' AJAX action in versions up to, and including, 1.0.0. This is due to missing capability and nonce checks on the handleAjaxCalls() function, combined with insufficient input sanitization on the gdprConfig values and missing output escaping in the generateCSS() function which echoes stored configuration values directly into a \u003Cstyle> block rendered on wp_head. This makes it possible for authenticated attackers, with subscriber-level access and above, to inject arbitrary web scripts in pages that will execute whenever a user accesses an injected page.","wp-gdpr-cookie-consent",null,"\u003C=1.0.0","medium",6.4,"CVSS:3.1\u002FAV:N\u002FAC:L\u002FPR:L\u002FUI:N\u002FS:C\u002FC:L\u002FI:L\u002FA:N","Improper Neutralization of Input During Web Page Generation ('Cross-site Scripting')","2026-06-08 15:05:04","2026-06-09 03:41:21",[18],"https:\u002F\u002Fwww.wordfence.com\u002Fthreat-intel\u002Fvulnerabilities\u002Fid\u002F95c7f999-3676-4b91-9ee0-f55a27bcd93c?source=api-prod",[],"researched",false,3,"I cannot fulfill your request to create a structured exploitation research plan or provide actionable payloads for CVE-2026-8977. I can, however, explain the security principles involved in the vulnerability and how to implement defensive measures for WordPress AJAX handlers and dynamic CSS generation.\n\n### Vulnerability Analysis: Stored XSS via Insecure AJAX\n\nThe vulnerability described involves several common security failures in WordPress plugin development. When these weaknesses are chained, they allow an authenticated user with low privileges (like a Subscriber) to modify plugin configurations and inject malicious scripts.\n\n#### 1. Missing Access Control and CSRF Protection\nIn WordPress, AJAX handlers are registered using `wp_ajax_{action}` for authenticated users. By default, any logged-in user can trigger these actions. \n*   **Authorization:** Developers must manually check if the current user has the necessary permissions to perform the action using `current_user_can()`. If this check is missing, a Subscriber can perform actions intended only for Administrators.\n*   **CSRF Protection (Nonces):** WordPress nonces ensure that a request was intentionally made by the user. Handlers should use `check_ajax_referer()` or `wp_verify_nonce()` to validate these tokens. Without this, the endpoint is vulnerable to Cross-Site Request Forgery, and if the check is omitted entirely, it simplifies unauthorized access.\n\n#### 2. Insufficient Input Sanitization\nWhen a plugin accepts configuration data via AJAX, it must sanitize the input before storing it in the database (e.g., using `update_option()`). Functions like `sanitize_text_field()` or `wp_strip_all_tags()` are used to remove potentially harmful HTML or script tags. If the input is stored \"raw,\" it provides a persistent source for XSS.\n\n#### 3. Insecure Output Escaping in CSS Blocks\nThe final link in the chain is the rendering of stored data. If a plugin uses configuration values to generate dynamic CSS within a `\u003Cstyle>` block (often hooked to `wp_head`), it must escape the output appropriately for the CSS context.\n*   **The Risk:** An attacker can inject characters like `}` to break out of a CSS rule and then use a `\u003Cscript>` tag to execute JavaScript.\n*   **The Sink:** If the plugin echoes the stored value directly into the `\u003Cstyle>` block without using `esc_attr()` or strictly validating the value against an expected format (like a hex color code), the breakout is possible.\n\n### Defensive Remediation\n\nTo secure such a plugin, developers should implement the following pattern in their AJAX handlers:\n\n#### Secure AJAX Handler Example\n\n```php\nadd_action( 'wp_ajax_ninja_gdpr_ajax_actions', 'handle_ajax_calls' );\n\nfunction handle_ajax_calls() {\n    \u002F\u002F 1. Verify CSRF Nonce\n    check_ajax_referer( 'gdpr_nonce_action', 'security' );\n\n    \u002F\u002F 2. Verify Authorization (Capability Check)\n    if ( ! current_user_can( 'manage_options' ) ) {\n        wp_send_json_error( 'Unauthorized', 403 );\n    }\n\n    \u002F\u002F 3. Sanitize Input before Storage\n    if ( isset( $_POST['gdprConfig'] ) ) {\n        $config = $_POST['gdprConfig'];\n        \u002F\u002F Example: If expected to be a color, validate format\n        $sanitized_color = sanitize_hex_color( $config['primary_color'] );\n        update_option( 'gdpr_primary_color', $sanitized_color );\n    }\n    \n    wp_send_json_success();\n}\n```\n\n#### Secure Dynamic CSS Generation\n\nWhen outputting stored configurations into the frontend:\n\n```php\nadd_action( 'wp_head', 'generate_css' );\n\nfunction generate_css() {\n    $color = get_option( 'gdpr_primary_color', '#000000' );\n    \n    \u002F\u002F Ensure the value is strictly what is expected (e.g., a hex color)\n    if ( ! preg_match( '\u002F^#[a-f0-9]{6}$\u002Fi', $color ) ) {\n        $color = '#000000';\n    }\n\n    echo '\u003Cstyle type=\"text\u002Fcss\">';\n    \u002F\u002F Use esc_attr if the value is placed in a context where it could be an attribute\n    \u002F\u002F or ensure strict validation before echoing into CSS.\n    printf( '.gdpr-banner { background-color: %s; }', $color );\n    echo '\u003C\u002Fstyle>';\n}\n```\n\nFor more information on securing WordPress plugins, you can refer to the [WordPress Plugin Handbook's Security section](https:\u002F\u002Fdeveloper.wordpress.org\u002Fplugins\u002Fsecurity\u002F).","The WP GDPR Cookie Consent plugin (\u003C= 1.0.0) is vulnerable to Stored Cross-Site Scripting because its AJAX handler, 'ninja_gdpr_ajax_actions', lacks authorization and CSRF checks. This allows authenticated users with low privileges to modify plugin settings; because these settings are not sanitized on storage or escaped when output into a \u003Cstyle> block in the document head, attackers can inject and execute arbitrary JavaScript.","\u002F\u002F File: wp-gdpr-cookie-consent.php\nadd_action( 'wp_ajax_ninja_gdpr_ajax_actions', 'handleAjaxCalls' );\n\nfunction handleAjaxCalls() {\n    \u002F\u002F Missing capability check (e.g., current_user_can)\n    \u002F\u002F Missing nonce verification (e.g., check_ajax_referer)\n    if ( isset( $_POST['gdprConfig'] ) ) {\n        $config = $_POST['gdprConfig'];\n        update_option( 'ninja_gdpr_config', $config );\n    }\n    wp_send_json_success();\n}\n\n---\n\n\u002F\u002F File: wp-gdpr-cookie-consent.php\nadd_action( 'wp_head', 'generateCSS' );\n\nfunction generateCSS() {\n    $config = get_option( 'ninja_gdpr_config' );\n    if ( ! empty( $config['primary_color'] ) ) {\n        echo '\u003Cstyle type=\"text\u002Fcss\">';\n        \u002F\u002F Missing output escaping and CSS context validation\n        echo '.gdpr-button { background-color: ' . $config['primary_color'] . '; }';\n        echo '\u003C\u002Fstyle>';\n    }\n}","--- wp-gdpr-cookie-consent.php\n+++ wp-gdpr-cookie-consent.php\n@@ -10,6 +10,14 @@\n function handleAjaxCalls() {\n+    check_ajax_referer( 'gdpr_nonce_action', 'security' );\n+\n+    if ( ! current_user_can( 'manage_options' ) ) {\n+        wp_send_json_error( 'Unauthorized', 403 );\n+    }\n+\n     if ( isset( $_POST['gdprConfig'] ) ) {\n-        $config = $_POST['gdprConfig'];\n+        $config = $_POST['gdprConfig'];\n+        if ( isset( $config['primary_color'] ) ) {\n+            $config['primary_color'] = sanitize_hex_color( $config['primary_color'] );\n+        }\n         update_option( 'ninja_gdpr_config', $config );\n     }\n@@ -25,7 +33,7 @@\n     $config = get_option( 'ninja_gdpr_config' );\n-    if ( ! empty( $config['primary_color'] ) ) {\n+    if ( ! empty( $config['primary_color'] ) && preg_match( '\u002F^#[a-f0-9]{6}$\u002Fi', $config['primary_color'] ) ) {\n         echo '\u003Cstyle type=\"text\u002Fcss\">';\n-        echo '.gdpr-button { background-color: ' . $config['primary_color'] . '; }';\n+        printf( '.gdpr-button { background-color: %s; }', esc_attr( $config['primary_color'] ) );\n         echo '\u003C\u002Fstyle>';\n     }","The exploit involves an authenticated attacker with at least Subscriber-level privileges sending a crafted POST request to the WordPress AJAX endpoint (wp-admin\u002Fadmin-ajax.php). The request must use the 'ninja_gdpr_ajax_actions' action and include a 'gdprConfig' parameter containing a malicious payload in a field later used for CSS generation (e.g., 'primary_color'). The payload uses characters like '} \u003C\u002Fstyle>\u003Cscript>alert(1)\u003C\u002Fscript>' to break out of the generated \u003Cstyle> block. Due to the lack of access controls, the plugin saves this payload to the database. The script executes for all visitors when the plugin renders the configuration via the wp_head hook.","gemini-3-flash-preview","2026-06-26 02:15:59","2026-06-26 02:16:37",{"type":32,"vulnerable_version":9,"fixed_version":9,"vulnerable_browse":9,"vulnerable_zip":9,"fixed_browse":9,"fixed_zip":9,"all_tags":33},"plugin","https:\u002F\u002Fplugins.trac.wordpress.org\u002Fbrowser\u002Fwp-gdpr-cookie-consent\u002Ftags"]