[置顶]Nexus Repository Manager历史表达式注入漏洞分析
CVE-2018-16621
影响版本:
Nexus Repository Manager 3.x OSS / Pro <= 3.14.0
环境搭建:
下载NXRM OSS
https://help.sonatype.com/repomanager3/download/download-archives---repository-manager-3
本文分析调试的版本是3.13.0-01,下载对应的版本,解压,之后./nexus start运行,默认账号密码:admin/admin123;
为了能够让IDE附加调试,在启动脚本nexus-3.13.0-01-mac/nexus-3.13.0-01/bin/nexus里添加参数:
1INSTALL4J_ADD_VM_PARAMS="-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=5050"
下载 NXRM源码,并切换至 3.13.0-01
分支:
git clone https://github.com/sonatype/nexus-public.git
git checkout -b release-3.13.0-01 origin/release-3.13.0-01
IDEA导入项目并在Run/Debug Configuration里面配置调试信息:
调试分析:
文章https://securitylab.github.com/research/bean-validation-RCE/指出:
It was soon clear that the root cause of the issue was that one of the properties of a user-controller Java Bean (coming from an HTTP request) was concatenated into a Bean Validation error message, and that this message was later processed and any EL expressions were evaluated and interpolated in the final violation message.
更具体的,在创建用户或角色进行校验时,对于不存在的privilege
或role
参数会抛出错误,且将privilege
或role
插入到了报错信息模版中,模版在渲染时会取出其中的表达式并解释执行;对privilege
和role
的校验分别对应在org.sonatype.nexus.security.privilege.PrivilegesExistValidator
和org.sonatype.nexus.security.role.RolesExistValidator
中。
漏洞poc如下:
在RolesExistValidator#isValid()
中调用context#buildConstraintViolationWithTemplate()
处下断点,可看到roles参数被放入到报错信息模版中;
回到上层ConstraintTree#validateSingleConstraint()
,在调用完RolesExistValidator#isValid()
后,接着调用ValidationContext#createConstraintViolations()
,参数constraintValidatorContext
中包含roles
参数;
ValidationContext#createConstraintViolations()
继续调用ValidationContext#createConstraintViolation()
跟进ValidationContext#createConstraintViolation()
,其调用了ValidationContext#interpolate()
,messageTemplate
包含roles
参数;
ValidationContext#interpolate()
继续调用ResourceBundleMessageInterpolator#interpolate()
在ResourceBundleMessageInterpolator#interpolate()
中,其调用ResourceBundleMessageInterpolator#interpolateMessage()
,message
包含roles
参数;
ResourceBundleMessageInterpolator#interpolateMessage()
,调用ResourceBundleMessageInterpolator#interpolateExpression()
来处理tokens
调用InterpolationTerm#interpolate()
,roles
参数在expression
跟进InterpolationTerm#interpolate()
,继续调用InterpolationTerm#interpolateExpressionLanguageTerm()
最后在InterpolationTerm#interpolateExpressionLanguageTerm()
中通过valueExpression.getValue()
执行表达式;
修复:
对比patch,新增了EscapeHelper#stripJavaEl()
对用户输入正则匹配进行清理,将‘${’
替换为‘{ ’
,不过该修复方案之后被绕过,详见后文;
CVE-2020-10204
影响版本:
Nexus Repository Manager 3.x OSS / Pro <= 3.21.1
环境搭建:
参照CVE-2018-16621,本文分析调试的版本是3.21.1-01
调试分析:
CVE-2020-10204即是对CVE-2018-16621 patch的绕过;绕过的根本原因是stripJavaEl()
中的正则不严谨,未考虑到"$"和"{"之间的字符,pwntester@GHSL发现Hibernate message interpolation parser ()存在bug,使得形如FOO$\A{payload}
这样的payload可被正常解析执行,但却绕过了该正则。
虽然版本3.21.1-01和3.13.0-01代码有所不同,但NXRM对参数的校验过程总体一致,故这里不再列出具体的执行步骤,可参考CVE-2018-16621调试分析;我们跟踪一下TokenCollector.parse()
解析FOO$\A{payload}
的过程;
当parser解析到$
时,currentParserState
类型为MessageState
,调用MessageState#handleELDesignator()
;
由于interpolationTermType==EL
,跳过$
,currentParserState
转变为ELState
;
\\
时,currentParserState
类型为ELState
,调用ELState#handleEscapeCharacter()
\\
,currentParserState
转变为EscapedState
当parser解析到A
时,currentParserState
类型为EscapedState
,调用EscapedState#handleNonMetaCharacter()
;
将A
追加到currentToken
,currentParserState
转变为前一State,即ELState
;
当parser解析到{
时,currentParserState
类型为ELState
,调用ELState#handleBeginTerm()
;
会将currentToken
(为Missing roles: [nx-adminA
)加入到tokenList
中,并创建空的currentToken
,且追加${
,currentParserState
转变为InterpolationTermState
修复:
增强了stripJavaEl()的正则匹配,但显然仍存在问题;
调试3.21.2发现,其使用org.hibernate.validator.messageinterpolation.ParameterMessageInterpolator
来解析处理message,而在3.21.1中用到了org.hibernate.validator.messageinterpolation.ResourceBundleMessageInterpolator
;我们尝试找源码中对应的改动,但未找到。
CVE-2020-10199
影响版本:
Nexus Repository Manager 3.x OSS / Pro <= 3.21.1
环境搭建:
参照CVE-2018-16621,本文分析调试的版本是3.21.1-01
调试分析:
根据对CVE-2018-16621的分析可知,如果不可信数据被插入到报错信息模版,被当作参数传给了ConstraintValidatorContext.buildConstraintViolationWithTemplate()
,那么极可能会被当作Java表达式执行;pwntester据此特征利用CodeQL对NXRM进行数据流分析,发现stripJavaEl()
未被应用到类org.sonatype.nexus.validation.ConstraintViolationFactory
。所以当用户可控数据流入createViolation(final String path, final String message)
,将会被当作Java EL执行。
跟踪源码发现 org.sonatype.nexus.repository.rest.api.AbstractGroupRepositoriesApiResource#validateGroupMembers()
在校验失败时调用 createViolation()
,并传入参数repositoryName。
AbstractGroupRepositoriesApiResource#validateGroupMembers()
则被AbstractGroupRepositoriesApiResource#createRepository()
和AbstractGroupRepositoriesApiResource#updateRepository()
调用;
AbstractGroupRepositoriesApiResource
有子类GolangGroupRepositoriesApiResource
,其在创建仓库,更新仓库时分别调用了父类的createRepository()
和updateRepository()
;
由文章https://www.cnblogs.com/magic-zero/p/12641068.html可知,根据接口文档,可构造出完整URL和数据包如下:
AbstractGroupRepositoriesApiResource#validateGroupMembers()
对buildConstraintViolationWithTemplate()
下断点,发现payload被插入到了错误信息模版;
修复:
同CVE-2020-10204一样,在3.21.2中修复;
参考
https://www.cnblogs.com/magic-zero/p/12641068.html
https://paper.seebug.org/1166/
https://securitylab.github.com/research/bean-validation-RCE/
https://securitylab.github.com/advisories/GHSL-2020-020-hibernate-validator/
https://securitylab.github.com/advisories/GHSL-2020-011-nxrm-sonatype/