s2-061漏洞复现及分析文档
0x0 漏洞情况
0x01漏洞描述
Apache Struts2框架是一个用于开发Java EE网络应用程序的Web框架。Apache Struts于2020年12月08日披露 S2-061 Struts 远程代码执行漏洞(CVE-2020-17530)。S2-061是对S2-059沙盒进行的绕过。攻击者可构造特殊的OGNL表达式,造成远程代码执行。
0x02影响版本
Struts 2.0.0 - Struts 2.5.25
0x1 前置知识
0x11:Struts2标签解析
Struts2 会对某些标签属性(比如 id) 的属性值进行二次表达式解析,因此当这些标签属性中使用了 %{x} 且 x 的值我们可控时,我们再传入一个 %{payload} 即可造成OGNL表达式执行。
0x12:OGNL语言
OGNL(Object-Graph Navigation Language)–对象图导航语言。Struts2框架使用它作为默认的表达式语言,用于获取与设置Java对象的表达式语言,还附加一些例如集合投影、过滤、Lambda表达式的功能。
OGNL中有两个常用的类:
ognl.Ognl类:主要用来解析和解释执行OGNL表达式
ognl.OgnlContext类:这个类为OGNL表达式提供了一个执行环境,这个类实现了Map接口,允许通过put(key,obj)方法向OgnlContext环境中方式各种类型的对象,需要注意的是在OgnlContext中对象分为两种,第一种是叫做root对象(根对象),在整个OgnlContext中有且最多只能有一个根对象,可以通过调用OgnlContext.setRoot(obj)设置为根对象,另外一种就是OgnlContext中的普通对象,这种个数类型不受限制。要获取普通对象的属性值,只能通过“#对象名.name”的方式去获取属性值,且在对象的类型中要提供getName方法。
0x2 漏洞原理分析
已知OGNL沙盒的限制如下:
无法new一个对象
无法调用黑名单类和包的方法、属性
无法调用静态方法
无法直接执行命令
同时在ognl中,默认已经无法直接调用诸如Runtime等方法用来执行命令,如下:
1 | 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) |
所以需要一一绕过这些限制。
0x21 创建一个对象
在context的application中,org.apache.tomcat.SimpleInstanceManager中可以实例化一个无参构造函数。
利用newInstance 方法,className 参数可控,就可以实例化任意无参构造方法的类并返回。
代码如下:
1 | @Override |
0x22 间接获取ognl context
org.apache.commons.collections.BeanMap 这个类存在无参公开的构造方法:
跟进 this.initialise(),可以看到是把传入对象的class当做bean,并且传入name值。
get方法如下,通过我们传入的name值,调用 getreadMethods 中对应的 getXxx 方法
以下是getreadmethod方法:调用 readMethods 中对应的 getxxx方法。
而在com.opensymphony.xwork2.ognl.OgnlValueStack中,存在getContext方法,因此可以利用 BeanMap 间接获取到 OgnlContext。
0x23 清空黑名单
在ognl的securityMemberAccess有存放黑名单,我们需要将黑名单置空,才可以任意调用类。
通过beanMap中的put方法调用setExculdedClasses和setExcludedPackageNames可以覆盖掉黑名单,我们就可以实例化任意黑名单中的类。
0x24 寻找存在无参构造函数,又可以命令执行的类
黑名单中的freemarker.template.utility.Execute类存在无参构造方法Execute(),可以利用exec直接执行命令
0x3 漏洞复现
环境:
在docker的s2-059环境中使用cat > pom.xml << EOF命令修改pom.xml,添加依赖包
1 | <dependency> |
攻击:
根据漏洞原理编写poc,发包,即可看到返回的页面中执行了poc中的whoami命令