Feb 24, 2026·6 min read·31 visits
Craft CMS contained a 'ghost' column type called 'html' within its Table Field component. While not visible in the UI, it was supported by the backend. Attackers with field configuration permissions could manually set a column to this type, enabling the rendering of raw, unsanitized HTML (and JavaScript) in the admin panel.
A hidden, semi-internal 'html' column type in Craft CMS's Table Field component allowed authenticated administrators to inject arbitrary JavaScript. By manipulating field configuration requests, an attacker could enable this unexposed column type, bypassing sanitization and achieving Stored XSS against other control panel users.
We all love a flexible Content Management System. Craft CMS is arguably one of the best, giving developers granular control over data structures. One of its most powerful tools is the 'Table Field,' which allows content editors to manage rows of repeated data—text, numbers, checkboxes, etc. It is the Excel spreadsheet of the CMS world.
But here is the thing about software development: features get deprecated, hidden, or half-built, and sometimes they never really leave the codebase. They just become ghosts. In CVE-2026-27126, researchers found one of these ghosts lurking in the Table Field logic.
It turns out, while the UI only let you pick mundane column types like singleline or number, the backend logic was secretly holding a candle for a type called html. And unlike its siblings, the html type did exactly what it sounds like—it took whatever input you gave it and dumped it directly into the DOM, raw and wriggling. It’s like finding a secret menu item at a restaurant that serves you uncooked chicken.
The vulnerability stems from a classic disconnect between the User Interface and the Application Logic. The developers correctly assumed that users shouldn't be creating columns that render raw HTML, so they simply didn't include html in the dropdown menu of the Field Settings page. Case closed, right? Wrong.
The backend component responsible for validating the Table Field configuration (src/fields/Table.php) had a blind spot. It accepted the column configuration array and processed it, but it didn't strictly validate that the requested type was one of the publicly supported options.
Simultaneously, the frontend template responsible for rendering the table in the control panel (editableTable.twig) had logic explicitly designed to handle this html type. It was likely a remnant of an internal feature or a deprecated capability. When the renderer encountered a column with type: 'html', it skipped the usual output encoding and treated the cell's content as trusted markup. This created a perfect Stored XSS vector for anyone who could force the configuration to acknowledge this hidden type.
Let's look at the PHP code. Before the patch, the validateColumns method was a bit too permissible. It iterated through columns to check basic properties but failed to enforce a strict allowlist on the type attribute.
The fix, implemented by Brandon Kelly, introduces a hardcoded list of allowed types and forces anything else to degrade to a harmless singleline text field.
Before (Conceptual Logic):
foreach ($this->columns as &$col) {
// Checking label, handle, etc.
// No check if $col['type'] is actually valid!
}After (Patched Logic):
// src/fields/Table.php
private static function typeOptions(): array
{
return [
'checkbox' => true,
'color' => true,
'date' => true,
'select' => true,
// ... (other safe types)
'url' => true,
];
}
public function validateColumns(): void
{
$typeOptions = self::typeOptions();
foreach ($this->columns as &$col) {
// If the type isn't in our VIP list, you're getting a text box.
if (!isset($typeOptions[$col['type']])) {
$col['type'] = 'singleline';
}
// ...
}
}By adding this check, the 'ghost' html type is effectively exorcised. Even if an attacker tries to inject it, the system forces it into a singleline type, which escapes all output.
Since the UI won't let us select the html type, we have to get our hands dirty with a proxy. This is an 'Authenticated Admin' exploit, meaning the attacker needs permissions to configure fields (specifically allowAdminChanges must be on).
Here is the attack chain:
"type": "singleline"."type": "html". The backend, lacking the validation we saw earlier, saves this configuration to the database.<img src=x onerror=alert(document.cookie)> into the cell.editableTable.twig sees the html type and renders the script tag immediately.You might be thinking, "If I already have Admin access to change settings, why do I need XSS?" That is a fair question, but it misses the nuance of modern access control.
First, Privilege Escalation. In many organizations, there are tiers of administrators. A 'Site Builder' might have permission to configure fields but not to install plugins or execute PHP. By using this XSS, the lower-tier admin can hijack the session of a Super Admin. Once they have that session, they can install a malicious plugin or edit templates to achieve full Remote Code Execution (RCE) on the server.
Second, Persistence. An attacker who briefly compromises an admin account can plant this 'time bomb' in the database. Even if their original access is revoked, the XSS payload remains in the content, waiting for a high-value target to view the table. It is a backdoor that lives in the legitimate data structure of the site.
The remediation is straightforward: upgrade. The patch exists in Craft CMS 4.16.19 and 5.8.23. These versions introduce the strict type checking we analyzed above.
If you cannot upgrade immediately, there is a configuration-level mitigation. Craft CMS has a setting called allowAdminChanges. In a properly secured production environment, this should be set to false.
// config/general.php
'allowAdminChanges' => false,Disabling admin changes prevents anyone from modifying field configurations via the control panel, effectively neutralizing the attack vector (unless the attacker has direct database access, in which case you have bigger problems). This is a best practice for Craft CMS deployment pipelines anyway—treat your production schema as immutable.
CVSS:4.0/AV:N/AC:L/AT:P/PR:L/UI:P/VC:N/VI:H/VA:N/SC:N/SI:N/SA:N| Product | Affected Versions | Fixed Version |
|---|---|---|
Craft CMS Pixel & Tonic | >= 4.5.0-RC1, < 4.16.19 | 4.16.19 |
Craft CMS Pixel & Tonic | >= 5.0.0-RC1, < 5.8.23 | 5.8.23 |
| Attribute | Detail |
|---|---|
| CWE ID | CWE-79 |
| Attack Vector | Network (Authenticated) |
| CVSS v4.0 | 5.9 (Medium) |
| Impact | Stored XSS / Session Hijacking |
| Privileges Required | Low (Admin with Settings Access) |
| Exploit Status | PoC Available |
Improper Neutralization of Input During Web Page Generation ('Cross-site Scripting')
CVE-2026-48861 is a client-side HTTP request-line CRLF (Carriage Return Line Feed) injection vulnerability in the popular Elixir HTTP client library, Mint. The vulnerability permits HTTP Request Splitting and HTTP Request Smuggling when an application forwards untrusted, attacker-controlled inputs to Mint's HTTP client requests as either the HTTP request method or target. By embedding CRLF characters within these parameters, an attacker can terminate the request line prematurely, inject malicious headers, or pipeline entirely independent requests. These smuggled requests are then processed by upstream or downstream proxy servers as separate HTTP queries on the same TCP connection. While Mint version 1.7.0 introduced target validation to secure the request target, the HTTP request method parameter remained completely unvalidated. This flaw allows attackers to bypass routing filters, access restricted internal APIs, or poison HTTP caches under default configurations.
An Inconsistent Interpretation of HTTP Requests (HTTP Request/Response Smuggling) vulnerability in the Elixir Mint HTTP client allows attacker-controlled HTTP/1 servers to desynchronize response framing on shared connections due to over-lenient parsing of sign-prefixed Content-Length headers.
An allocation of resources without limits or throttling vulnerability in Elixir Mint allows an attacker-controlled HTTP/2 server to exhaust memory in a Mint client. The vulnerability is exploited by sending a HEADERS frame without the END_HEADERS flag followed by an infinite stream of CONTINUATION frames. Because the client lacks limits on the incoming header-block accumulator, the client continuously consumes memory until an out-of-memory crash occurs.
CVE-2026-48596 is an Improper Neutralization of CRLF Sequences in HTTP Headers (HTTP Request/Response Splitting, CWE-113) in the Elixir Tesla HTTP client. The flaw resides in how multipart content-type parameters are joined and serialized, enabling attackers to inject arbitrary headers or split HTTP requests when applications pass untrusted inputs to the parameters of multipart uploads.
An improper handling of highly compressed data (decompression bomb) vulnerability exists in the Elixir Tesla HTTP client when utilizing response decompression middlewares. By serving highly compressed responses or stacked content-encoding headers, a malicious server can cause arbitrary heap exhaustion, leading to a denial of service (DoS) crash in the BEAM virtual machine.
A high-severity security vulnerability in Elixir's Tesla HTTP client library (CVE-2026-48595) allows unauthenticated remote attackers to harvest sensitive credentials, including Authorization headers and cookies. The flaw resides in the 'Tesla.Middleware.FollowRedirects' component, which performs case-sensitive lookups when stripping credentials during cross-origin redirects. Because HTTP headers are case-insensitive by RFC specifications, standard canonical casing (e.g., 'Authorization') bypasses the lowercase-only blocklist, leaking tokens to untrusted external redirect destinations.