简介
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中读取文件对象并作为响应返回。
转载
分享
为啥报这个错