Apache Kylin CVE-2022-43396&CVE-2022-44621分析
前言:年前看的这个,被CVE-2022-44621狠狠坑了,找了快一周的利用路径最终得出无法利用的结果
CVE-2022-43396
漏洞概述
Apache Kylin™是用于大数据的开源,分布式分析数据仓库;它旨在提供大数据时代的OLAP(在线分析处理)功能。通过在Hadoop和Spark上革新多维多维数据集和预计算技术,无论数据量如何增长,Kylin都能实现接近恒定的查询速度。Kylin将查询延迟从几分钟减少到亚秒级,将在线分析重新带回大数据。
Apache Kylin命令注入漏洞,该漏洞是历史漏洞CVE-2022-24697的黑名单修复绕过,恶意用户可以通过控制conf的kylin.engine.spark-cmd参数来执行命令,漏洞编号为CVE-2022-43396
漏洞版本
该漏洞在以下版本被修复:
Kylin >= 4.0.3
漏洞分析
根据漏洞补丁定位到github中修复处
https://github.com/apache/kylin/pull/2011/commits/418a63c61379d429312972fc94b87994e06b664f
可以看到漏洞修复方式是在NsparkExecutable.java中去掉了sparkSubmitCmd参数从KylinconfigBase.java的getSparksubmitCmd方法中取值的代码,并且在KylinconfigBase.java中去掉了getSparksubmitCmd
从漏洞描述中可知该漏洞是CVE-2022-24697的绕过,而CVE-2022-24697最终触发漏洞是在CliCommandExecutor中的runNativeCommand方法中,回顾apache kylin的所有历史命令执行漏洞会发现,所有漏洞的最终触发点均是在此处。
在CliCommandExecutor的runNativeCommand中打上断点,先随便使用touch /tmp/kylin_rce
执行命令来调试流程
可以看到带入到CliCommandExecutor的execute方法中的cmd参数是在runSparkExecutable类中的runSparkSubmit方法中通过调用同类的generateSparkCmd方法获得的
进入到generateSparkCmd方法调试流程,其中sparkSubmitCmd参数来自于config.getSparkSubmitCmd
config.getSparkSubmitCmd返回值正是编辑配置时传入的kylin.engine.spark-cmd参数的值touch /cmd/kylin_rce
sparkSubmitCmd参数值touch /cmd/kylin_rce
在418行处拼接到aliasedJar参数return,赋值给runSparkSubmit的cmd参数
接着cmd参数带入CliCommandExecutor的excute方法中,excute方法又调用runNativeCommand方法,最终在runNativeCommand方法中带入ProcessBuilder.start中命令执行
以上流程就是kylin.engine.spark-cmd的值被带入cmd参数中并最终在CliCommandExecutor中的runNativeCommand方法中命令执行的过程
然而由于我们使用的docker环境只有apache kylin 4.0.0版本,而该docker环境并未包含对CVE-2022-24697的修复,查看CVE-2022-24697的修复代码
https://github.com/apache/kylin/pull/1811/commits/bd1b6028b8374d38e6968b7ee8b7c16213334bf0#diff-0bbd2aecd8516a4a43475f99cbc29fd17bdd76c9bc01def796cdf5bc745d1ee8R24
可以看到CVE-2022-24697修复的代码中对所有config的key和value均调用了ParameterFilter.checkSparkConf
查看ParameterFilter.checkSparkConf,可以看到其实现就是对config的key和value做了黑名单,不允许出现SPARK_CONF_REGULAR_EXPRESSION中定义的几个特殊字符
那么就意味着我们不能使用来执行命令,回顾前面的调试流程会发现
touch /tmp/kylin_rce`被注入的位置是在&&之后,那么就意味着其实并不需要反引号来执行命令
而直接插入touch /tmp/kylin_rce会报错unrecognized option '--class'
只需在touch /tmp/kylin_rce 后加入--将后续的--class 等参数当作文件名来处理即可成功执行命令且绕过CVE-2022-24697的黑名单
漏洞复现
根据官方文档安装apache kylin 4.0.0 docker环境https://kylin.apache.org/docs/install/kylin_docker.html
docker pull apachekylin/apache-kylin-standalone:4.0.0
docker run -d --name kylin -m 8G -p 7070:7070 -p 8088:8088 -p 50070:50070 -p 8032:8032 -p 8042:8042 -p 5005:5005 -p 2181:2181 apachekylin/apache-kylin-standalone:4.0.0
配置远程调试
编辑/home/admin/apache-kylin-4.0.0-bin-spark2/conf/setenv.sh,增加如下代码
export KYLIN_DEBUG_SETTINGS="-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=5005"
环境运行成功后在访问http://127.0.0.1:7070/kylin/login
使用默认密码ADMIN/KYLIN登录系统
在model处选择一个cube编辑,在Configuration Overwrites处填写Kylin Properties如下
保存配置后选中Actions中的build
查看docker环境可以看到命令已经成功执行
修复方案
Kylin 2.x & Kylin 3.x & 4.x 的用户应升级到4.0.3或应用补丁以修复漏洞,补丁获取链接:
https://github.com/apache/kylin/pull/2011
CVE-2022-44621
分析
根据漏洞补丁定位到github中修复处
https://github.com/apache/kylin/pull/2011/commits/418a63c61379d429312972fc94b87994e06b664f
可以看到针对CVE-2022-44621的修复方式是在DiagnosisController中的dumpJobDiagnosisInfo方法中对jobId参数做了过滤,关于dumpJobDiagnosisInfo方法中的JobId会被带入到在CliCommandExecutor中的runNativeCommand中这一事实其实早在CVE-2020-13925中就提到过
https://www.freebuf.com/vuls/243541.html
然而跟踪流程就会发现,jobId虽然看似是从http请求中传入,但其实根本无法控制
查看dumpJobDiagnosisInfo的代码,调用this.dgService.dumpJobDiagnosisInfo
跟到dumpJobDiagnosisInfo可以看到,把传入的jobId带入到Jobservice的getJobInstance方法,如果返回的jobInstance为null则抛出异常,而这一步如果传入自定义的JobId就会因为找不到JobInstance抛出异常
那么再来看看生成JobId的部分是否可以被控制,经过动态调试和代码分析发现,构建Job的地方有两个
第一个是在NsparkCubingJob的create方法,对应的是前端对cube的build操作
在构建时首先调用第一个create方法,然后在此处可以看到调用UUID.randomUUID().toString()生成一串随机字符串带入到第二个create方法中并作为JobId参数,也就是说JobId为随机生成的字符串
第二个是NSparkMergingJob的merge方法,对应的是前端对cube的merge操作,同样可以看到JobId为随机生成的字符串
只有传入数据库中已有的随机字符串JobId才能使Jobservice的getJobInstance不返回null从而流程继续直到拼接到CliCommandExecutor中的runNativeCommand的cmd中,而传入随机字符串JobId当然无法命令注入,所以该漏洞并不能被利用