最近看CVE发现多个因为引入hibernate.validtor组件且在抛错时带入外部可控参数执行SpEL表达式导致RCE漏洞。hibernate.validator实现Jakarta表达式语言用户动态执行、表示违反约定的信息,所以外部可控参数带入表达式执行存在RCE漏洞。

hibernate.validtor

1
2
String message = String.format("Invalid params: %s", s);
constraintValidatorContext.buildConstraintViolationWithTemplate(message).addConstraintViolation();

包含外部可控参数的message会在org/hibernate/validator/messageinterpolation/AbstractMessageInterpolator.java判断是否要解析为EL

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
if ( resolvedMessage.indexOf( '{' ) > -1 ) {
// resolve parameter expressions (step 2)
resolvedMessage = interpolateExpression(
new TokenIterator( getParameterTokens( resolvedMessage, tokenizedParameterMessages, InterpolationTermType.PARAMETER ) ),
context,
locale
);

// resolve EL expressions (step 3)
resolvedMessage = interpolateExpression(
new TokenIterator( getParameterTokens( resolvedMessage, tokenizedELMessages, InterpolationTermType.EL ) ),
context,
locale
);
}

并带入org/hibernate/validator/internal/engine/messageinterpolation/ElTermResolver.java执行EL

1
2
3
4
5
6
7
      try {
ValueExpression valueExpression = bindContextValues( expression, context, elContext );
resolvedExpression = (String) valueExpression.getValue( elContext );
}
catch (PropertyNotFoundException pnfe) {
LOG.unknownPropertyInExpressionLanguage( expression, pnfe );
}

Netflix-conductor

CVE-2020-9296
TaskTimeoutConstraint中将外部可控参数带入buildConstraintViolationWithTemplate解析EL表达式导致RCE

1
2
3
String message = String.format("TaskDef: %s responseTimeoutSeconds: %d must be less than timeoutSeconds: %d",
taskDef.getName(), taskDef.getResponseTimeoutSeconds(), taskDef.getTimeoutSeconds());
context.buildConstraintViolationWithTemplate(message).addConstraintViolation();

修复commit
将hibernate validtor替换为org.apache.bval:bval-jsr,org.apache.bval:bval-jsr在该版本下不会解析EL表达式。

sonatype-nexus-public

官方通告中可以看到nexus存在多个因为hibernate validtor修复不完善bypass导致的CVE
release-3.18.1-01 components/nexus-security/src/main/java/org/sonatype/nexus/security/role/RolesExistValidator.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
for (Object item : value) {
try {
authorizationManager.getRole(String.valueOf(item));
}
catch (NoSuchRoleException e) {
missing.add(getEscapeHelper().stripJavaEl(item.toString()));
}
}
if (missing.isEmpty()) {
return true;
}

context.disableDefaultConstraintViolation();
context.buildConstraintViolationWithTemplate("Missing roles: " + missing)
.addConstraintViolation();
return false;

直接带入导致RCE

release-3.19.0-01 增加对EL的过滤函数 components/nexus-common/src/main/java/org/sonatype/nexus/common/template/EscapeHelper.java

1
2
3
4
5
6
public String stripJavaEl(final String value) {
if (value != null) {
return value.replaceAll("\\$+\\{", "{");
}
return null;
}

该正则过滤存在bypass 在45ce815b3244e6f91a1929204d1cb6d058e45781 commit中修复正则表达式过滤

1
2
3
4
5
6
public String stripJavaEl(final String value) {
if (value != null) {
return value.replaceAll("\\$+\\{", "{").replaceAll("\\$+\\\\A\\{", "{");
}
return null;
}

在测试文件中可以bypass poc

1
2
3
4
5
6
@Test
public void testStripJavaEl_bugged_interpolator() {
String test = "$\\A{badstuffinhere}";
String result = underTest.stripJavaEl(test);
assertThat(result, is("{badstuffinhere}"));
}

在表达式前增加\A同样可以解析是因为在hibernate.validtor处理时对el做分词

1
2
3
4
5
6
7
8
9
10
11
12
org/hibernate/validator/messageinterpolation/AbstractMessageInterpolator.java
private List<Token> getParameterTokens(String resolvedMessage, ConcurrentReferenceHashMap<String, List<Token>> cache, InterpolationTermType termType) {
if ( cachingEnabled ) {
return cache.computeIfAbsent(
resolvedMessage,
rm -> new TokenCollector( resolvedMessage, termType ).getTokenList()
);
}
else {
return new TokenCollector( resolvedMessage, termType ).getTokenList();
}
}

$\A{"".getClass().forName("javax.script.ScriptEngineManager").newInstance().getEngineByName("JavaScript").eval("java.lang.Runtime.getRuntime().exec(‘open /System/Applications/Calculator.app’)")}
=>
debug
同时在validate conf中新增commit 4f8ffbe13fe96f009dbecb91cee1f3995003592a增加消息factory

1
2
3
4
5
6
ValidatorFactory factory = Validation.byDefaultProvider().configure()
.constraintValidatorFactory(constraintValidatorFactory)
.parameterNameProvider(new AopAwareParanamerParameterNameProvider())
.traversableResolver(new AlwaysTraversableResolver())
+ .messageInterpolator(new ParameterMessageInterpolator())
.buildValidatorFactory();

ParameterMessageInterpolator() 从官方文档中可以看到是不支持EL表达式插值,所以不会存在因为EL表达式带入恶意数据执行导致的RCE漏洞。