简介
Apache Flink 1.11.0-1.11.2中引入的一项更改,允许攻击者通过JobManager
进程的REST接口读取本地文件系统上的任何文件,访问仅限于JobManager进程可访问的文件。
复现
下载源码:
git clone https://github.com/apache/flink.git
根据官方公告可以得到修复的commit
为b561010b0ee741543c3953306037f00d7a9f0801
里边还有测试用例可以去看看。
使用未打补丁的parent-commit
搭建环境。
git checkout 7fed9f0243aaf80d0060f075b95ba46b3207c8a8
编译
mvn clean package -DskipTests
一般半小时就行
编译后会出现build-target
目录,修改配置文件用于IDEA-DEBUG动态调试
flink-debug-src/build-target/conf/flink-conf.yaml
IDEA调试配置
在配置文件最后添加:
env.java.opts: -agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=5006
开启服务
flink-debug-src/build-target/bin/start-cluster.sh
POC
http://localhost:8081/jobmanager/logs/..%252f..%252f..%252f..%252f..%252f..%252f..%252f..%252f..%252f..%252f..%252f..%252fetc%252fpasswd
分析
从路由处开始分析。
org.apache.flink.runtime.rest.handler.router.Router#route(org.apache.flink.shaded.netty4.io.netty.handler.codec.http.HttpMethod, java.lang.String, java.util.Map<java.lang.String,java.util.List<java.lang.String>>)
跟踪POC的path
部分,POC的后半部分是双URL编码的,到达服务器自动解码一次,第二次解码处理发生在decodePathTokens
函数。
org.apache.flink.runtime.rest.handler.router.Router#decodePathTokens
对path使用/
进行分割,然后得到三个元素,接着遍历三个元素使用QueryStringDecoder.decodeComponent
进行URL解码并返回包含穿越目录的tokens
。
org.apache.flink.runtime.rest.handler.router.MethodlessRouter#route
对routes.entrySet()
进行循环遍历
然后和前面的pathTokens
进行匹配判断,跟进pattern.match
。
org.apache.flink.runtime.rest.handler.router.PathPattern#match
遍历并判断key
是否:
开头,然后将../../../../../../../../../../../../etc/passwd
放进Map中并返回ture
。
org.apache.flink.runtime.rest.handler.router.RouteResult#RouteResult
在上层中通过if判断,并将Map
作为参数穿个RouteResult
实例化。
org.apache.flink.runtime.rest.handler.router.RouterHandler#routed
实例化的RouteResult
对象又作为参数传入RoutedRequest
对象。
org.apache.flink.runtime.rest.handler.router.RoutedRequest#RoutedRequest
最终的RoutedRequest
对象成员如下:
org/apache/flink/runtime/rest/handler/AbstractHandler.java:161
层层跟进至关键创建HandlerRequest
对象的部分,跟进构造参数routedRequest.getRouteResult().pathParams()
org.apache.flink.runtime.rest.handler.router.RoutedRequest#getRouteResult
org.apache.flink.runtime.rest.handler.router.RouteResult#pathParams
结果为包含目录穿越的MAP
org.apache.flink.runtime.rest.handler.HandlerRequest#HandlerRequest(R, M, java.util.Map<java.lang.String,java.lang.String>, java.util.Map<java.lang.String,java.util.List<java.lang.String>>, java.util.Collection<java.io.File>)
从Map
中取出目录穿越的POC,然后传入resolveFromString
。
org.apache.flink.runtime.rest.messages.MessageParameter#resolveFromString
最终放入MessageParameter
对象的value成员中,接着将MessageParameter
对象放入以class
为键的MAP中。
实例化后的HandlerRequest
对象成员为:
org/apache/flink/runtime/rest/handler/AbstractHandler.java:179
HandlerRequest
对象又作为参数传入respondToRequest
函数
org.apache.flink.runtime.rest.handler.cluster.AbstractJobManagerFileHandler#respondToRequest
HandlerRequest
对象又传入getFile
函数。
org.apache.flink.runtime.rest.handler.cluster.JobManagerCustomLogHandler#getFile
org.apache.flink.runtime.rest.handler.HandlerRequest#getPathParameter
取出../../../../../../../../../../../../etc/passwd
然后用于创建File
对象,最后在HandlerUtils.transferFile
中读取文件对象并作为响应返回。