CVE-2022-22980 Spring Data MongoDB SpEL表达式注入
2022-7-4 15:48:7 Author: xz.aliyun.com(查看原文) 阅读量:41 收藏

昨天在推特上看到threedr3am师傅发的Spring Data MongoDB SpEL的环境,然后就自己去手动分析了一波,这里用的版本是Spring Data MongoDB 3.3.4

漏洞的原因是当使用@Query@Aggregation注解进行数据库查询,且使用了占位符获取参数导致的Spel表达式注入

diif上可以看到漏洞触发的点

org.springframework.data.mongodb.util.json.ParameterBindingJsonReader#bindableValueFor中进行绑定参数时触发漏洞,直接在327行打个断点调试,此时expression的值就是构造好的payload,但此时并不会触发漏洞,会在下一次再到这个地方时才会触发漏洞,这里跟进到this.evaluateExpression中看一下

此时this.expressionEvaluator的值为ParameterBindingDocumentCodec,此时会进入到ParameterBindingDocumentCodec中对expression进行处理,最后会返回一个空的对象

接下来比较了在同一个地方两个的堆栈,发现是在org.springframework.data.mongodb.repository.query.StringBasedMongoQuery#createQuery中先进入到getBindingContext进行了参数绑定,看到此时传入的codec就是ParameterBindingDocumentCodec导致第一次并没有触发漏洞,然后绑定参数后在进入到decode中最后会再次进入bindableValueFor

先来看一下第一次进行参数绑定时进行了什么操作,在org.springframework.data.mongodb.util.json.ParameterBindingJsonReader#readBsonType中,通过switch判断token的Type属性,进入到UNQUOTED_STRING中,在这里进行setCurrentName操作,该值是在bindableValueFor中通过一系列操作后获得,其为实体类中的id参数

接着继续往下走,会经过很多对value值进行equals的对比,此时value是:#{?0},肯定是false的,最后进入到bindableValueFor中,首先是先把值传给了tokenValue,然后先后对其进行了PARAMETER_BINDING_PATTERNEXPRESSION_BINDING_PATTERN规则匹配表达式,然后取出值交给binding,在通过substring取出占位符?0,接下来通过for循环将一开始传进来的payload和占位符进行替换,然后执行this.evaluateExpression,因为同时传入的codec,只是返回了一个空的Object实例,最后将value和type进行set后返回bindableValue,这里感觉就是先对实体类的id参数进行了绑定

private static final Pattern PARAMETER_ONLY_BINDING_PATTERN = Pattern.compile("^\\(\\d+)$");
private static final Pattern PARAMETER_BINDING_PATTERN = Pattern.compile("\\?(\\d+");
private static final Pattern EXPRESSION_BINDING_PATTERN = Pattern.compile("[\\?:#\\{.*\\}");

接着进行this.getBindingContext后,会进入到decode中,最后进行一样的操作,此时this.expressionEvaluatorDefaultSpELExpressionEvaluator,最后执行getValue触发SpEl表达式注入

这里引入的是Spring Data MongoDB 3.3.4

在新版本中额外增加了一个规则匹配表达式,并对binding也就是:#{?0}进行匹配,然后将传进来的payload放入到innerSpelVariables的键值对里,key为特殊字符,最后和binding一起传入到this.evaluateExpression

private static final Pattern SPEL_PARAMETER_BINDING_PATTERN = Pattern.compile("('\\?(\\d+)'|\\?(\\d+))");

其中进行了三元运算符判断,判断的是this.expressionEvaluator是否为EvaluationContextExpressionEvaluator的实例,然后会是false进入到evaluate,此时传进来的是键值对中的key#__QVar0然后无法触发SpEL表达式注入

public Object evaluateExpression(String expressionString, Map<String, Object>variables) {
    return this.expressionEvaluator instanceof EvaluationContextExpressionEvaluator ? ((EvaluationContextExpressionEvaluator)this.expressionEvaluator).evaluateExpression(expressionString, variables) : this.expressionEvaluator.evaluate(expressionString);
}

  1. https://github.com/threedr3am/learnjavabug/tree/master/spring/spring-data-mongodb-spel-CVE-2022-22980
  2. https://github.com/spring-projects/spring-data-mongodb/commit/7c5ac764b343d45e5d0abbaba4e82395b471b4c4?diff=split

文章来源: https://xz.aliyun.com/t/11484
如有侵权请联系:admin#unsafe.sh