Apache Flink CVE-2020-17518/17519 漏洞分析

t015c21411f18ecb0c8

本文从环境搭建入手,分析了Flink漏洞的调试和产生原因,针对两个漏洞的触发条件进行了细致的研究,总结漏洞的修补情况,特别是分析了Flink框架的web路由及REST API接口。

0x01 介绍

0x1 功能介绍

ApaC++he Flink是一个面向数据流处理和批量数据处理的可分布式的开源计算框架。它可以用来做批处理,即处理静态的数据集、历史的数据集;也可以用来做流处理,即实时地处理一些实时数据流,实时地产生数据的结果;也可以用来做一些基于事件的应用。

0x2 漏洞介绍

漏洞出现在Flink的web服务上,Flink采用的REST API实现的部分服务,漏洞介绍如下。

  • CVE-2020-17518
    apache Flink 1.5.1版本引入的REST handler可导致文件上传到文件系统中的任意一个地方。
  • CVE-2020-17519
    Apache Flink 1.11.0 及以上版本受到任意文件读漏洞的影响。

0x02 调试环境搭建

0x1 服务安装

在该链接中选择需要的版本 https://archive.apache.org/dist/flink/
下载bin文件即可,本文下载的是flink-1.11.2-bin-scala_2.11.tgz
直接在解压好的目录下执行./bin/start-cluster.sh即可,一般先开启调试再执行此命令。

t013d6b68f791388b4e

0x2 开启调试

需要注意的是使用JAVA1.8的环境,主要采用java的远程调试方法,

Step1

在 conf/flink-conf.yaml 文件的最后添加

env.java.opts: "-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=9996"

Step2

添加lib依赖库

t018726ad5e2455a3b3

只需要将lib库添加为Libraries即可

Step3

配置idea,打开调试模式,连接相对应的端口

t01124bc7559564f4e5

0x03 漏洞分析

0x1 CVE-2020-17518 任意文件上传

1. 漏洞触发点分析

文件上传漏洞拥有着较为明显的触发函数,在FileUploadHandler中存在如下函数

t01117434f5bd7ca455

这里的文件重命名将fileupload的post包中的Filename取出,并生成相对应的文件。

t01353c3c2f46466c31

2. 路由分析

文件上传的触发路由比较独特,经过调试分析得到了以下结果

  1. 访问的所有路径都会经过handler链的处理。
  2. 在FileUploadHandler类中如果发送的包为file包,那么会对currentHttpPostRequestDecoder进行赋值以及对msg进行对象转换,从而进行文件操作。
  3. CVE-2020-17519 发生在RouterHandler 类处理中。

handler链在AbstractChannelHandlerContext::invokeChannelRead函数中体现

t0176633a4718ec0d64

FileUploadHandler就在这个这条handler链中

3. 漏洞利用

可以看出路由路径可以是任意值,但必须是POST方法给currentHttpPostRequestDecoder赋值,所以可以发送如下数据包

POST /xxxxx HTTP/1.1
Host: localhost:8081
Accept-Encoding: gzip, deflate
Accept: */*
Accept-Language: en
User-Agent: Mozilla/5.0 (Windows NT 10.0; win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36
Connection: close
Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryoZ8meKnrrso89R6Y
Content-Length: 187

------WebKitFormBoundaryoZ8meKnrrso89R6Y
Content-Disposition: form-data; name="jarfile"; filename="../../../../../../tmp/success"

success
------WebKitFormBoundaryoZ8meKnrrso89R6Y--

在第97行生成的dest变量为带有目录遍历的字符串内容,如下图所示

t01717ce739c5af4ebd

进入fileUpload.renameTo方法,利用java file的renameTo方法,生成目标文件

t01c958c75d38ba389a

4. 问题思考

漏洞到底发生在服务运行的哪个时期?触发路由为什么是多样的?
发生在服务handler遍历阶段,flink中维护着一个大的handler链表,每个handler结构拥有next和prev指向节点,FileUploadHandler也是其中的一个handler节点,所以通过任意路由走进该handler节点。其实漏洞发生在RouterHandler路由之前。

0x2 CVE-2020-17519 任意文件读

我们从漏洞产生的地方开始分析,同样也是漏洞挖掘的分析思路。

1. 漏洞触发点分析

漏洞触发点为JobManagerCustomLogHandler类中的一处文件读取函数,如下图所示

t0123082aa4bebd5ebc

在该函数中 logDir 不可控,但是filename可控,这样我们可以利用../进行路径遍历操作。

2. 路由规则分析

这时就要考虑怎么触发到该漏洞了,通常来讲Handler代码对应的是一个路由,通过访问该路由就可以执行到Handler,name接下来要分析Flink的路由规则了。

RouterHandler为Flink的路由处理类,其中包含了他对传递过来的路由的处理逻辑,同时也是补丁修补的地方。其相关处理逻辑如下

t019bc40e1bc9b1b000

这时的routers变量保存着各种路由对应关系,如下图所示

t01735adc049d373976

在其中可以发现本次路由对应的handler

t01acd3f8cbdd262b8b

可以看到jobmanager/logs/路由的后面跟的是filename参数,因此我们可以通过最后一个path传递

3. 漏洞触发条件

可以想到如果想让漏洞触发必须让路径拼接成功,但是../在正常的url中是会被解析的,因此不可能传递到后台代码。所以这里的触发条件就是允许路径遍历payload进行编码,之后再后台解析时去解码。分析过程中发现了两次url解码,位置如下:

RouterHandler::channelRead0()
->QueryStRingDecoder::path()
->QueryStringDecoder::decodeComponent()

decdoeComponent为自己编写的urldecode函数,大致逻辑为寻找%之后将%后的字符进行解析

t014331b46142a2f8af

第二次解码在Router::route函数中

t0192bcfd20b7962bd1

decodePathTokens函数是获取路由及url参数的主要函数,会通过斜杠分割获取相对应的内容,其调用urldecode的代码如下。

t01ac9c8c176f212f32

完整payload

http://127.0.0.1:8081/jobmanager/logs/..%252f..%252f..%252f..%252f..%252f..%252f..%252f..%252f..%252f..%252f..%252f..%252fetc%252fpasswd

4. 问题思考

为什么要两次URL编码,一次不可以吗?
当传入编码一次的路径的时候,在tokens解析的时候会出现如下错误

t0111391174bc892ec8

在路由匹配的时候不能有多余的斜杠,否则都当做是路由进行处理,所以最好的方法是编码两次。

0x04 漏洞修复

0x1 CVE-2020-17518

apache flink采取了最简单的修复方法,在JobManagerCustomLogHandler::channelRead0函数的目录拼接之前,利用File的getName方法直接获取文件名,如下所示

Path dest = this.currentUploadDir.resolve((new File(fileUpload.getFilename())).getName());

0x2 CVE-2020-17519

采取去同样的修补方法,在JobManagerCustomLogHandler::getFile方法中添加如下代码,获取文件名

 String filename = (new File((String)handlerRequest.getPathParameter(LogFileNamePathParameter.class))).getName();

参考文献

https://securityreport.com/poc-exploits-for-apache-flink-path-traversal-vulnerabilities-posted/

标签