CVE Analysis: Adobe Coldfusion from LFI to RCE (CVE-2023-26359 / CVE-2023-26360)

As the description from this CVE,

I will download two versions 2018.0.15 and 2018.0.16 to diff. We can easily install Adobe Coldfusion with docker via the link: https://hub.docker.com/r/adobecoldfusion/coldfusion2018

LFI

We should only diff two patches 15 and 16. The result looks good. Quickly I realized the change:

In coldfusion.runtime.JSONUtils#convertToTemplateProxy, server check allowNonCFCDeserializationand not accept deserialization from CFClient with ending file .cfc.

In the vulnerable version, convertToTemplateProxy takes the input as a Map, get the key _metadata as an Object, if it's not null, the key classname of the metatdata object will be taken. This value called serverClass will be checked if start with "/" and not null. Finally, server will get the real path from this value via FusionContext.getRealPath() in coldfusion.filter.FusionContext#getRealPath(String, boolean), totally return the same path from ServletContext.getRealPath(). This function return the real path in disk file system, so we can use it to exploit LFI. (Link).

The pageFile value goes to coldfusion.runtime.TemplateProxyFactory#resolveName(coldfusion.runtime.TemplateProxy, coldfusion.runtime.NeoPageContext, File, String, boolean, Map, HashSet, boolean)

Continue go to coldfusion.runtime.TemplateProxyFactory#getCFCInstance

Continue to coldfusion.runtime.TemplateClassLoader#newInstance(ServletContext, String, coldfusion.runtime.VariableScope, coldfusion.runtime.LocalScope)

Goes to coldfusion.runtime.TemplateClassLoader#findClass(ServletContext, String)

Goes to coldfusion.util.SoftCache#get

Goes to get_statsOn or get_statsOff

fetch as implemented in TemplateCache will use the ColdFusion compiler NeoTranslator to translate the files contents into a Java class via a call to coldfusion.compiler.NeoTranslator.translateJava.

Finally translateJava will perform action: the file will be compiled on the fly as source code for a Java ColdFusion component or module.

The root cause is in coldfusion.runtime.JSONUtils#convertToTemplateProxy.

This function called when we perform action:

coldfusion.runtime.JSONUtils#deserializeJSON(Object, boolean, boolean)

→coldfusion.runtime.JSONUtils#parseJSON

→coldfusion.runtime.JSONUtils#parseObject

→coldfusion.runtime.JSONUtils#convertToTemplateProxy

Found a place that deserializeJSON is called, i got coldfusion.filter.ComponentFilter#invoke get input from _variables

To trigger this vuln, we have to bypass three check:

  1. pagePath not end with "cfr"

  2. method is not null

  3. CFClientCall must be true

The pagePath is the path to any file in webroot folder that accessible, so can be any .cfc file for example (because this filetype can be accessed directly).

The method is taken from url, so can be set as any value via GET parameter.

The context is a FusionContext, and CFCClientCall has been taken from a GET parameter called _cfclient and set value to true

In short, our endpoint will look like:

/path/to/accessible/file.cfc?method=hehe&_cfclient=true

And our payload will look like:

_variables={"_metadata":{"classname":"../../../../../../../../../../../etc/passwd"}}

Some POC:

RCE

ColdFusion Markup Language (CFML) includes a set of tags that you use in ColdFusion pages to interact with data sources, manipulate data, and display output. CFML tag syntax is similar to HTML element syntax.

Attacker can take advantage of CFML to execute a shell command. For example <cfexecute name='calc'></cfexecute> will pop a calc.

In this scenario, firstly attacker send a request has the payload and it will be in log. Secondly, attacker use the above LFI to navigate to the log file. As we said before, NeoTranslator will translate the contents of an arbitrary file the attacker can specify as Java class of ColdFution, which is CFML. This technique is similar with log poisoning that we usually see in PHP.

So firstly we will send a request with payload:

_variables={<cfexecute name='sh touch /tmp/test.txt'></cfexecute>}

The result is 500 JSON parsing failure at character 1:'_' in _variables={<cfexecute name='touch /tmp/test.txt'></cfexecute>}

And we can see in the log file logs/coldfusion-out.log:

Now, we will send the payload: _variables={"_metadata":{"classname":"../logs/coldfusion-out.log"}}

And the result: