戳上面的蓝字关注我们哦!
01 漏洞简介
—
在昨日(2021/1/5),Apache Flink发布安全更新,修复了由蚂蚁安全非攻实验室发现的2个高危漏洞:
CVE-2020-17518:通过构造恶意的http header可实现远程文件写入。
CVE-2020-17519:攻击者可通过REST API使用../跳目录实现任意文件读取。
02影响范围
—
CVE-2020-17518:
Apache Flink 1.5.1-1.11.2
CVE-2020-17519:
Apache Flink 1.11.0,1.11.1,1.11.2
03 CVE-2020-17519漏洞复现
—
漏洞路径
/jobmanager/logs/..%252f..%252f..%252f..%252f..%252f..%252f..%252f..%252f..%252f..%252f..%252f..%252fetc%252fpasswd
04 CVE-2020-17519漏洞分析
—
漏洞出现在Jobmanager的REST API上
经过错误定位后发现调用栈是在
org.apache.flink.runtime.rest.handler.util.HandlerUtils#transferFile
方法。
于是从网上下载相关二进制和源码程序到本地:
下载路径:https://archive.apache.org/dist/flink/flink-1.11.2/
在Linux上搭建web环境,本机Win10通过IDEA远程Debug。
只需修改远程Flink的conf/flink-conf.yaml文件,添加如下内容:
`# jobmanager debug port``env.java.opts.jobmanager: "-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=0.0.0.0:5006"``# taskmanager debug port``env.java.opts.taskmanager: "-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=0.0.0.0:5005"`
随后在本机的IDEA上添加一个Remote容器,参数如下图所示:
因为这里只需要jobmanager,只需attach相关端口即可。
并在transferFile函数上下断点,并访问漏洞poc。
我查看调用栈发现上层函数是
org.apache.flink.runtime.rest.handler.cluster.AbstractJobManagerFileHandler#respondToRequest
函数第一行
File file = getFile(handlerRequest);
就组合路径,跟进getFile查看
org.apache.flink.runtime.rest.handler.cluster.JobManagerCustomLogHandler#getFile
获取到的filename正好是可以跨级读取文件的恶意payload
回到刚才的HandlerUtils类
定义了HttpResponse类**,并设置text/plain**等信息
继续跟进发现if分支
`ctx.write(` `new DefaultFileRegion(fileChannel, 0, fileLength), ctx.newProgressivePromise())` `.addListener(completionListener);`
这里的逻辑是先判断是否启用SSL,这里走到if分支中,可以看到这里是通过Netty的零拷贝技术来写入ctx中的。简单过一下零拷贝过程。
核心内容是Netty的零拷贝技术:
1.第一步是通过RandomAccessFile以r的方式打开一个文件, 然后可以通过RandomAccessFile.getChannel()来获取一个FileChannel对象。
2.有了FileChannel对象,即可以使用DefaultFileRegion来封装这个FileChannel,即:
new DefaultFileRegion(fileChannel, 0, fileLength)
可以直接将内容拷贝到jvm中,再到最后ctx.writeAndFlush刷新输出到页面。
至此便是整个CVE-2020-14519任意文件读取漏洞的流程。
最后写下Netty整个http返回报文的执行流程
`HttpResponse response = new DefaultHttpResponse(msg.getProtocolVersion(), HttpResponseStatus.OK);``response.headers().set(HttpHeaders.Names.CONTENT_TYPE,"text/html; charset=UTF-8");``//添加头部信息``ctx.write(response);``//添加正文信息``ctx.write(Unpooled.copiedBuffer("This is response", CharsetUtil.UTF_8));``//最后记得flush``ChannelFuture future = ctx.writeAndFlush(LastHttpContent.EMPTY_LAST_CONTENT);``//关闭连接``future.addListener(ChannelFutureListener.CLOSE);`
整体漏洞定位不难,就是对Netty不太熟悉,还需要多实操分析。