GHSA-9RCC-PMJ8-FFHR
Vulnerability from github – Published: 2026-09-18 16:59 – Updated: 2026-09-18 16:59Summary
Special:FacetedSearch cstate hidden inputs enable reflected XSS (residual of CVE-2025-10354)
Details
Affected versions and vulnerable location
- Confirmed present on latest shipped release tag available in the local clone:
SemanticMediaWiki/SemanticMediaWiki@7.2.0. - Confirmed present on default branch
masterat HEAD18f418b4cdf2875e67a741349179a22c1573f61c.
Vulnerable sink (default-branch representation):
src/MediaWiki/Specials/FacetedSearch/HtmlBuilder.php:131-133- Builds
$hiddenby concatenating unescaped request-controlledcstate[$key]values into an HTML attribute context (value="..."). templates/FacetedSearch/search.mustache:25- Inserts the constructed fragment via
{{{hidden}}}(no HTML escaping at this boundary).
Reachability trace (verified from source)
- HTTP entrypoint:
GETtoSpecial:FacetedSearchdispatches intoSMW\MediaWiki\Specials\SpecialFacetedSearch::execute().- Request decoding boundary:
SpecialFacetedSearch::execute()constructsUrlArgsfrom$request->getValues()and callsParametersProcessor::checkRequest($request).- Checksum gate:
ParametersProcessor::checkRequest()clearscstateonly whenfiltered != 1andgetInt('csum', 0) != crc32(getVal('q', '')).- Decoder -> HTML assembly:
HtmlBuilder::buildHTML()iteratesforeach ( $urlArgs->getArray( 'cstate' ) as $key => $value )and concatenates each into$hiddenwithout escaping.HtmlBuilder::buildHTML()passes$hiddeninto the template variablehidden.- HTML injection sink:
templates/FacetedSearch/search.mustacherenders{{{hidden}}}into the<form>, so the concatenated markup is inserted as raw HTML.
PoC
Reproduction steps (source-derived)
- Choose a
qvalue. - Compute
csumascrc32(q). - Send a request that includes:
q=<chosen>csum=<crc32(q)>- at least one
cstate[<key>]=<payload>entry
Example request shape:
/index.php/Special:FacetedSearch?q=Text&csum=<crc32(Text)>&cstate[0]=x%22%20autofocus%20onfocus%3Dalert(1)%20x%22
Impact
Attacker model
- Any remote attacker who can send HTTP requests to
Special:FacetedSearch(or the localized alias mapped to the sameSpecialFacetedSearchclass) can supply attacker-controlled query parameters. - Preconditions:
- The attacker must make
cstatesurviveParametersProcessor::checkRequest(), either by settingcsumtocrc32(q)(whenfiltered != 1), or by settingfiltered=1. - The attacker must supply
cstate[<key>]values containing characters that break out of the HTMLvalue="..."attribute context (for example an injected"to terminate the attribute value).
Severity and CVSS reasoning
Proposed severity: MEDIUM.
Proposed CVSS v3.1 vector: CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:L/I:L/A:N.
Rationale:
- AV:N: delivered over the network via query parameters.
- AC:L: requires only setting
q,csum, and at least onecstateentry. - PR:N: no authentication required for the request path in this code.
- UI:R: the victim must load the crafted URL.
- S:C: reflected XSS executes in the wiki origin and can affect other users depending on deployment and browser behavior.
Why this is a residual of CVE-2025-10354
- The CVE-2025-10354 hardening shipped by escaping the
qparameter before emitting it into thevalue="{{q}}"attribute. - Commit
3d675ceupdates only theqrendering to usehtmlspecialchars( $urlArgs->get( 'q', '' ) )and does not touch the adjacentcstate->$hiddenconstruction loop. - As a result,
cstateremains an unescaped input source that flows into the same raw template injection point ({{{hidden}}}), creating a distinct reflected-XSS lane.
Output (from code inspection)
Given the payload idea where cstate[0] starts with x" ... x", HtmlBuilder.php constructs the hidden fragment by concatenation:
<input name="cstate[0]" type="hidden" value="x" autofocus onfocus=alert(1) x">
Because search.mustache injects the fragment via {{{hidden}}}, the attacker-controlled markup participates in normal HTML parsing in the response body.
Suggested fix
- Escape both the
cstatekey and value when constructing$hidden. - Minimal code change in
src/MediaWiki/Specials/FacetedSearch/HtmlBuilder.php:
foreach ( $urlArgs->getArray( 'cstate' ) as $key => $value ) {
$safeKey = htmlspecialchars( (string)$key, ENT_QUOTES, 'UTF-8' );
$safeValue = htmlspecialchars( (string)$value, ENT_QUOTES, 'UTF-8' );
$hidden .= '<input name="cstate[' . $safeKey . ']" type="hidden" value="' . $safeValue . '">';
}
This keeps the raw {{{hidden}}} template insertion safe by ensuring the concatenated HTML fragment itself is attribute-escaped.
How I found it and a note on tooling
I anchored on the published CVE-2025-10354 patch by verifying in the checked-out repository that commit 3d675ce changes only the q rendering in HtmlBuilder.php to use htmlspecialchars.
Then I traced the reachable request path from SpecialFacetedSearch::execute() through ParametersProcessor::checkRequest() (checksum gate for whether cstate survives) into HtmlBuilder::buildHTML() where $hidden is constructed from cstate without escaping and injected into templates/FacetedSearch/search.mustache via {{{hidden}}}.
(End of file)
AI tooling
I used AI assistance for the code audit and for drafting this report. I manually verified the finding against the project's source at the location cited above before reporting it, and the severity and impact assessment are my own.
{
"affected": [
{
"database_specific": {
"last_known_affected_version_range": "\u003c= 7.2.0"
},
"package": {
"ecosystem": "Packagist",
"name": "mediawiki/semantic-media-wiki"
},
"ranges": [
{
"events": [
{
"introduced": "4.2.0"
},
{
"fixed": "7.2.1"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"aliases": [],
"database_specific": {
"cwe_ids": [
"CWE-79"
],
"github_reviewed": true,
"github_reviewed_at": "2026-09-18T16:59:16Z",
"nvd_published_at": null,
"severity": "MODERATE"
},
"details": "### Summary\n\nSpecial:FacetedSearch `cstate` hidden inputs enable reflected XSS (residual of CVE-2025-10354)\n\n### Details\n\n#### Affected versions and vulnerable location\n\n- Confirmed present on latest shipped release tag available in the local clone: `SemanticMediaWiki/SemanticMediaWiki@7.2.0`.\n- Confirmed present on default branch `master` at HEAD `18f418b4cdf2875e67a741349179a22c1573f61c`.\n\nVulnerable sink (default-branch representation):\n\n- `src/MediaWiki/Specials/FacetedSearch/HtmlBuilder.php:131-133`\n - Builds `$hidden` by concatenating unescaped request-controlled `cstate[$key]` values into an HTML attribute context (`value=\"...\"`).\n- `templates/FacetedSearch/search.mustache:25`\n - Inserts the constructed fragment via `{{{hidden}}}` (no HTML escaping at this boundary).\n\n#### Reachability trace (verified from source)\n\n1. HTTP entrypoint:\n - `GET` to `Special:FacetedSearch` dispatches into `SMW\\MediaWiki\\Specials\\SpecialFacetedSearch::execute()`.\n2. Request decoding boundary:\n - `SpecialFacetedSearch::execute()` constructs `UrlArgs` from `$request-\u003egetValues()` and calls `ParametersProcessor::checkRequest($request)`.\n3. Checksum gate:\n - `ParametersProcessor::checkRequest()` clears `cstate` only when `filtered != 1` and `getInt(\u0027csum\u0027, 0) != crc32(getVal(\u0027q\u0027, \u0027\u0027))`.\n4. Decoder -\u003e HTML assembly:\n - `HtmlBuilder::buildHTML()` iterates `foreach ( $urlArgs-\u003egetArray( \u0027cstate\u0027 ) as $key =\u003e $value )` and concatenates each into `$hidden` without escaping.\n - `HtmlBuilder::buildHTML()` passes `$hidden` into the template variable `hidden`.\n5. HTML injection sink:\n - `templates/FacetedSearch/search.mustache` renders `{{{hidden}}}` into the `\u003cform\u003e`, so the concatenated markup is inserted as raw HTML.\n\n### PoC\n\n#### Reproduction steps (source-derived)\n\n1. Choose a `q` value.\n2. Compute `csum` as `crc32(q)`.\n3. Send a request that includes:\n - `q=\u003cchosen\u003e`\n - `csum=\u003ccrc32(q)\u003e`\n - at least one `cstate[\u003ckey\u003e]=\u003cpayload\u003e` entry\n\nExample request shape:\n\n```text\n/index.php/Special:FacetedSearch?q=Text\u0026csum=\u003ccrc32(Text)\u003e\u0026cstate[0]=x%22%20autofocus%20onfocus%3Dalert(1)%20x%22\n```\n\n### Impact\n\n#### Attacker model\n\n- Any remote attacker who can send HTTP requests to `Special:FacetedSearch` (or the localized alias mapped to the same `SpecialFacetedSearch` class) can supply attacker-controlled query parameters.\n- Preconditions:\n - The attacker must make `cstate` survive `ParametersProcessor::checkRequest()`, either by setting `csum` to `crc32(q)` (when `filtered != 1`), or by setting `filtered=1`.\n - The attacker must supply `cstate[\u003ckey\u003e]` values containing characters that break out of the HTML `value=\"...\"` attribute context (for example an injected `\"` to terminate the attribute value).\n\n#### Severity and CVSS reasoning\n\nProposed severity: MEDIUM.\n\nProposed CVSS v3.1 vector: `CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:L/I:L/A:N`.\n\nRationale:\n\n- AV:N: delivered over the network via query parameters.\n- AC:L: requires only setting `q`, `csum`, and at least one `cstate` entry.\n- PR:N: no authentication required for the request path in this code.\n- UI:R: the victim must load the crafted URL.\n- S:C: reflected XSS executes in the wiki origin and can affect other users depending on deployment and browser behavior.\n\n### Why this is a residual of CVE-2025-10354\n\n- The CVE-2025-10354 hardening shipped by escaping the `q` parameter before emitting it into the `value=\"{{q}}\"` attribute.\n- Commit `3d675ce` updates only the `q` rendering to use `htmlspecialchars( $urlArgs-\u003eget( \u0027q\u0027, \u0027\u0027 ) )` and does not touch the adjacent `cstate` -\u003e `$hidden` construction loop.\n- As a result, `cstate` remains an unescaped input source that flows into the same raw template injection point (`{{{hidden}}}`), creating a distinct reflected-XSS lane.\n\n### Output (from code inspection)\n\nGiven the payload idea where `cstate[0]` starts with `x\" ... x\"`, `HtmlBuilder.php` constructs the hidden fragment by concatenation:\n\n```html\n\u003cinput name=\"cstate[0]\" type=\"hidden\" value=\"x\" autofocus onfocus=alert(1) x\"\u003e\n```\n\nBecause `search.mustache` injects the fragment via `{{{hidden}}}`, the attacker-controlled markup participates in normal HTML parsing in the response body.\n\n### Suggested fix\n\n- Escape both the `cstate` key and value when constructing `$hidden`.\n- Minimal code change in `src/MediaWiki/Specials/FacetedSearch/HtmlBuilder.php`:\n\n```php\nforeach ( $urlArgs-\u003egetArray( \u0027cstate\u0027 ) as $key =\u003e $value ) {\n\t$safeKey = htmlspecialchars( (string)$key, ENT_QUOTES, \u0027UTF-8\u0027 );\n\t$safeValue = htmlspecialchars( (string)$value, ENT_QUOTES, \u0027UTF-8\u0027 );\n\t$hidden .= \u0027\u003cinput name=\"cstate[\u0027 . $safeKey . \u0027]\" type=\"hidden\" value=\"\u0027 . $safeValue . \u0027\"\u003e\u0027;\n}\n```\n\nThis keeps the raw `{{{hidden}}}` template insertion safe by ensuring the concatenated HTML fragment itself is attribute-escaped.\n\n### How I found it and a note on tooling\n\nI anchored on the published CVE-2025-10354 patch by verifying in the checked-out repository that commit `3d675ce` changes only the `q` rendering in `HtmlBuilder.php` to use `htmlspecialchars`.\n\nThen I traced the reachable request path from `SpecialFacetedSearch::execute()` through `ParametersProcessor::checkRequest()` (checksum gate for whether `cstate` survives) into `HtmlBuilder::buildHTML()` where `$hidden` is constructed from `cstate` without escaping and injected into `templates/FacetedSearch/search.mustache` via `{{{hidden}}}`.\n\n(End of file)\n\n### AI tooling\n\nI used AI assistance for the code audit and for drafting this report. I manually verified the finding against the project\u0027s source at the location cited above before reporting it, and the severity and impact assessment are my own.",
"id": "GHSA-9rcc-pmj8-ffhr",
"modified": "2026-09-18T16:59:17Z",
"published": "2026-09-18T16:59:16Z",
"references": [
{
"type": "WEB",
"url": "https://github.com/SemanticMediaWiki/SemanticMediaWiki/security/advisories/GHSA-9rcc-pmj8-ffhr"
},
{
"type": "PACKAGE",
"url": "https://github.com/SemanticMediaWiki/SemanticMediaWiki"
},
{
"type": "WEB",
"url": "https://github.com/SemanticMediaWiki/SemanticMediaWiki/releases/tag/7.2.1"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:L/I:L/A:N",
"type": "CVSS_V3"
}
],
"summary": "Semantic MediaWiki\u0027s Special:FacetedSearch cstate hidden inputs enable reflected XSS (residual of CVE-2025-10354)"
}
Sightings
| Author | Source | Type | Date | Other |
|---|
Nomenclature
- Seen: The vulnerability was mentioned, discussed, or observed by the user.
- Confirmed: The vulnerability has been validated from an analyst's perspective.
- Published Proof of Concept: A public proof of concept is available for this vulnerability.
- Exploited: The vulnerability was observed as exploited by the user who reported the sighting.
- Patched: The vulnerability was observed as successfully patched by the user who reported the sighting.
- Not exploited: The vulnerability was not observed as exploited by the user who reported the sighting.
- Not confirmed: The user expressed doubt about the validity of the vulnerability.
- Not patched: The vulnerability was not observed as successfully patched by the user who reported the sighting.
The approach is described in our paper Mapping CVEs to MITRE ATT&CK Techniques: A Curated Gold-Set Classifier and the Limits of LLM-Assisted Label Expansion.
Browse all ATT&CK techniques and the vulnerabilities related to each.
Related by attack behaviour
Vulnerabilities whose description is nearest to this one in the vector space of the CIRCL/vulnerability-attack-technique-biencoder model. This is a similarity search over the bi-encoder space (plain cosine), not a classification, and it has no measured accuracy.