log4j2漏洞分析与思考
本文首发于seebug:https://paper.seebug.org/1786/
近期最火的漏洞莫过于log4j2 RCE漏洞,都说是史诗级的漏洞,那必然要分析下。
0x01 定位漏洞
既然是利用了jndi,那么断点就打在javax.naming.InitialContext
构造方法肯定是没错的。
通过回溯,最初调用lookup相关的地方在org.apache.logging.log4j.core.lookup.StrSubstitutor.replace()
进一步回溯分析调用lookup的原因,在MessagePatternConverter.format()
中,遍历每个字符,当匹配到${
就调用StrSubstitutor.replace()
处理jndi相关信息。
0x02 log4j日志记录及漏洞分析
在详细分析前先简单了解下log4j三大组件:
- Logger:日志记录器,负责收集处理日志记录
- Appender:日志存放的地方,负责日志的输出
- Layout:日志格式化,负责日志输出的形式
1、在log4j2中通过LoggerConfig.processLogEvent()
处理日志事件,主要部分在调用callAppenders()
即调用Appender:
2、Appender功能主要是负责将日志事件传递到其目标,常用的Appender有ConsoleAppender(输出到控制台)、FileAppender(输出到本地文件)等,通过AppenderControl获取具体的Appender,本次调试的是ConsoleAppender。
调用ConsoleAppender.tryAppend()
尝试输出日志
3、首先获取Layout日志格式,通过Layout.encode()
进行日志的格式化
Layout会获取formatters来完成具体的格式化的事情
4、处理传入的message通过MessagePatternConverter.format()
,也是本次漏洞的关键之处,我们具体来看下。首先创建一个workingBuilder,当config存在并且noLookups为false,匹配到${'
则调用workingBuilder.append()
获取StrSubstitutor内容来替换原来的信息(这里说明下,jndi执行命令后如果返回了结果就会将其append输出)
noLookups来自设置来自配置文件,默认值为false
,相当于默认支持jndi。
5、StrSubstitutor.resolveVariable()
解析变量,调用Interpolator.lookup()
,Interpolator有date、 java、marker、ctx、jndi,、main、jvmrunargs、 sys、 env、 log4j共10种
根据前缀获取lookup为JndiLookup,后续就是jndi处理,这里不再继续。
0x03 漏洞思考
那么究竟为什么要给log4j2开发jndi功能,特地去官网看了下,开发者最初考虑到使用log4j通过本地文件加载属性可能不够用,某些场景需要从更多的地方加载属性,因此设计了Property Substitution
.
jndi是是一组Java命名和目录接口,主要用于将JNDI API映射为特定的命名服务和目录系统。添加jndi服务主要是为了日志功能更加丰富,但是开发者没有考虑到jndi可能带来的危害。我想如果开发者在设计之初就考虑到了潜在的危害,考虑用白名单校验jndi的地址,便能够有效避免今天的局面。
https://logging.apache.org/log4j/log4j-2.3/manual/lookups.html#ContextMapLookup
0x04 参考链接
1 | https://logging.apache.org/log4j/log4j-2.3/manual/lookups.html#ContextMapLookup |