在官方漏洞报告中,说CVE-2022-43396是CVE-2022-24697的绕过。实际情况却是:两个漏洞均是发送在 Kylin 的 cube build 功能中,但CVE-2022-24697是参数可控;CVE-2022-43396是命令可控。
下文以CVE-2022-24697 为例,主要目的是跟踪下数据流,看看能否用CodeQL 复现。
1. 漏洞概况
影响版本:
Kylin 2.x & Kylin 3.x & 4.x should upgrade to 4.0.2
2. 漏洞分析
2.1 漏洞环境搭建
同前文,不赘述
参考[1] 中的步骤,在Cube Designer Configuration Overwrites 中配置如下:
1 | kylin.engine.spark-conf.spark.driver.memory |
2.2 漏洞原理分析
整个逻辑涉及到几个线程
- web api CubeController#updateCubeDesc 更新CubeDesc 配置信息,在其config.overrides,config是个KylinConfigExt 类型;随后异步更新CubeInstance
- web api CubeController#rebuild 生成一条rebuild Job,写入ExecutableManager 的队列中
- DefaultScheduler 循环线程,始终从ExecutableManager 中获取job 信息,根据job 中的cubename从CubeManager 获取Cube 信息,获取步骤1 中的config.overrides 进行命令拼接,导致命令执行
2.2.1 CubeController#updateCubeDesc
CubeController#updateCubeDesc
1 |
|
CubeService.java
1 | public class CubeService extends BasicService implements InitializingBean { |
CubeDescManager.java
1 | public class CubeDescManager { |
- 更新CubeDesc
CubeDesc to CubeInstance
但是从下文中rebuild 中得知,是直接获取的cubeInstance,那么是CubeDesc 是如何修改CubeInstance 的呢?
- 更新完cubeDesc后,会触发EntityChange 事件
- 最终调用cubeManager.reloadCubeQuietly 重新加载cube
这中间涉及到异步,CodeQL 基本无望了
2.2.2 CubeController#rebuild
CubeController#rebuild
1 |
|
- 通过jobService.getCubeManager().getCube(cubeName) 获取cube
JobService#submitJob
1 | public JobInstance submitJob(CubeInstance cube, TSRange tsRange, SegmentRange segRange, // |
ExecutableManager.java
1 | public class ExecutableManager { |
ExecutableDao.java
1 | public class ExecutableDao { |
- ExecutableDao 存在个map,保存所有的Executeable 任务
- addJob 新增job任务接口
- getJobIdsInCache 获取所有任务接口
2.2.2 DefaultScheduler 定时任务线程
1 | execute:90, CliCommandExecutor (org.apache.kylin.common.util) |
中间变量为 CubeInstance.config.overrides 内
NSparkExecutable.java
1 | protected ExecuteResult doWork(ExecutableContext context) throws ExecuteException { |
ThreadPool 任务从何而来
DefaultScheduler.java
1 | public class DefaultScheduler implements Scheduler<AbstractExecutable> { |
- jobPool.execute 是从pool 里面添加任务的接口
- 得看后续jobExecutor 何处调用execute
FetcherRunner.java
1 | public abstract class FetcherRunner implements Runnable { |
1 | public class DefaultFetcherRunner extends FetcherRunner { |
- DefaultFetcherRunner 是个Runnable,其run 逻辑为循环从getExecutableManager() 获取任务,并且添加到jobExecutor 中去执行
1 | private class JobRunner implements Runnable { |
- 所以最终调用的是executable#execute 接口
1 |
|
3. 补丁分析
CVE-2022-24697
https://github.com/apache/kylin/pull/1811/files
新增了ParameterFilter
CVE-2022-43396
https://github.com/apache/kylin/pull/2011/files
禁用了kylin.engine.spark-cmd 属性
4. CodeQL 复现
4.1 CodeQL 复现尝试
- 从api 到KylinConfig.override
没问题
1 | /** |
- 从KylinConfig 到Sink
1 |
5. 思考
这个漏洞流程很复杂,涉及到3个线程,2个http 请求,想靠CodeQL 直接实现难度很大。提两个脑洞思路
- 寻找全局静态类实例,比如这个case 中的CubeManager
- 从http source 能够传播到CubeManager 中哪些field
- 从这些field 再作为source 点,能否传播至sink
上main中的全局类实例,也可以是
- db
- cache
- 文件
- 全局变量
实现起来应该是很难的了。。。