s2-061漏洞复现及分析文档

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等方法用来执行命令,如下:

image-20201210174949997

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
2
3
4
5
6
7
@Override
public Object newInstance(String className) throws IllegalAccessException,
InvocationTargetException, NamingException, InstantiationException,
ClassNotFoundException, NoSuchMethodException {
Class<?> clazz = Thread.currentThread().getContextClassLoader().loadClass(className);
return prepareInstance(clazz.getConstructor().newInstance());
}
0x22 间接获取ognl context

org.apache.commons.collections.BeanMap 这个类存在无参公开的构造方法:

image-20201210174129548

跟进 this.initialise(),可以看到是把传入对象的class当做bean,并且传入name值。

image-20201210175445871

get方法如下,通过我们传入的name值,调用 getreadMethods 中对应的 getXxx 方法

image-20201210174211228

以下是getreadmethod方法:调用 readMethods 中对应的 getxxx方法。

image-20201210174225413

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

image-20201210174000271

0x23 清空黑名单

在ognl的securityMemberAccess有存放黑名单,我们需要将黑名单置空,才可以任意调用类。

通过beanMap中的put方法调用setExculdedClasses和setExcludedPackageNames可以覆盖掉黑名单,我们就可以实例化任意黑名单中的类。

image-20201210175929284

image-20201210174039736

0x24 寻找存在无参构造函数,又可以命令执行的类

黑名单中的freemarker.template.utility.Execute类存在无参构造方法Execute(),可以利用exec直接执行命令

image-20201210174340623

0x3 漏洞复现

环境:

在docker的s2-059环境中使用cat > pom.xml << EOF命令修改pom.xml,添加依赖包

1
2
3
4
5
<dependency>
<groupId>commons-collections</groupId>
<artifactId>commons-collections</artifactId>
<version>3.2.2</version>
</dependency>

攻击:

根据漏洞原理编写poc,发包,即可看到返回的页面中执行了poc中的whoami命令

image-20201210182827945