log4j2漏洞复现及漏洞分析
一、漏洞复现
Apache Log4j2是一个基于Java的日志记录工具。该工具重写了Log4j框架,并且引入了大量丰富的特性。该日志框架被大量用于业务系统开发,用来记录日志信息。大多数情况下,开发者可能会将用户输入导致的错误信息写入日志中。
由于Apache Log4j2某些功能(lookup)存在递归解析功能,未经身份验证的攻击者通过发送特别构造的数据请求包,可在目标服务器上执行任意代码。
实际受影响范围如下:
Apache Log4j 2.x < 2.15.0-rc2
JDK在11.0.1、8u191、7u201、6u211及以上的高版本,将一定程度上导致利用失败。
复现环境:
IDEA 2018 (用最新版的2022编译时弹不了,应该加了安全限制)
JDK 1.8.0_144
Log4j版本:2.13.3
因为属于maven项目,直接通过pom.xml下载依赖组件即可:
然后直接运行即可:
选择的jdk版本为1.8
然后先运行JNDIExploit,java -jar JNDIExploit-1.2-SNAPSHOT.jar -i IP地址
最后点击右上角的run就行,最后运行结果如下图所示:
二、漏洞分析
一般log4j2项目中会引用到两个组件包,log4j-api和log4j-core,log4j-api是日志接口,log4j-core是日志标准库。
Log4j2的Lookup主要功能是通过引用一些变量,往日志中添加动态的值。这些变量可以是外部环境变量,也可以是MDC中的变量,还可以是日志上下文数据等。
env:允许系统在全局文件(如 /etc/profile)或应用程序的启动脚本中配置环境变量,然后在日志输出过程中,查找这些变量。例如:${env:USER}。
java:允许查找Java环境配置信息。例如:${java:version}。
jndi:允许通过 JNDI 检索变量。
log4j2中包含两个关键组件LogManager和LoggerContext。LogManager是Log4J2启动的入口,可以初始化对应的LoggerContext。LoggerContext会对配置文件进行解析等其它操作。
常见的Log4J用法是从LogManager中获取Logger接口的一个实例,并调用该接口上的方法。运行下列代码查看打印结果。
log4j2支持多种日志级别,通过日志级别我们可以将日志信息进行分类,在合适的地方输出对应的日志。哪些信息需要输出,哪些信息不需要输出,只需在一个日志输出控制文件中稍加修改即可。级别由高到低共分为6个:fatal, error, warn, info, debug, trace(堆栈)。
当日志级别(调用)大于等于系统设置的intLevel的时候,log4j2才会启用日志打印。在存在配置文件的时候,会读取配置文件中<root level="error">值设置intLevel。当然我们也可以通过Configurator.setLevel("当前类名", Level.INFO);来手动设置。如果没有配置文件也没有指定则会默认使用Error级别,也就是200。
在本次漏洞分析过程中日志等级为Level.LEVEL,它的intLevel()为200,而本环境中默认的日志级别为ERROR(200),如下图所示:
转换器名称msg对应的插件实例MessagePatternConverter对于日志中的消息内容处理存在问题,在大多数场景下这部分是攻击者可控的。MessagePatternConverter会将日志中的消息内容为${prefix:key}格式的字符串进行解析转换,用来读取环境变量等
传入的message会通过MessagePatternConverter.format(),判断如果config存在并且noLookups为false(默认为false),然后匹配到${则通过getStrSubstitutor()替换原有的字符串,比如这里的${java:os},如下图所示:
属性占位符之Interpolator(插值器)
log4j2中环境变量键值对被封装为了StrLookup对象。这些变量的值可以通过属性占位符来引用,格式为:${prefix:key}。在Interpolator(插值器)内部以Map<String,StrLookup>的方式则封装了多个StrLookup对象。
这些实现类存在于org.apache.logging.log4j.core.lookup包下,可以看到处理event的时候根据前缀选择对应的StrLookup进行处理,目前支持date,jndi,java,main等多种类型,如果构造的event是jndi,则通过JndiLoopup进行处理,从而构造漏洞。如下图所示:
使用${jndi:key}时,将会调用JndiLookup的lookup方法。
因为JNDI支持从指定的远程服务器上下载class文件,加载到本地JVM中,并通过适当的方式创建对象,从而可以利用JNDI注入来执行命令。
三、利用条件
在版本小于Apache Log4j 2.x < 2.15.0-rc2及JDK满足的情况下,还需以下两点:
1、代码中日志等级的优先级高于或等于默认级别(ERROR(200),数值越低优先级越高)才可以成功。
2、必须能够控制log方法中的参数内容。
四、修复方法
在log4j2.component.properties配置文件,增加如下内容为:
log4j2.formatMsgNoLookups=true
或者删除jndilooup类文件