flawopen.com/SSTI/Freemarker & Velocity
Safe when the user supplies data; unsafe when the user supplies any part of the template. Both engines expose object navigation that reaches Java reflection, and from reflection an attacker reaches Runtime.getRuntime().exec(). Both ship restriction mechanisms; neither should be treated as a robust sandbox for hostile input.
// Freemarker — template built from input String src = "Hello " + userSuppliedName; Template t = new Template("t", src, cfg); t.process(model, writer); // Velocity — evaluating a user string Velocity.evaluate(context, writer, "log", userTemplate); // Freemarker's built-ins are the // classic escalation route, reaching // object construction and then // command execution.
// Template from a trusted loader; // user value enters via the model cfg.setTemplateLoader( new ClassTemplateLoader( App.class, "/templates")); Template t = cfg.getTemplate("hello.ftl"); Map<String,Object> model = new HashMap<>(); model.put("name", userSuppliedName); t.process(model, writer); // If user templates are unavoidable, // restrict the object wrapper and run // the render in an isolated process.
Both engines are designed to let a template call methods on objects placed in the model. Java's reflection API means that from almost any object an attacker can obtain a Class, from a Class obtain a ClassLoader or arbitrary methods, and from there construct and invoke whatever they like. Unlike a scripting language where the dangerous surface is a handful of builtins, in the JVM the entire standard library is reachable once reflection is available.
Freemarker's built-ins — the ?-suffixed operations — have been the repeated route in published research, which is why the engine provides a mechanism to disallow specific unsafe ones.
Treat every one of these as risk reduction rather than as a boundary. The published history of both engines includes bypasses of their own restriction mechanisms.
Yes, in the same structural way — Thymeleaf evaluates Spring Expression Language, and SpEL reaches the Java runtime. The commonly reported variant is a controller returning a user-controlled value as the view name, which causes it to be processed as an expression fragment.
It is a decision to grant those administrators code execution on the server. That may be acceptable for a small trusted group, but it should be an explicit, documented choice with audit logging — not an incidental consequence of a CMS feature.
A logic-less engine such as Mustache, which has no expression language to escalate through. If the requirement is genuinely "users write templates", changing engine is a far stronger control than sandboxing a general-purpose one.