长亭百川云 - 文章详情

Spring 参数绑定的分析以及甲方自查

宽字节安全

48

2024-07-13

简介

环境,找个spring参数绑定的博客就行,这里给个环境例子https://github.com/wycm/SpringMVC-Demo.git。

具体参考SpringMVC参数绑定入门就这一篇 https://segmentfault.com/a/1190000022586808 

该漏洞的本质类似于php的变量覆盖漏洞,exp利用的话,恰好覆盖到tomcat的配置,并修改tomcat的日志位置到根目录,修改日志的后缀为jsp。但是这里叫SpringMVC的参数绑定。如图

http://localhost:8080/web_war/ParameterBind/test2?name=aa

如果我们把name这个变量给User这个对象,User对象的代码如下

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

正常思维是从Http get参数中获取name的值 实例化对象,然后赋值,但是spring框架简化这个过程,所以就叫参数绑定。spring代码如下

    @ResponseBody     @RequestMapping("/test2")     public String test2(User u){         System.out.println(u.toString());         return "test2";     }

也就是说spring从http请求中自动解析变量,并给user对象。

可以想象,该项技术的实现必然有大量的反射技术。下面我们来分析一下实现过程。

参数绑定实现过程

org.springframework.beans.AbstractPropertyAccessor#setPropertyValues(org.springframework.beans.PropertyValues, boolean, boolean)

在这里开始,将http请求中每一个kv对,设置到bean对象上,

 public void setPropertyValues(PropertyValues pvs, boolean ignoreUnknown, boolean ignoreInvalid)    throws BeansException {   List<PropertyAccessException> propertyAccessExceptions = null;   List<PropertyValue> propertyValues = (pvs instanceof MutablePropertyValues ?     ((MutablePropertyValues) pvs).getPropertyValueList() : Arrays.asList(pvs.getPropertyValues()));   for (PropertyValue pv : propertyValues) {     // This method may throw any BeansException, which won't be caught     // here, if there is a critical failure such as no matching field.     // We can attempt to deal only with less serious exceptions.     setPropertyValue(pv);

image.png

org.springframework.beans.BeanWrapperImpl#setPropertyValue(org.springframework.beans.PropertyValue)

 public void setPropertyValue(PropertyValue pv) throws BeansException {   PropertyTokenHolder tokens = (PropertyTokenHolder) pv.resolvedTokens;   if (tokens == null) {    String propertyName = pv.getName();    BeanWrapperImpl nestedBw;    try {     nestedBw = getBeanWrapperForPropertyPath(propertyName);    }    catch (NotReadablePropertyException ex) {     throw new NotWritablePropertyException(getRootClass(), this.nestedPath + propertyName,       "Nested property in path '" + propertyName + "' does not exist", ex);    }    tokens = getPropertyNameTokens(getFinalPath(nestedBw, propertyName));    if (nestedBw == this) {     pv.getOriginalPropertyValue().resolvedTokens = tokens;    }    nestedBw.setPropertyValue(tokens, pv);   }   else {    setPropertyValue(tokens, pv);   }  }

在getBeanWrapperForPropertyPath中,开始解析http中的key,也就是类似于这类请求

下一个调用上一个的get + 属性名。在这里就是调用class的setModel方法,参数为aa,字符串类型。也就是设置class的Model值为aa。那么问题来了,class是谁?所以对于参数绑定来讲,就是你的那个bean对象的属性。也就是系统默认会有name和age。但是偏偏多了一个class,指向bean对象的类的引用。导致通过这个class引用,修改非bean对象的属性的值。也就造成了变量覆盖。

但是通过参数绑定去修改的对象有限,必须能通过class为起始对象,并且可以通过无参get方法获取到引用,必须有get/set方法。修改的值必须为字符串。

每个bean对象的Propery的cache,在初始化的时候由下面的方法调用生成。

org.springframework.beans.CachedIntrospectionResults#CachedIntrospectionResults

这也就是为什么很多exp在Java8不可以的原因。

当初 Spring 修复了 CVE-2010-1622,修复方式是拦截 Class.getClassLoader的访问,也就是如上图,但是Java9新增了可以通过Class.getModule方法。通过getModule的结果可以调用getClassloader的方式继续访问更多对象的属性。

https://docs.oracle.com/javase/9/docs/api/java/lang/Module.html

org.springframework.beans.BeanWrapperImpl#setPropertyValue(org.springframework.beans.BeanWrapperImpl.PropertyTokenHolder, org.springframework.beans.PropertyValue)

调用set+属性名的方法,设置bean的值

class.module.classLoader.resources.context.parent.pipeline.first.prefix我们可以发现,可以直接从class中获取到tomcat的context,在context中存储很多东西,例如修改日志路径属性等等,修改的值为字符串,完美符合本次漏洞的需求。

但是weblogic的context会不会可以通过class获取到引用很难说。所以影响的局限性不限于tomcat这一种中间件。

自查方案

目前exp具体影响不明,因为一个完美的武器级exp需要满足

  1. 必须要能通过class为起始对象获取到引用(深度搜索的起点为class),并且还要有无参get方法

  2. 必须有get/set方法,符合java bean规范。

  3. set方法的值必须为字符串

  4. 该controller必须存在spring的参数绑定。

目前只流传tomcat的exp,不排除其他中间件的exp,不排除dos等其他漏洞的exp。

waf规则

现在我们知道为什么java9可以而java8不可以的原因,所以我们可以断定class.module这串字符串一定出现在exp的请求中,可以重点防御这串字符串

甲方自查手册

  1. 确定线上业务中的controller是否使用了spring的参数绑定技术,如果使用则按照下一条继续排查

  2. jdk版本是否为jdk9以上,jdk8以下天然防御

因为该漏洞的本质是变量覆盖漏洞,但是利用手法通过覆盖tomcat的配置修改tomcat的日志位置到根目录,修改日志的后缀为jsp去getshell。

  1. 如果防止getshell,则重点排查中间件是否为tomcat。

  2. 非tomcat中间件目前来说不一定会被getshell,但是存在被该漏洞影响到线上业务的风险(任意变量覆盖到中间件的其他变量配置,导致dos等其他场景),建议停机修改应用,修复方式如下

注意,目前只有通过应用下线重发布的方式打补丁,并且非spring官方推荐修复,存在一定几率的翻车风险。同时按以下两个步骤进行漏涧的临时修复:

1.在应用中全局搜索@InitBinder注解,看看方法体内是否调用dataBinder.setDisallowedFields方法,如果发现此代码片段的引入,则在原来的黑名单中,添加{"class.","Class. ",". class.", ".Class."}。(注:如果此代码片段使用较多,需要每个地方都追加)

在应用系统的项目包下新建以下全局类,并保证这个类被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 a{ @InitBinder public void setAllowedFields(WebDataBinder dataBinder) { String[] abd = new String[]{"class.*", "Class.*", "*.class.*", "*.Class.*"}; dataBinder.setDisallowedFields(abd); } }

相关推荐
关注或联系我们
添加百川云公众号,移动管理云安全产品
咨询热线:
4000-327-707
百川公众号
百川公众号
百川云客服
百川云客服

Copyright ©2024 北京长亭科技有限公司
icon
京ICP备 2024055124号-2