长亭百川云 - 文章详情

Struts2 系列漏洞 - S2-059、S2-061

Medi0cr1ty

37

2024-07-13

01

很久不见的絮叨

好像终于暂时告一段落,也是看了蛮久,中间还有一些我没有看的。虽然很多想说,但是言而总之总而言之希望我的记录对你有所帮助。

02

漏洞概述

在某些标签属性中,如 id , struts2 会对其中内容进行二次解析。假若开发者在某些标签属性中使用 %{…} 的形式传输值,而其中内容用户可控,那么此时传入形如 %{…} 的利用代码则可执行 ognl 表达式。

S2-061 是对 S2-059 的绕过,触发方式一样,所以就连一起了。

官方链接:

https://cwiki.apache.org/confluence/display/WW/S2-059

https://cwiki.apache.org/confluence/display/WW/S2-061

影响版本:

Struts 2.0.0 - Struts 2.5.20 - Struts 2.5.25

03

漏洞复现

环境:

apache-tomcat-9.0.37 、 jdk1.8.0_261 、 struts 2.5.20/struts 2.5.25

能成功运行即可,其中 struts.xml :

index.jsp :

payload :%25{233*233}

payload :%25{(#im=#application['org.apache.tomcat.InstanceManager']).(#bm=#im.newInstance('org.apache.commons.collections.BeanMap')).(#vs=#request['struts.valueStack']).(#bm.setBean(#vs)).(#context=#bm.get('context')).(#bm.setBean(#context)).(#access=#bm.get('memberAccess')).(#bm.setBean(#access)).(#empty=#im.newInstance('java.util.HashSet')).(#bm.put('excludedClasses',#empty)).(#bm.put('excludedPackageNames',#empty)).(#cmdout=#im.newInstance('freemarker.template.utility.Execute').exec({'whoami'}))}

(这个 payload 执行成功,tomcat 版本不能再使用前面的 6.0.10 啦;同时还需要引入 commons-collections 的 jar 包:)

`<!-- https://mvnrepository.com/artifact/commons-collections/commons-collections -->``<dependency>`  `<groupId>commons-collections</groupId>`  `<artifactId>commons-collections</artifactId>`  `<version>3.2.2</version>``</dependency>`

04

漏洞分析

发送的请求进行一系列处理后,返回 jsp 文件,对其中的标签进行解析。来到 doStartTag :

populateParams 中对 a 标签属性进行赋值。跟进 org.apache.struts2.views.jsp.ui.AnchorTag#populateParams --> org.apache.struts2.views.jsp.ui.AbstractClosingTag#populateParams --> org.apache.struts2.views.jsp.ui.AbstractUITag#populateParams

跟进 org.apache.struts2.components.UIBean#setId

其中调用了 findString 方法,在这个方法中将 name 值从 root 栈中取出。(其中 altSyntax 要为 true 默认为 true )取出如图中 o 所示。UIBean 中的 id 属性也就是我们的 payload ,此时还没有对其进行再一次解析,仅仅只是取出。(前面 S2-012 就修复了故不会造成递归解析的现象)

回到 org.apache.struts2.views.jsp.ComponentTagSupport#doStartTag ,接下来下一步跟进 org.apache.struts2.components.Anchor#start --> org.apache.struts2.components.ClosingUIBean#start

进入 org.apache.struts2.components.UIBean#evaluateParams 在这里对 %{…} 中的内容进行了再一次解析。

最终同样调用 com.opensymphony.xwork2.util.OgnlTextParser#evaluate ,在其中解析执行了 ognl 表达式。

接下来重点看一下 struts2 的变化,以及 payload 的演变。

struts2.5.20 和 struts2.5.21 对比:

https://github.com/apache/struts/compare/STRUTS\_2\_5\_20...STRUTS\_2\_5\_21

struts-default.xml :(将 com.opensymphony.xwork2.ActionContext 、com.opensymphony.xwork2.ognl 包都给屏蔽了,那么通过 #context=#attr['struts.valueStack'].context 的形式获得 context 就行不通了。因为其是通过 com.opensymphony.xwork2.ognl.OgnlValueStack 的 getContext 获得 OgnlContext 。同时也不能通过 com.opensymphony.xwork2.ActionContext.container 来创建实例了)

struts2.5.16 和 struts2.5.20 对比:

https://github.com/apache/struts/compare/STRUTS\_2\_5\_16...STRUTS\_2\_5\_20

com.opensymphony.xwork2.ognl.OgnlUtil.java :都变成了 protected 修饰,com.opensymphony.xwork2.ognl.SecurityMemberAccess 没变。

前面用到的 payload 不再有效。这次 payload 中主要使用到的是 org.apache.tomcat.InstanceManager 类以及 org.apache.commons.collections.BeanMap 类。不能使用 tomcat6.0.10 的版本也是因为其 jar 包中没有 InstanceManager 类,而引入 commons-collections 的 jar 包也是为了将 BeanMap 类引用进来。

我们可以依次看一下两个类在 payload 中用到的方法。

InstanceManager 接口的实现类 DefaultInstanceManager 中 newInstance 方法。

跟进 clazz.getConstructor() 看到会去找 clazz 中 public 的无参构造方法,并通过其返回一个实例。

org.apache.catalina.core.DefaultInstanceManager#newInstance :

BeanMap 类中 setBean 、 get 、 put 方法:

org.apache.commons.collections.BeanMap#reinitialise 中调用 initialise 方法。取出当前 bean 的 set 、get 方法并将其存入 writeMethod 、 readMethod 集合中。

而 BeanMap 中的 get 方法则是可以通过当前 bean 的某个 get 方法返回一个对象。那么是不是可以首先通过 setBean 方法将 OgnlValueStack 中的 getContext 方法存入当前 readMethods 集合的 context 键值中(具体看上图中 279 行 Introspector.getBeanInfo 中的逻辑),接下来可以通过 get 方法获得 OgnlContext 对象。(同样也能获得 MemberAccess 实例)

BeanMap 中的 put 方法传入 name 、 value 就是通过 name 获得针对当前 bean 的具体方法,接下来通过 value 获取到具体的参数,最终通过反射调用方法。那么是不是当前 bean 为 SecurityMemberAccess 的话就能调用他的 setExcludedPackageNames 等方法从而将黑名单置空。

接下来通过实例化 freemarker.template.utility.Execute 调用它的 exec 方法来执行命令。其实里面就是通过 Runtime.getRuntime().exec() 执行命令。【那为什么不直接实例化 Runtime 类的实例呢?因运行时仍会屏蔽该类

05

漏洞修复

增加了新的黑名单:

那接下来怎么弄呢...........

参考链接:

https://securitylab.github.com/research/apache-struts-double-evaluation

https://mp.weixin.qq.com/s/RD2HTMn-jFxDIs4-X95u6g

相关推荐
关注或联系我们
添加百川云公众号,移动管理云安全产品
咨询热线:
4000-327-707
百川公众号
百川公众号
百川云客服
百川云客服

Copyright ©2024 北京长亭科技有限公司
icon
京ICP备 2024055124号-2