Java安全-深入BeanValidation的RCE漏洞

前面

在学习Java的表达式的时候,学习了Thymeleaf造成的模板注入漏洞,后续又看到了相似的注入问题,在参数验证的错误消息中。漏洞简单描述就是,用户控制器的Java Bean 的属性(来自于HTTP请求)被连接到 Bean Validation 的错误信息中,错误信息会被处理,其中的EL表达式会被执行,最后插入到违规信息并返回。

这里面可以提取到的重要信息:

  • 使用 Java Bean Validation 验证

  • 验证用户可控的 Java Bean

  • Java Bean 的不合法属性回显到错误消息中(从回显处fuzz模板注入

Bean Validation

这是在 JSR(303) 提出来的,是Java定义的一套基于注解的数据规范验证。现在已经到了 JSR(380) 的Bean Validation 2.0 版。

使用他可以简单的在需要验证的类或者属性上加上对应的注解,就可以使用内置或者自定义验证器对Bean进行验证,优势就是只需要约束一次,不用在需要验证的所有接口处加入大量的if-else 判断,方便代码维护,简化代码量。

demo

写一个springbootbean validation 的例子。

加入依赖

<dependency>
<groupId>org.hibernate.validator</groupId>
<artifactId>hibernate-validator</artifactId>
<version>6.1.2.Final</version>
</dependency>
<dependency>
<groupId>org.hibernate.javax.persistence</groupId>
<artifactId>hibernate-jpa-2.1-api</artifactId>
<version>1.0.0.Final</version>
</dependency>

Hibernate Validator 是 Bean Validation 的实现,其中包含了

Java安全-深入BeanValidation的RCE漏洞jakarta是java改名而来的。

创建一个 Input的 Java Bean

public class Input {

@Id
@GeneratedValue
@NotNull
@Null
private Long id;

@Min(1)
@Max(10)
@Column
private int numberBetweenOneAndTen;

@IpAddress
@Column
private String ipAddress;

//setter getter ……

注意上面的@IpAddress 注解需要创建

@Target({ FIELD })
@Retention(RUNTIME)
@Constraint(validatedBy = IpAddressValidator.class)
@Documented
public @interface IpAddress {

String message() default "";

Class<?>[] groups() default { };

Class<? extends Payload>[] payload() default { };

}

@Constraint(validatedBy = IpAddressValidator.class) 用来指定由哪个类进行验证,创建它。

验证器需要继承自ConstraintValidator 接口,此接口使用了泛型,第一个参数是自定义的注解,第二个参数是校验的数据类型。

public class IpAddressValidator implements ConstraintValidator<IpAddress, String> {

@Override
public boolean isValid(String value, ConstraintValidatorContext context) {
Pattern pattern = Pattern.compile("^([0-9]{1,3})\.([0-9]{1,3})\.([0-9]{1,3})\.([0-9]{1,3})$");
Matcher matcher = pattern.matcher(value);
boolean isValid = true;
String message = "";
try {
if (!matcher.matches()) {
isValid = false;
message = value+"不匹配";
} else {
for (int i = 1; i <= 4; i++) {
int octet = Integer.valueOf(matcher.group(i));
if (octet > 255) {
isValid = false;
message = value + "大于255";
}
}
}
} catch (Exception e) {
isValid = false;
}
if(!isValid){
context.disableDefaultConstraintViolation();
context.buildConstraintViolationWithTemplate("invalid ip :" + message)
.addConstraintViolation();
}
return isValid;

}

}

然后写一个Controller,加上@Valid 注解

@RestController
class ValidateRequestBodyController {

@PostMapping("/validateBody")
ResponseEntity<String> validateBody(@Valid @RequestBody Input input) {
return ResponseEntity.ok("valid");
}

}

此时已经可以验证参数了,不过参数不合法的话会直接返回 400 和415 状态码,这并不友好,上面的自定义验证器添加的错误信息也没有意义了。

参数验证不通过抛出的异常是,

Java安全-深入BeanValidation的RCE漏洞

MethodArgumentNotValidException

Spring 提供一个 @ExceptionHandler 注解,创建一个@ConrtrollerAdvice控制器接口,用于全局的异常处理,使用fastjson来回显json格式的错误信息。

@ControllerAdvice
class ErrorHandlingControllerAdvice {
// 处理接口参数数据格式错误异常
@ExceptionHandler(MethodArgumentNotValidException.class)
@ResponseStatus(HttpStatus.BAD_REQUEST)
@ResponseBody
public Object errorHandler(HttpServletRequest request, MethodArgumentNotValidException e) {
Map<String,String> error = new HashMap<>();
for (FieldError fieldError : e.getBindingResult().getFieldErrors()) {
error.put(fieldError.getField(),fieldError.getDefaultMessage());
}
return JSON.toJSONString(error);
}
}

Java安全-深入BeanValidation的RCE漏洞

可以发现 ipAddress的错误信息里直接把内容输出出来,尝试 EL表达式注入。

Java安全-深入BeanValidation的RCE漏洞

跟一下大概的逻辑,

验证失败后,写入自定义的错误信息,

Java安全-深入BeanValidation的RCE漏洞

然后在抛出带有错误信息的MethodArgumentNotValidException异常。

Java安全-深入BeanValidation的RCE漏洞

后面就是Springboot捕获异常,并使用特定的异常处理接口来处理。

问题还是出在验证过程中对于错误信息的处理,添加错误信息后,violatedConstraintValidatorContexts 不为空,

Java安全-深入BeanValidation的RCE漏洞

跟进,ValidationContext#addConstraintFailure

Java安全-深入BeanValidation的RCE漏洞

调用了 自身的interpolate 方法来插入消息,其中有两种主要的,一种是参数插值({})和表达式插值(${}),表达式插值会被执行Java 表达式语言引擎执行。

Java安全-深入BeanValidation的RCE漏洞

不断跟进中间的 interpolate 方法,直到org.hibernate.validator.messageinterpolation.AbstractMessageInterpolator#interpolateMessage

Java安全-深入BeanValidation的RCE漏洞

如果存在 { 就会先处理 参数表达式插入,然后再处理EL 表达式。

Java安全-深入BeanValidation的RCE漏洞

造成EL表达式的注入。

interpolate:67, ElTermResolver (org.hibernate.validator.internal.engine.messageinterpolation)
interpolate:64, InterpolationTerm (org.hibernate.validator.internal.engine.messageinterpolation)
interpolate:159, ResourceBundleMessageInterpolator (org.hibernate.validator.messageinterpolation)
interpolateExpression:519, AbstractMessageInterpolator (org.hibernate.validator.messageinterpolation)
interpolateMessage:415, AbstractMessageInterpolator (org.hibernate.validator.messageinterpolation)
interpolate:355, AbstractMessageInterpolator (org.hibernate.validator.messageinterpolation)
interpolate:59, MessageSourceMessageInterpolator (org.springframework.boot.validation)
interpolate:51, LocaleContextMessageInterpolator (org.springframework.validation.beanvalidation)
interpolate:313, AbstractValidationContext (org.hibernate.validator.internal.engine.validationcontext)
addConstraintFailure:230, AbstractValidationContext (org.hibernate.validator.internal.engine.validationcontext)
validateConstraints:79, ConstraintTree (org.hibernate.validator.internal.engine.constraintvalidation)
doValidateConstraint:130, MetaConstraint (org.hibernate.validator.internal.metadata.core)
validateConstraint:123, MetaConstraint (org.hibernate.validator.internal.metadata.core)
validateMetaConstraint:555, ValidatorImpl (org.hibernate.validator.internal.engine)
validateConstraintsForSingleDefaultGroupElement:518, ValidatorImpl (org.hibernate.validator.internal.engine)
validateConstraintsForDefaultGroup:488, ValidatorImpl (org.hibernate.validator.internal.engine)
validateConstraintsForCurrentGroup:450, ValidatorImpl (org.hibernate.validator.internal.engine)
validateInContext:400, ValidatorImpl (org.hibernate.validator.internal.engine)
validate:172, ValidatorImpl (org.hibernate.validator.internal.engine)
validate:109, SpringValidatorAdapter (org.springframework.validation.beanvalidation)
validate:66, ValidatorAdapter (org.springframework.boot.autoconfigure.validation)
validate:895, DataBinder (org.springframework.validation)
validateIfApplicable:245, AbstractMessageConverterMethodArgumentResolver (org.springframework.web.servlet.mvc.method.annotation)
resolveArgument:139, RequestResponseBodyMethodProcessor (org.springframework.web.servlet.mvc.method.annotation)
resolveArgument:121, HandlerMethodArgumentResolverComposite (org.springframework.web.method.support)
getMethodArgumentValues:179, InvocableHandlerMethod (org.springframework.web.method.support)
invokeForRequest:146, InvocableHandlerMethod (org.springframework.web.method.support)
invokeAndHandle:117, ServletInvocableHandlerMethod (org.springframework.web.servlet.mvc.method.annotation)
invokeHandlerMethod:895, RequestMappingHandlerAdapter (org.springframework.web.servlet.mvc.method.annotation)
handleInternal:808, RequestMappingHandlerAdapter (org.springframework.web.servlet.mvc.method.annotation)
handle:87, AbstractHandlerMethodAdapter (org.springframework.web.servlet.mvc.method)
doDispatch
……

Nexus Repository Manager

CVE-2018-16621

影响版本:

Nexus Repository Manager OSS/Pro 3.x – 3.13

分析

org.sonatype.nexus.coreui.UserXO

Java安全-深入BeanValidation的RCE漏洞

存在自定义的验证注解,注解类型为org.sonatype.nexus.security.role.RolesExist

Java安全-深入BeanValidation的RCE漏洞

验证器为org.sonatype.nexus.security.role.RolesExistValidator

Java安全-深入BeanValidation的RCE漏洞

可以发现跟漏洞demo中的验证方法大致相同,可以在roles 参数处注入EL表达式。

所以存在验证roles参数的地方都可以触发漏洞。

Java安全-深入BeanValidation的RCE漏洞

修复

简单粗暴,

Java安全-深入BeanValidation的RCE漏洞

Java安全-深入BeanValidation的RCE漏洞

这个正则还防止了$${ 双写绕过的问题。

CVE-2020-10693 与 CVE-2020-10204

首先来说CVE-2020-10204 ,影响版本Nexus Repository Manager OSS/Pro 3.x -3.21.1,是对于上面的漏洞的绕过版本,使用了exp|$\A{2*333}| 来绕过正则,他的修复是又加了一个replaceAll

为什么会出现这种绕过,是因为hibernate-validator 在6.0.15.final 之前的版本中,可以将错误的表达式,类似上面的exp,解析成正确的表达式执行,也就是CVE-2020-10693

https://in.relation.to/2020/05/07/hibernate-validator-615-6020-released/

分析

org.hibernate.validator.internal.engine.messageinterpolation.parser.TokenCollector#parse 方法将会对消息进行解析。

Java安全-深入BeanValidation的RCE漏洞

Java安全-深入BeanValidation的RCE漏洞

Java安全-深入BeanValidation的RCE漏洞

通过遍历错误信息,

Java安全-深入BeanValidation的RCE漏洞

当前的字符是$

Java安全-深入BeanValidation的RCE漏洞

调用org.hibernate.validator.internal.engine.messageinterpolation.parser.MessageState#handleELDesignator方法, interpolationTermType 属性值为EL

Java安全-深入BeanValidation的RCE漏洞

目前的状态变更为 ELState

Java安全-深入BeanValidation的RCE漏洞

当匹配到  时,调用目前状态的currentParserState.handleEscapeCharacter

Java安全-深入BeanValidation的RCE漏洞

Java安全-深入BeanValidation的RCE漏洞

进入EscapedState 状态,接下来再匹配到{}$ 以外字符的话,调用EscapedState#handleNonMetaCharacter方法,

Java安全-深入BeanValidation的RCE漏洞

将当前的字符给到currentToken,返回上一个状态 (ElState)。

后面的代码不贴了,直接说好了,后面到{ 的时候,ELState#handleBeginTerm

currentToken 进行一些结束操作然后写入tokenList,主要是定义其isEL属性。

然后给currentToken赋值为null,然后给他添加有 ${ 的value,然后,isELisParameter为true。

进入InterpolationTermState 状态,此状态就是不断添加字符,直到}

Java安全-深入BeanValidation的RCE漏洞

主要就是ELState#handleEscapeCharacter 方法,直接进入EscapedState 状态,使得$再次遇到其他字符时,继续返回ELState状态,从而获取了合法的EL表达式。

https://github.com/hibernate/hibernate-validator/commit/6ae28a1bc8f7ccb208fa004bdb7c6da569be8a59#diff-3b58c78300599cd6be8467d4722d40a9912c7b256d708b291bce792290395f1b

来源:https://xz.aliyun.com/  感谢【j1ang 

原文始发于微信公众号(衡阳信安):Java安全-深入BeanValidation的RCE漏洞

版权声明:admin 发表于 2023年10月1日 上午12:00。
转载请注明:Java安全-深入BeanValidation的RCE漏洞 | CTF导航

相关文章

暂无评论

您必须登录才能参与评论!
立即登录
暂无评论...