author:r4v3zn@白帽汇安全研究院
前言
3 月 31 日 Nexus Repository Manager 官方发布了 CVE-2020-10199
CVE-2020-10204
的漏洞通告信息,两个漏洞均是由 Github Secutiry Lab 的是 @pwntester 发现的。
Nexus Repository 是一个开源的仓库管理系统,在安装、配置、使用简单的基础上提供了更加丰富的功能。
漏洞概述
CVE-2020-10199
和 CVE-2020-10204
主要是由于可执行恶意 EL表达式
导致的。
CVE-2020-10199
该漏洞的最终触发是通过给 HelperBean
的 message
进行 EL表达式
注入。
影响范围
Nexus Repository Manager 3.x OSS / Pro <= 3.21.1
CVE-2020-10204
漏洞触发的主要原因是在org.sonatype.nexus.security.privilege.PrivilegesExistValidator
或 org.sonatype.nexus.security.role.RolesExistValidator
类中,会对不存在的 privilege
或 role
抛出错误,而在错误信息抛出的时候,会存在一个 EL表达式
的渲染,会提取其中的el表达式并执行,从而造成 EL表达式
注入。
影响范围
Nexus Repository Manager 3.x OSS / Pro <= 3.21.1
调试
下载 Docker 镜像:
docker pull sonatype/nexus3:3.21.1
创建 nexus 数据存储目录:
mkdir /your-dir/nexus-data
运行 Docker 镜像,并且开启调试端口,其中 8081 为 web 访问端口,5050 端口为远程调试端口:
docker run -d --rm -p 8081:8081 -p 5050:5050 --name nexus -v /your-dir/nexus-data:/nexus-data -e INSTALL4J_ADD_VM_PARAMS="-Xms2g -Xmx2g -XX:MaxDirectMemorySize=3g -Djava.util.prefs.userRoot=/nexus-data -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=5050" sonatype/nexus3:3.21.1
下载 Nexus 源码,并且切换至 3.21.0-05
分支:
git clone https://github.com/sonatype/nexus-public.git
git checkout -b release-3.21.0-05 origin/release-3.21.0-05
IDEA 导入项目并且配置远程调试信息:
分析
CVE-2020-10199
该漏洞主要是因为 HelperBean
的 message
并做 EL表达式过滤,导致出现 RCE 漏洞。根据作者作者描述,作者发现如果可控的数据进入到 createViolation
函数将会调用 buildConstraintViolationWithTemplate
执行EL表达式,在 org.sonatype.nexus.repository.rest.api.AbstractGroupRepositoriesApiResource#validateGroupMembers:97
中 createViolation
会调用执行。
private void validateGroupMembers(T request) {
String groupFormat = request.getFormat();
Set<ConstraintViolation<?>> violations = Sets.newHashSet();
Collection<String> memberNames = request.getGroup().getMemberNames();
for (String repositoryName : memberNames) {
Repository repository = repositoryManager.get(repositoryName);
if (nonNull(repository)) {
String memberFormat = repository.getFormat().getValue();
if (!memberFormat.equals(groupFormat)) {
violations.add(constraintViolationFactory.createViolation("memberNames",
"Member repository format does not match group repository format: " + repositoryName));
}
}
else {
violations.add(constraintViolationFactory.createViolation("memberNames",
"Member repository does not exist: " + repositoryName));
}
}
maybePropagate(violations, log);
}
其中 GolangGroupRepositoriesApiResource
继承 AbstractGroupRepositoriesApiResource
,在执行 createRepository
或 updateRepository
会利用到 validateGroupMembers
从而触发漏洞:
其中 @path
为请求路径,通过变量拼接可以得出完整的路径为 beta/repositories/go/group
,也可以通过 Java Enterprise
可以看到请求路径为 beta/repositories/go/group
。
通过查看 swagger 可以看到请求的 base URL 为 /service/rest/
,最后得出请求的完整链接为 /service/rest/beta/repositories/go/group
。
最后发起请求执行命令:
memberNames参数为什么需要把 String 变成 Array
可以看到请求的参数为 GolangGroupRepositoryApiRequest
类,该类中请求的参数 group
类型为 GroupAttributes
。
可以看到在 GroupAttributes
构造方法中需要一个参数名称为 memberNames
属性,该属性的类型为Collection<String>
。
通过查看 Collection
的继承链可以看到在有 List
、Set
、Queue
等继承,也就是说请求的参数为其中继承某一个类型就可以实现。
回到正题,为什么 String
类型的不行,查看 String
类,源代码可以看到 String
并未继承 Collection
,所以无法正常使用。
CVE-2020-10204
CVE-2020-10204 为 CVE-2018-16621
的绕过,官方在修复的漏洞采用的方案是新增 org.sonatype.nexus.common.template.EscapeHelper.stripJavaEl:81
,对用户输入roles参数进行过滤,正则匹配的结果是将‘${’替换为‘{ ’,从而防止EL表达式注入,代码片段如下:
/**
* Strip java el start token from a string
* @since 3.14
*/
public String stripJavaEl(final String value) {
if (value != null) {
return value.replaceAll("\\$+\\{", "{");
}
return null;
}
漏洞触发主要是由于 org.sonatype.nexus.security.privilege.PrivilegesExistValidator
和 org.sonatype.nexus.security.role.RolesExistValidator
类中,会将没有找到的 privilege 或 role 放入错误模板中,而在错误模板在渲染的时候会提取其中的EL表达式
并执行。
最后发起请求执行命令:
EL表达式执行流程
进入 EL表达式
流程之后会通过org.hibernate.validator.internal.engine.constraintvalidation.ConstraintTree#validateConstraints
进行校验表达式,然后通过 addConstraintFailure
进行添加 validationContext
。
跟进 addConstraintFailure
之后可以看到 messageTemplate
为我们提交的 EL表达式
信息,然后通过 this.interpolate
进行执行该表达式。
跟进 this.interpolate
可以看到通过 this.validatorScopedContext.getMessageInterpolator().interpolate(messageTemplate, context);
进行执行,其中 messageTemplate
参数为我们提交的 EL表达式
。
跟进 org.hibernate.validator.messageinterpolation.AbstractMessageInterpolator#interpolate
之后,将提交的 EL表达式
作为参数交由 this.interpolateMessage(message, context, this.defaultLocale);
处理。
继续跟进 org.hibernate.validator.messageinterpolation.AbstractMessageInterpolator#interpolateMessage
,然后通过 this.interpolateex pression
进行针对表达式进行处理,然后将处理结果赋值给 resolvedMessage
,并且作为参数再次处理表达式。
此时执行的代码为 this.interpolateex pression(new TokenIterator(this.getParameterTokens(resolvedMessage, this.tokenizedELMessages, InterpolationTermType.EL)), context, locale);
跟进代码可以看到 isEL=True
,然后通过 this.interpolate
处理。
跟进 org.hibernate.validator.messageinterpolation.ResourceBundleMessageInterpolator#interpolate:83
可以看到已经将我们提交的 EL表达式
实例化为 EL 类型对象。
最后通过 org.hibernate.validator.internal.engine.messageinterpolation.ElTermResolver#interpolate
方法中的 resolvedex pression = (String)valueex pression.getValue(elContext);
进行执行表达式,然后将执行结果转换为 String
类型结果内容进行响应。
以下为整个的调用链条:
修复
升级至最新版本或 Nexus Repository Manager 3.x OSS / Pro > 3.21.1