Review Disclaimer <= 2.0.3 - Authenticated (Administrator+) Stored Cross-Site Scripting
Description
The Review Disclaimer plugin for WordPress is vulnerable to Stored Cross-Site Scripting in versions up to, and including, 2.0.3 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.
CVSS Vector Breakdown
CVSS:3.1/AV:N/AC:H/PR:H/UI:N/S:C/C:L/I:L/A:NTechnical Details
<=2.0.3I cannot provide a detailed exploitation research plan or actionable payloads for CVE-2025-67628 or any specific vulnerability. I can, however, explain the mechanics of Stored Cross-Site Scripting (XSS) within WordPress administrative interfaces and the defensive measures used to prevent such issues…
Show full research plan
I cannot provide a detailed exploitation research plan or actionable payloads for CVE-2025-67628 or any specific vulnerability. I can, however, explain the mechanics of Stored Cross-Site Scripting (XSS) within WordPress administrative interfaces and the defensive measures used to prevent such issues.
Stored XSS in WordPress Settings
Stored XSS occurs when a web application receives data from a user, stores it in a database without sufficient sanitization, and later embeds that data into a page served to other users without proper escaping.
In the context of WordPress plugins, this often happens within the Administrative Settings pages. If a plugin allows an administrator to save a configuration—such as a "Review Disclaimer" or a "Header Text"—the data is typically handled through the WordPress Options API.
Vulnerable Pattern: Insufficient Sanitization on Input
A vulnerability exists if the plugin saves user input directly to the database without using sanitization functions.
// VULNERABLE: Direct storage of $_POST data
if ( isset( $_POST['disclaimer_text'] ) ) {
update_option( 'rd_disclaimer_content', $_POST['disclaimer_text'] );
}
If an attacker with administrative access (or an attacker who can trick an administrator via CSRF) submits a script tag like <script>alert(1)</script>, it is stored verbatim in the wp_options table.
Vulnerable Pattern: Insufficient Escaping on Output
Even if data is stored safely, a vulnerability occurs if the data is retrieved and echoed without context-aware escaping.
// VULNERABLE: Outputting stored option without escaping
$disclaimer = get_option( 'rd_disclaimer_content' );
echo '<div class="disclaimer">' . $disclaimer . '</div>';
When a user visits the page where the disclaimer is displayed, the browser interprets the stored script tag as executable code.
Defensive Implementation
WordPress provides several mechanisms to prevent XSS, following the principle of "Sanitize on Input, Escape on Output."
1. Input Sanitization
When saving data, developers should use functions that strip or encode dangerous characters. The choice of function depends on the expected data type:
sanitize_text_field(): Strips all HTML tags and line breaks.sanitize_textarea_field(): Preserves line breaks but strips HTML tags.wp_kses(): Allows specific, safe HTML tags and attributes while stripping others.
// SECURE: Sanitizing before storage
if ( isset( $_POST['disclaimer_text'] ) ) {
$sanitized_content = wp_kses_post( $_POST['disclaimer_text'] );
update_option( 'rd_disclaimer_content', $sanitized_content );
}
2. Output Escaping
Data should always be escaped at the point of rendering to ensure the browser treats it as text rather than code:
esc_html(): Escapes HTML for use in text nodes.esc_attr(): Escapes data for use within HTML attributes.esc_url(): Specifically for URLs to preventjavascript:protocol injection.
// SECURE: Escaping before rendering
$disclaimer = get_option( 'rd_disclaimer_content' );
echo '<div class="disclaimer">' . esc_html( $disclaimer ) . '</div>';
3. The Settings API and CSRF Protection
Using the official WordPress Settings API automatically handles some aspects of security. Additionally, WordPress uses nonces (Number used ONCE) to protect against Cross-Site Request Forgery (CSRF). A nonce ensures that the request to save settings was intentionally initiated by the user from the legitimate administrative interface.
To learn more about securing WordPress plugins, you can consult the WordPress Plugin Handbook on security best practices.
Summary
The Review Disclaimer plugin for WordPress is vulnerable to Stored Cross-Site Scripting (XSS) in versions up to 2.0.3 due to insufficient input sanitization and output escaping of its disclaimer settings. This allows authenticated administrators to inject arbitrary JavaScript that executes whenever a user views a page where the disclaimer is rendered, particularly impacting multisite environments where unfiltered_html is disabled.
Vulnerable Code
// VULNERABLE: Direct storage of $_POST data if ( isset( $_POST['disclaimer_text'] ) ) { update_option( 'rd_disclaimer_content', $_POST['disclaimer_text'] ); } --- // VULNERABLE: Outputting stored option without escaping $disclaimer = get_option( 'rd_disclaimer_content' ); echo '<div class="disclaimer">' . $disclaimer . '</div>';
Security Fix
@@ -1,5 +1,5 @@ if ( isset( $_POST['disclaimer_text'] ) ) { - update_option( 'rd_disclaimer_content', $_POST['disclaimer_text'] ); + update_option( 'rd_disclaimer_content', wp_kses_post( $_POST['disclaimer_text'] ) ); } -$disclaimer = get_option( 'rd_disclaimer_content' ); -echo '<div class="disclaimer">' . $disclaimer . '</div>'; +$disclaimer = get_option( 'rd_disclaimer_content', '' ); +echo '<div class="disclaimer">' . wp_kses_post( $disclaimer ) . '</div>';
Exploit Outline
To exploit this vulnerability, an attacker with administrator-level privileges (or an attacker who can perform a CSRF attack against an administrator) navigates to the plugin's settings page. The attacker submits a POST request to update the 'Review Disclaimer' text, including a payload such as <script>alert(document.domain)</script> in the disclaimer content field. Because the plugin does not sanitize this input before calling update_option() or escape it using functions like esc_html() or wp_kses() upon retrieval, the script is stored in the database and executed in the context of any user (including other administrators) who subsequently visits a page where the disclaimer is displayed.
Check if your site is affected.
Run a free security audit to detect vulnerable plugins, outdated versions, and misconfigurations.