从SSRF 到 RCE —— 对 Spring Cloud Gateway RCE漏洞的分析
0x01 写在前面
对 log4j2 漏洞的后续研究中,发现一些有趣的东西,记录分享一下
0x02 log4j 真的在任何情况不存在 JNDI注入吗?
首先提出一个问题, log4j 真的在任何情况不存在 JNDI注入吗?
答案是否定的。
翻阅 Log4j2 的 pull request 发现一个有意思的对话:
有人提出实际上 log4j 和 log4j2 一样易受攻击的,只不过与 log4j2 相比,Log4j 的攻击向量“更安全”
因为 Log4j 的攻击入口点是其配置文件,而 log4j2 的攻击入口点是用户的输入
那么实际上如何呢?经过我简单测试,发现修改 log4j 的配置文件确实会导致漏洞的产生,但要求要比pull reques中所说的更苛刻。
案例1 - log4j 配置文件中 JMSAppender 的 RCE
首先在 maven 中添加以下依赖:
然后在resource 目录下新建 log4j.properties 文件,内容如下:
最后新建 Log4jJMSAppenderTest.java 文件,内容如下:
可以看到,项目的所用到的主要依赖是 log4j 1.2.17 版本,然后为了满足条件要求(后文会说具体什么条件),又引入了最新版的 activemq 依赖。
然后如果直接运行 main 函数,可以直接触发 RCE:
原理很简单,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 方法:
然后在
ctx.lookup(name)
中传入我们指定的恶意 LDAP 服务地址,从而触发 RCE
这里虽然可以实现了 RCE,但实际上你可以发现,必须要有一个支持 jms 代理的类(
org.apache.activemq.jndi.ActiveMQInitialContextFactory
)才可以,否则是会报错的,如果实际业务代码或引用的包中没有 jms 代理类,就显得就十分鸡肋+苛刻了
那么可利用的仅仅是 JMSAppender 吗?
案例 2 - log4j 配置文件中 JDBC 的 RCE
在 log4j 中,除了 JMSAppender 配置项外,还有很多 Appender,JDBCAppender就是其一。
同样的,在 resources 目录下创建
log4j.properties
文件,内容如下:
为了方便测试 JDBC反序列化漏洞,所以maven 中我们新增了其他依赖,具体如下:
最后再新建 test.java 文件,内容如下:
运行main函数,直接触发 RCE:
原理和JMSAppender比较类似,同样是那么会进入
JDBCAppender.java
中,只不过触发的方法是
getConnection()
,后续就是我们比较熟知的 JDBC 反序列漏洞流程了
这里提到的仅仅是 log4j 的1.x 版本,实际上 log4j 2.15.0 同样可以实现上述操作
在能够控制配置文件的情况下,可以不用再花心思去绕过 lookup 的白名单和各种限制,直接采用类似于上面的方式实现 RCE,比如三梦师傅之前提到的:
当然,总体来看,这种修改配置文件的方式还是很鸡肋的,实际利用有限,只是适用于特殊场景,此处仅作技术性探讨
0x03 logback 的鸡肋 RCE
提到 log 日志记录,除了 log4j 外,还有就是
logback
,
logbakc
和
log4j
是 同一个人写的,因此实际上我想看看 logback 中是否存在类似问题
并且由于 logback 是 springboot 的默认组件,如果同样存在类似问题,那么可能遇到这种场景的机会会加大
首先 看的是 JMSAppender,遗憾的是,在 logback 的 1.2.2版本后,就移除了 JMSTopicAppender
但幸运的是 ,在 logback 中同样存在类似于 JDBCAppender 的 Appender —— DBAppender
DBAppender 中有一个名为
ConnectionSource
的接口,该接口提供了一种可插拔式的方式为需要使用
java.sql.Connection
的 logback 类获取 JDBC 连接,目前有三种实现,分别为:
DriverManagerConnectionSource
、
DataSourceConnectionSource
与
JNDIConnectionSource
。这三种实现每一种都可以用来实现 RCE。
DriverManagerConnectionSource 和 DataSourceConnectionSource 比较类似,都可以通过控制 JDBC 的 URL 去实现 JDBC 反序列化攻击的目的。
首先在 resource 目录下新建 logback-spring.xml ,内容如下
然后在新建的 SpringBoot 项目的 pom.xml中新加两个依赖,如下:
然后直接运行
SpringApplication.run()
所在方法,即可触发漏洞:
除上述两种,还有 JNDIConnectionSource 方法,JNDIConnectionSource 是 logback 自带的方法,从名字就可以看出来,它通过 JNDI 获取 javax.sql.DataSource,然后再获取 java.sql.Connection 实例
同样的,对于我们来说,这种方式实现 RCE 更方便,完全不需要其他的依赖,测试如下:
在 resource 目录下新建 logback-spring.xml ,内容如下
同样的,直接运行
SpringApplication.run()
所在方法,即可触发漏洞:
实际上跟踪一下可以发现,最终会进入到
JNDIConnectionSource.java
的
getConnection
方法,如果dataSource 为空,那么就令
dataSource = lookupDataSource();
然后在
lookupDataSource()
中触发
lookup
:
不过这里需要注意的是,JNDIConnectionSource类是通过无参构造函数获取
javax.naming.InitialContext
,这种方式在 J2EE 环境通常可以行得通,但是在 J2EE 环境之外,需要额外提供一个
jndi.properties
的配置文件才可以。
实际上除了上述方式,还有一种配置不借助 DBAppender 也可以直接实现 RCE,配置如下: