本文从环境搭建入手,分析了Flink漏洞的调试和产生原因,针对两个漏洞的触发条件进行了细致的研究,总结漏洞的修补情况,特别是分析了Flink框架的web路由及REST API接口。
0x01 介绍
0x1 功能介绍
Apache 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
即可,一般先开启调试再执行此命令。
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依赖库
只需要将lib库添加为Libraries即可
Step3
配置idea,打开调试模式,连接相对应的端口
0x03 漏洞分析
0x1 CVE-2020-17518 任意文件上传
1. 漏洞触发点分析
文件上传漏洞拥有着较为明显的触发函数,在FileUploadHandler中存在如下函数
这里的文件重命名将fileupload的post包中的Filename取出,并生成相对应的文件。
2. 路由分析
文件上传的触发路由比较独特,经过调试分析得到了以下结果
- 访问的所有路径都会经过handler链的处理。
- 在FileUploadHandler类中如果发送的包为file包,那么会对currentHttpPostRequestDecoder进行赋值以及对msg进行对象转换,从而进行文件操作。
- CVE-2020-17519 发生在RouterHandler 类处理中。
handler链在AbstractChannelHandlerContext::invokeChannelRead函数中体现
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变量为带有目录遍历的字符串内容,如下图所示
进入fileUpload.renameTo方法,利用java file的renameTo方法,生成目标文件
4. 问题思考
漏洞到底发生在服务运行的哪个时期?触发路由为什么是多样的?
发生在服务handler遍历阶段,flink中维护着一个大的handler链表,每个handler结构拥有next和prev指向节点,FileUploadHandler也是其中的一个handler节点,所以通过任意路由走进该handler节点。其实漏洞发生在RouterHandler路由之前。
0x2 CVE-2020-17519 任意文件读
我们从漏洞产生的地方开始分析,同样也是漏洞挖掘的分析思路。
1. 漏洞触发点分析
漏洞触发点为JobManagerCustomLogHandler类中的一处文件读取函数,如下图所示
在该函数中 logDir 不可控,但是filename可控,这样我们可以利用../进行路径遍历操作。
2. 路由规则分析
这时就要考虑怎么触发到该漏洞了,通常来讲Handler代码对应的是一个路由,通过访问该路由就可以执行到Handler,name接下来要分析Flink的路由规则了。
RouterHandler为Flink的路由处理类,其中包含了他对传递过来的路由的处理逻辑,同时也是补丁修补的地方。其相关处理逻辑如下
这时的routers变量保存着各种路由对应关系,如下图所示
在其中可以发现本次路由对应的handler
可以看到jobmanager/logs/路由的后面跟的是filename参数,因此我们可以通过最后一个path传递
3. 漏洞触发条件
可以想到如果想让漏洞触发必须让路径拼接成功,但是../在正常的url中是会被解析的,因此不可能传递到后台代码。所以这里的触发条件就是允许路径遍历payload进行编码,之后再后台解析时去解码。分析过程中发现了两次url解码,位置如下:
RouterHandler::channelRead0()
->QueryStringDecoder::path()
->QueryStringDecoder::decodeComponent()
decdoeComponent为自己编写的urldecode函数,大致逻辑为寻找%之后将%后的字符进行解析
第二次解码在Router::route函数中
decodePathTokens函数是获取路由及url参数的主要函数,会通过斜杠分割获取相对应的内容,其调用urldecode的代码如下。
完整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解析的时候会出现如下错误
在路由匹配的时候不能有多余的斜杠,否则都当做是路由进行处理,所以最好的方法是编码两次。
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/