Spring4Shell(CVE-2022-22965)

声明

出品|博客(ID:moon_flower)

以下内容,来自博客moon_flower作者原创,由于传播,利用此文所提供的信息而造成的任何直接或间接的后果和损失,均由使用者本人负责,长白山攻防实验室以及文章作者不承担任何责任。

环境搭建

github上拉了一个现成的spring+tmcat环境:https://github.com/winn-hu/interface。可以在其中添加实验用的model和controller。

漏洞成因

这次的CVE-2022-22965其实是CVE-2010-1622的绕过,由参数绑定造成的变量覆盖漏洞,通过更改tomcat服务器的日志记录属性,触发pipeline 机制实现任意文件写入。

SpringMVC的参数绑定机制

演示 demo:
HelloController.java

@Controller
public class HelloController {
    @RequestMapping("/index")
    public String index(User user) {
        return user.toString();
    }
}

User.java

package com.moonflower.model;
import com.moonflower.model.info;
public class User {
    public String name;
    public String age;
    public com.moonflower.model.info info;
    public User(String name, String age, com.moonflower.model.info info) {
        this.name = name;
        this.age = age;
        this.info = info;
        System.out.println("调用了User的有参构造");
    }
    public User() {
        System.out.println("调用了User的无参构造");
    }
    public String getName() {
        System.out.println("调用了User的getName");
        return name;
    }
    public void setName(String name) {
        System.out.println("调用了User的setName");
        this.name = name;
    }
    public com.moonflower.model.info getInfo() {
        System.out.println("调用了User的getInfo");
        return info;
    }
    public void setInfo(com.moonflower.model.info info) {
        System.out.println("调用了User的setInfo");
        this.info = info;
    }
    @Override
    public String toString() {
        return "User{" +
                "name='" + name + '\'' +
                ", info=" + info +
                '}';
    }
}

info.java

package com.moonflower.model;
public class info {
    public String QQ;
    public String vx;
    public info(String QQ, String vx) {
        this.QQ = QQ;
        this.vx = vx;
        System.out.println("调用了info的有参构造");
    }
    public info() {
        System.out.println("调用了info的无参构造");
    }
    public String getQQ() {
        System.out.println("调用了info的getQQ");
        return QQ;
    }
    public void setQQ(String QQ) {
        System.out.println("调用了info的setQQ");
        this.QQ = QQ;
    }
    public String getVx() {
        System.out.println("调用了info的getvx");
        return vx;
    }
    public void setVx(String vx) {
        this.vx = vx;
        System.out.println("调用了info的setvx");
    }
    @Override
    public String toString() {
        return "info{" +
                "QQ='" + QQ + '\'' +
                ", vx='" + vx + '\'' +
                '}';
    }
}

首先尝试访问/index?

name=moonflower&info.QQ=123&info.vx=13,在执行完toString之后,可以看到传入的name自动绑定到了user.name上,而 info.QQ和info.vx也分别自动绑定到了user.info.QQ和user.info.vx上,这也表明了SpringMVC持多层嵌套的参数绑定。
图片再看一下输出的内容,能看出参数的绑定先get后set,而对于多层嵌套绑定(info.QQ),则是依次调用了User.getinfo->info.getQQ->info.setQQ
图片执行参数绑定的函数可以跟进

ServletRequestDataBinder类中
图片继续跟进到doBind中,发现其又调用了父类的doBind,
图片图片在applyPropertyValues中添加参数的值
图片首先调用getPropertyAccessor获取,Bean-WrapperImpl然后调用setPropertyValues赋值,在setPropertyValues中循环调用setPropertyValue,为每一个propertyname赋值

(图中已经是赋值完 QQ,开始赋值 vx)

图片然后在setPropertyValue中持续跟进,

一直到 getPropertyAccessorForPropertyPath,
图片在getPropertyAccessorForPropertyPath中解析了即将绑定的参数(info.vx)
图片再跟到 getPropertyValue 中
图片在getLocalPropertyHandler中,BeanWrapperImpl的方法拿到了info类
图片继续跟到setDefaultValue,在setDefaultValue 又会调用 createDefaultPropertyValue 中
图片在createDefaultPropertyValue的newValue中可以看到反射构造
图片图片这时看一下output,发现已经打印了调用info的无参构造
图片回到setDefaultValue中,接着调用里setPropertyValue方法,
图片继续跟进到解析对应的参数,而这里解析到的是一个info类,
图片就像刚开始说的那样,在当前要绑定的参数 (info) 无法直接赋值的时候,会进行多层嵌套的参数绑定,可以看到程序又会回到 getProperty-AccessorForPropertyPath 中,而且参数从 info.QQ 变成了 QQ,然后继续跟进,就可以看到给对应属性(QQ)的赋值操作
图片在后续的getValue函数中,通过反射的方法调用了对应的get方法(getQQ),
图片继续向下跟进到setValue 中,同样也是用反射调用了对应的set方法,此时 output中出现对应打印内容。
图片大致流程(图来自rui0师傅)
图片

关于JavaBean

在上面的例子中声明的类(User, info)都是JavaBean,一种特殊的类。主要用于传递数据信息,要求方法符合某种命名规则,在这些bean中通常只有信息字段和存储方法,没有功能性方法。

对于JavaBean中的私有属性,可以通过getter/setter方法来访问/设置,在 jdk中提供了一套api来访问某个属性的getter/setter方法,也就是内省。

BeanInfo getBeanInfo(Class beanClass)
BeanInfo getBeanInfo(Class beanClass, Class stopClass)

在获得BeanInfo后,可以通过 PropertyDescriptors 类获取符合JavaBean 规范的对象属性和getter/setter方法。

(如果用IDEA调过前面参数绑定的过程,就会发现在Spring中对JavaBean 的操作不是用getBeanInfo(太麻烦了),而是用BeanWrapperImpl 这个类的各种方法来操作。

BeanWrapperImpl类是BeanWrapper接口的默认实现,可以看作前面提到的PropertyDescriptor的封装,BeanWrapperImpl 对Bean的属性访问和设置最终调用的是PropertyDescriptor。)
demo:

public class demo {
    public static void main(String[] args) throws Exception {
        BeanInfo userBeanInfo = Introspector.getBeanInfo(User.class);
        PropertyDescriptor[] descriptors = userBeanInfo.getPropertyDescriptors();
        for (PropertyDescriptor pd : descriptors) {
            System.out.println("Property: " + pd.getName());
        }
    }
}

程序跑起来的时候可以发现,User的属性(name,info)及其方法都在 PropertyDescriptor中可以拿到,
图片但除此之外,还能拿到一个Class类,而且自带一个getClass方法。
图片这里是因为没有使用stopClass,访问该类的时候访问到了Object.class,而内省机制的判定规则是,只要由getter/setter方法中的一个,就会认为存在一个对应的属性,而碰巧的是,Java中的所有对象都会默认继承Object类,同时它也存在一个getClass方法,这样就解析到了class属性。
如果直接调用:

Introspector.getBeanInfo(Class.class)

可以获取更多信息,包括关键的classLoader。
图片

参考

  • https://paper.seebug.org/1877/

  • https://mp.weixin.qq.com/s/kc7XP3K98c62Z-Euyz1EZA

  • https://mp.weixin.qq.com/s/G1z7mydl4nc9SxcZjwUQwg

  • http://rui0.cn/archives/1158

  • https://blog.csdn.net/weixin_45794666/article/details/123918066

  • https://www.iteye.com/topic/1123382

  • https://www.exploit-db.com/exploits/33142

  • https://github.com/BobTheShoplifter/Spring4Shell-POC/blob/0c557e85ba903c7ad6f50c0306f6c8271736c35e/poc.py

  • https://github.com/spring-projects/spring-framework/commit/002546b3e4b8d791ea6acccb81eb3168f51abb15

  • https://github.com/apache/tomcat/commit/8a904f6065080409a1e00606cd7bceec6ad8918c

欢迎关注长白山攻防实验室公众号
定期更新优质文章分享

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值