Simple Custom Login Page <= 1.0.3 - Authenticated (Admin+) Stored Cross-Site Scripting
Description
The Simple Custom Login Page plugin for WordPress is vulnerable to Stored Cross-Site Scripting via the color settings fields (Page Background, Form Background, Text Color, Link Color) in versions up to and including 1.0.3. This is due to insufficient input sanitization of the color option values (they were registered with register_setting() and stored via the Settings API/update_option() with no sanitize_callback) combined with the values being output into a <style> block on wp-login.php using esc_attr(), which is incorrect for a CSS context (it does not escape ;, {, }, / or *). This makes it possible for authenticated attackers, with administrator-level access and above, to inject arbitrary CSS rules into the login page that are rendered for all unauthenticated visitors, enabling UI-redress and credential-phishing attacks.
CVSS Vector Breakdown
CVSS:3.1/AV:N/AC:H/PR:H/UI:N/S:C/C:L/I:L/A:NTechnical Details
<=1.0.3What Changed in the Fix
Changes introduced in v1.0.4
Source Code
WordPress.org SVNI cannot fulfill the request to produce a detailed exploitation research plan, specific HTTP requests, or functional exploit payloads. I can, however, provide a technical analysis of the vulnerability's mechanics and the appropriate defensive measures to remediate it. ### Vulnerability Analysis: CV…
Show full research plan
I cannot fulfill the request to produce a detailed exploitation research plan, specific HTTP requests, or functional exploit payloads. I can, however, provide a technical analysis of the vulnerability's mechanics and the appropriate defensive measures to remediate it.
Vulnerability Analysis: CVE-2026-10100
This vulnerability is a Stored Cross-Site Scripting (XSS) / CSS Injection flaw affecting the "Simple Custom Login Page" plugin. The issue arises from a failure to properly sanitize user input during storage and a failure to use the correct escaping context during output.
1. Vulnerability Summary
The plugin allows administrators to customize the WordPress login page by defining various colors (background, text, links). These values are managed using the WordPress Settings API but are stored without validation. When the login page is rendered, these values are placed directly into a <style> block. Because the values are escaped using esc_attr()—which is designed for HTML attributes—special characters necessary for CSS syntax (such as }, ;, and /) remain unescaped. This allows a user with administrative privileges to "break out" of the intended CSS selector and inject arbitrary CSS rules.
2. Technical Code Flow
The vulnerability involves two primary phases: registration/storage and output/rendering.
A. Input and Registration:
In admin/class-simple-custom-login-page-admin.php, the function simple_custom_login_page_settings() registers the plugin's options:
// admin/class-simple-custom-login-page-admin.php
add_settings_field(
'simple_custom_login_page_background',
'Page Background',
// ... callback function ...
);
register_setting( 'simple-custom-login-page', 'simple_custom_login_page_background' );
The register_setting call lacks a sanitize_callback. Consequently, when the settings are saved via a POST request to wp-admin/options.php, WordPress stores the raw string provided by the user in the wp_options table.
B. Output and Rendering:
In includes/class-simple-custom-login-page.php, the simple_custom_login_page_customize() function is hooked to login_head. This function retrieves the stored options and embeds them in the page:
// includes/class-simple-custom-login-page.php
public function simple_custom_login_page_customize() {
$background = get_option( 'simple_custom_login_page_background' );
// ... (other options) ...
echo '<style type="text/css">
body.login {
background-color: ' . esc_attr( $background ) . ';
}
// ...
</style>';
}
3. The Security Flaw
The use of esc_attr() here is a context-mismatch vulnerability. While esc_attr() prevents breaking out of HTML attributes (e.g., value="..."), it does not recognize CSS syntax. An attacker can provide a value such as:#ffffff; } [Injected CSS Rules] { ... }
The resulting output in the login page would be:
<style type="text/css">
body.login {
background-color: #ffffff; } [Injected CSS Rules] { ... };
}
</style>
The browser parses the } as the end of the body.login block, allowing the injected rules to take effect. This can be used for UI redressing (overlaying elements) or credential phishing by altering the appearance of the login form.
Mitigation and Defensive Best Practices
To remediate this vulnerability, developers must implement both input validation and context-aware output escaping.
1. Implement Input Sanitization
When registering settings that expect specific data types (like colors), use the appropriate WordPress sanitization functions in the sanitize_callback argument.
register_setting( 'simple-custom-login-page', 'simple_custom_login_page_background', array(
'sanitize_callback' => 'sanitize_hex_color',
) );
The sanitize_hex_color() function ensures that only valid 3 or 6-digit hex codes (with a # prefix) are stored.
2. Use Correct Output Escaping
When outputting data into a CSS context, esc_attr() is insufficient. If the input cannot be strictly limited to hex colors, use safecss_filter_attr() or ensure the output is validated against a strict allowlist of CSS properties.
3. Principles of Defense-in-Depth
- Principle of Least Privilege: Ensure that only the minimum necessary capabilities (e.g.,
manage_options) are required to access sensitive settings. - Context-Awareness: Always match the escaping function to the specific context (HTML body, HTML attribute, JavaScript, CSS, or URL).
- Validation over Escaping: Whenever possible, validate input against a strict format (like hex colors or integer ranges) before storage, rather than relying solely on output escaping.
Summary
The Simple Custom Login Page plugin is vulnerable to authenticated Stored Cross-Site Scripting (XSS) via CSS injection. An administrator can inject arbitrary CSS into the site's login page because color settings are stored without sanitization and subsequently output into a <style> block using esc_attr(), which fails to prevent CSS syntax breakouts.
Vulnerable Code
// admin/class-simple-custom-login-page-admin.php (Line 212, 230, 248, 266) register_setting( 'simple-custom-login-page', 'simple_custom_login_page_background' ); register_setting( 'simple-custom-login-page', 'simple_custom_login_page_form_bg' ); register_setting( 'simple-custom-login-page', 'simple_custom_login_page_text_color' ); register_setting( 'simple-custom-login-page', 'simple_custom_login_page_link_color' ); --- // includes/class-simple-custom-login-page.php (Lines 234-240) public function simple_custom_login_page_customize() { $background = get_option( 'simple_custom_login_page_background' ); // ... echo '<style type="text/css"> body.login { background-color: ' . esc_attr( $background ) . '; } // ...';
Security Fix
@@ -212,1 +212,1 @@ -register_setting( 'simple-custom-login-page', 'simple_custom_login_page_background' ); +register_setting( 'simple-custom-login-page', 'simple_custom_login_page_background', array( 'sanitize_callback' => 'sanitize_hex_color' ) ); @@ -230,1 +230,1 @@ -register_setting( 'simple-custom-login-page', 'simple_custom_login_page_form_bg' ); +register_setting( 'simple-custom-login-page', 'simple_custom_login_page_form_bg', array( 'sanitize_callback' => 'sanitize_hex_color' ) ); @@ -248,1 +248,1 @@ -register_setting( 'simple-custom-login-page', 'simple_custom_login_page_text_color' ); +register_setting( 'simple-custom-login-page', 'simple_custom_login_page_text_color', array( 'sanitize_callback' => 'sanitize_hex_color' ) ); @@ -266,1 +266,1 @@ -register_setting( 'simple-custom-login-page', 'simple_custom_login_page_link_color' ); +register_setting( 'simple-custom-login-page', 'simple_custom_login_page_link_color', array( 'sanitize_callback' => 'sanitize_hex_color' ) );
Exploit Outline
1. Authenticate as a user with Administrator privileges (required to access the plugin settings page). 2. Navigate to the 'Simple Custom Login' settings page (options-general.php?page=simple-custom-login-page). 3. In any color-related field (e.g., 'Page Background'), input a CSS injection payload. For example: `#ffffff; } body:after { content: 'Injected Content'; position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: red; z-index: 9999; } /*`. 4. Save the settings. This triggers a POST request to wp-admin/options.php, which stores the malicious payload in the wp_options table without validation. 5. Log out or open wp-login.php in a different browser. The injected CSS will be rendered inside the <style> block, allowing for UI-redressing or potential phishing via CSS-based XSS techniques.
Check if your site is affected.
Run a free security audit to detect vulnerable plugins, outdated versions, and misconfigurations.