在官方漏洞报告中,说CVE-2022-43396是CVE-2022-24697的绕过。实际情况却是:两个漏洞均是发送在 Kylin 的 cube build 功能中,但CVE-2022-24697是参数可控;CVE-2022-43396是命令可控。
漏洞报告:
配置重写时,为正确处理引号,导致逃逸,造成命令执行
影响版本:
IP:
本机指Docker运行的环境实例;宿主机指运行Docker的主机。
版本:
直接采用官方 docker 镜像搭建环境,进行远程调试。
调试:
执行如下代码,修改 kylin.sh 的内容
sed -i 's/\${KYLIN_TOMCAT_OPTS} -classpath/\${KYLIN_TOMCAT_OPTS} -Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=5005 -classpath/g' /home/admin/apache-kylin-4.0.0-bin-spark2/bin/kylin.sh
sh /home/admin/apache-kylin-4.0.0-bin-spark2/bin/kylin.sh restart
启动:
执行如下命令即可启动 Kylin :
docker pull apachekylin/apache-kylin-standalone:4.0.0
docker run -d -m 8G -p 7070:7070 -p 8088:8088 -p 50070:50070 -p 8032:8032 -p 8042:8042 -p 2181:2181 -p 5005:5005 apachekylin/apache-kylin-standalone:4.0.0
具体看官方的docker安装文档 https://kylin.apache.org/cn/docs/install/kylin_docker.html
其中各端口功能如下:
登录 Kylin(账号:admin,密码:KYLIN)。选择自带的项目 learn_kylin
以此按箭头选择 ,查看 cube 列表
点击 Action -> Edit,编辑 cube
点击 Configuration Overwrites 中,新建一行的数据
kylin.engine.spark-conf.spark.driver.memory
512M' `touch /tmp/hacked` '
点击 Next 并 Save。
点击 Build
随便选个日期后点击 Submit
登录进 docker 容器查看,成功创建文件 /tmp/hacked
注意:
多次尝试反弹 shell,但是均失败了,不知道具体原因;可以考虑写 jsp 马。
源码:
从官方库下载 Kylin4.0.0 版本
断点:
Idea 打开后,在org.apache.kylin.engine.spark.job.NSparkExecutable#doWork
下断点:
调用栈:
execute:90, CliCommandExecutor (org.apache.kylin.common.util)
runSparkSubmit:282, NSparkExecutable (org.apache.kylin.engine.spark.job)
doWork:168, NSparkExecutable (org.apache.kylin.engine.spark.job)
execute:206, AbstractExecutable (org.apache.kylin.job.execution)
doWork:94, DefaultChainedExecutable (org.apache.kylin.job.execution)
execute:206, AbstractExecutable (org.apache.kylin.job.execution)
run:113, DefaultScheduler$JobRunner (org.apache.kylin.job.impl.threadpool)
runWorker:1149, ThreadPoolExecutor (java.util.concurrent)
run:624, ThreadPoolExecutor$Worker (java.util.concurrent)
run:748, Thread (java.lang)
调试:
每次点击 build 后,稍做等待即可捕获到断点。
org.apache.kylin.engine.spark.job.NSparkExecutable#doWork
方法在调用 runSparkSubmit 方法这一步之前,都是获取各种 config 的各种配置属性(cube配置重写的属性、系统的配置属性等),并传递下去。
在 runSparkSubmit 方法内定位到关键方法 getSparkConfigOverride(Override意为重写,与开头的漏洞描述有关联)
getSparkConfigOverride 调用父类的同名方法
略读 super.getSparkConfigOverride
方法,发现若不存在键值为spark.driver.memory
的元素,则为其指定值并加入 Map<String, String> sparkConfigOverride
。
而Map<String, String> sparkConfigOverride
是通过 config.getSparkConfigOverride()
方法获取的,其代码如下:
public Map<String, String> getSparkConfigOverride() {
return getPropertiesByPrefix("kylin.engine.spark-conf.");
}
getSparkConfigOverride()
从全局配置属性中(包括cube配置重写的属性)返回键值开头为kylin.engine.spark-conf.
的所有属性。
所以我们在攻击过程中设置的键值刚好满足上述条件
在super.getSparkConfigOverride
return时,我们可以看到在键值spark.driver.memory
的值中成功植入了恶意命令
此后,一路 return,一直到执行命令处exec.execute(cmd, patternedLogger, jobId);
键值对kylin.engine.spark-conf.spark.driver.memory
均未变化,我们可以看到此时变量 cmd 的值,已经被植入了恶意命令。
随后就是执行 Kylin 执行命令的一套方法,在 /tmp 目录下生成 hacked 文件
补丁:
调用 ParameterFilter.checkSparkConf
方法检查所有配置属性键值对
漏洞报告:
CVE-2022-24697 的绕过,用户可直接控制 kylin.engine.spark-cmd
执行命令
影响版本:
不再重复,和CVE-2022-24697一致
开始的步骤与 CVE-2022-24697一致,登录并编辑cube,但是新建的键值对不同;
点击 Configuration Overwrites 中,新建一行的数据
kylin.engine.spark-cmd
touch /tmp/hacked123; echo
点击 Next 并 Save。
点击 Build
随便选个日期后点击 Submit
登录进 docker 容器查看,成功创建文件 /tmp/hacked123
源码:
从官方库下载 Kylin4.0.0 版本
断点:
Idea 打开后,在org.apache.kylin.engine.spark.job.NSparkExecutable#doWork
下断点:
调用栈:
execute:90, CliCommandExecutor (org.apache.kylin.common.util)
runSparkSubmit:282, NSparkExecutable (org.apache.kylin.engine.spark.job)
doWork:168, NSparkExecutable (org.apache.kylin.engine.spark.job)
execute:206, AbstractExecutable (org.apache.kylin.job.execution)
doWork:94, DefaultChainedExecutable (org.apache.kylin.job.execution)
execute:206, AbstractExecutable (org.apache.kylin.job.execution)
run:113, DefaultScheduler$JobRunner (org.apache.kylin.job.impl.threadpool)
runWorker:1149, ThreadPoolExecutor (java.util.concurrent)
run:624, ThreadPoolExecutor$Worker (java.util.concurrent)
run:748, Thread (java.lang)
调试:
点击 build,等待后即可捕获断点
CVE-2022-43396是CVE-2022-24697的绕过。从漏洞报告中我们知道漏洞成因是kylin.engine.spark-cmd
参数可控。调试过CVE-2022-24697,我们知道该参数的使用出现在NSparkExecutable#generateSparkCmd
方法中。
判断 getSparkSubmitCmd 方法获取kylin.engine.spark-cmd
参数是否成功,成功则返回,不成功则指定默认值。
public String getSparkSubmitCmd() { return getOptional("kylin.engine.spark-cmd", null); }
该参数可由 cube: configuration Overrides进行设置。
getSparkSubmitCmd 获取到我们设置的恶意字符串后,被拼接如下
随意传入被执行,造成任意命令执行。
补丁:
直接指定 sparkSubmitCmd 参数为默认值。