action的name可以设置为*,当URL没有匹配到任何action的时候,就会匹配*对应的action;而一般开发中获取*对应的字符串,然后result返回以该字符串命名的jsp,获取该字符串用的是占位符{x},依照星号从左往右从1开始匹配
例如:
<action name="*-*" class="action.EmptyAction">
<result>/{1}-{2}.jspresult>
action>
那么访问URL/xxx-yyy时,result会返回xxx-yyy.jsp,此时字符串会被OGNL解析

Struts 2.0.0 - 2.3.14.2
写一个空action

然后匹配任意action,通过占位符取出action的名称,并返回对应jsp

URL编码,访问以下action即可
/${new java.lang.ProcessBuilder('calc').start()}.action
/%{#context['xwork.MethodAccessor.denyMethodExecution']=false,#f=#_memberAccess.getClass().getDeclaredField('allowStaticMethodAccess'),#f.setAccessible(true),#f.set(#_memberAccess,true),@java.lang.Runtime@getRuntime().exec('calc')}.action

.action影响OGNL解析的原因最开始payload尝试/${new java.lang.ProcessBuilder('calc').start()}并不能弹出计算器,包括直接反射调用Runtime也是不能弹出计算器,必须URL末尾加.aciton才能注入

然而官方演示的/${#a='index',#a}没有.action也能注入成功

审计前先探究这个问题,在struts的filterStrutsPrepareAndExecuteFilter下面处理Action映射

打上断点发送请求/${new java.lang.ProcessBuilder('calc').start()}.action,来到DefaultActionMapper.getMapping(),这一步把后缀截断返回了OGNL部分,交由后续*匹配到action

而/${new java.lang.ProcessBuilder('calc').start()}返回的是null!所以就不会被*匹配到

看来关键还是在上一步dropExtension()的处理,看名称就知道是处理后缀的方法,重新发个请求看看;这个方法处理两种后缀:action和无后缀

第一轮处理action进入else,第二轮处理空后缀进入if条件

看上述代码:如果是以.action结尾的就去除后缀直接return了,看一下没有.action后缀的情况(这也是没办法注入成功的原因):
首先获取了.最后出现的位置,如果没出现index是-1,这里因为有类名和方法调用肯定出现了点,index返回的是.start()中的点的下标;然后从最后一个点向后找有没有出现/,显然没有

条件都是false,就直接结束条件判断,方法返回null了

这是正常的处理逻辑没有action后缀自然不会判定是一个action,从上面可以看出来:/${#a='index',#a}能注入成功是原因是它没有.,在第二轮判断直接返回了

处理完映射后执行action

多个拦截器处理后,来到DefaultActionInvocation.executeResult()

可以看到此时已经按照result的处理加上了后缀

之后就是渲染解析触发OGNL注入

默认限制了action的格式

https://cwiki.apache.org/confluence/display/WW/S2-015
欢迎关注我的CSDN博客 :@Ho1aAs
版权属于:Ho1aAs
本文链接:https://blog.csdn.net/Xxy605/article/details/126555024
版权声明:本文为原创,转载时须注明出处及本声明